2012-03-09 13:15:24 +01:00
|
|
|
/* This file is part of Clementine.
|
|
|
|
Copyright 2012, David Sansome <me@davidsansome.com>
|
2014-02-07 16:34:20 +01:00
|
|
|
|
2012-03-09 13:15:24 +01:00
|
|
|
Clementine is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
2014-02-07 16:34:20 +01:00
|
|
|
|
2012-03-09 13:15:24 +01:00
|
|
|
Clementine is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
2014-02-07 16:34:20 +01:00
|
|
|
|
2012-03-09 13:15:24 +01:00
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "podcastbackend.h"
|
|
|
|
#include "podcastupdater.h"
|
|
|
|
#include "podcasturlloader.h"
|
|
|
|
#include "core/application.h"
|
|
|
|
#include "core/closure.h"
|
|
|
|
#include "core/logging.h"
|
2012-03-12 15:10:16 +01:00
|
|
|
#include "core/qhash_qurl.h"
|
2012-03-09 13:15:24 +01:00
|
|
|
#include "core/timeconstants.h"
|
|
|
|
|
|
|
|
#include <QSettings>
|
|
|
|
#include <QTimer>
|
|
|
|
|
|
|
|
const char* PodcastUpdater::kSettingsGroup = "Podcasts";
|
|
|
|
|
|
|
|
PodcastUpdater::PodcastUpdater(Application* app, QObject* parent)
|
2014-02-07 16:34:20 +01:00
|
|
|
: QObject(parent),
|
|
|
|
app_(app),
|
|
|
|
update_interval_secs_(0),
|
|
|
|
update_timer_(new QTimer(this)),
|
|
|
|
loader_(new PodcastUrlLoader(this)),
|
|
|
|
pending_replies_(0) {
|
2012-03-09 13:15:24 +01:00
|
|
|
connect(app_, SIGNAL(SettingsChanged()), SLOT(ReloadSettings()));
|
|
|
|
connect(update_timer_, SIGNAL(timeout()), SLOT(UpdateAllPodcastsNow()));
|
|
|
|
connect(app_->podcast_backend(), SIGNAL(SubscriptionAdded(Podcast)),
|
2012-03-09 17:47:56 +01:00
|
|
|
SLOT(SubscriptionAdded(Podcast)));
|
2012-03-09 13:15:24 +01:00
|
|
|
|
|
|
|
update_timer_->setSingleShot(true);
|
|
|
|
|
|
|
|
ReloadSettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PodcastUpdater::ReloadSettings() {
|
|
|
|
QSettings s;
|
|
|
|
s.beginGroup(kSettingsGroup);
|
|
|
|
|
|
|
|
last_full_update_ = s.value("last_full_update").toDateTime();
|
|
|
|
update_interval_secs_ = s.value("update_interval_secs").toInt();
|
|
|
|
|
|
|
|
RestartTimer();
|
|
|
|
}
|
|
|
|
|
2012-03-10 13:32:35 +01:00
|
|
|
void PodcastUpdater::SaveSettings() {
|
|
|
|
QSettings s;
|
|
|
|
s.beginGroup(kSettingsGroup);
|
|
|
|
s.setValue("last_full_update", last_full_update_);
|
|
|
|
}
|
|
|
|
|
2012-03-09 13:15:24 +01:00
|
|
|
void PodcastUpdater::RestartTimer() {
|
|
|
|
// Stop any existing timer
|
|
|
|
update_timer_->stop();
|
|
|
|
|
2012-03-10 13:32:35 +01:00
|
|
|
if (pending_replies_ > 0) {
|
|
|
|
// We're still waiting for replies from the last update - don't do anything.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-03-09 13:15:24 +01:00
|
|
|
if (update_interval_secs_ > 0) {
|
|
|
|
if (!last_full_update_.isValid()) {
|
|
|
|
// Updates are enabled and we've never updated before. Do it now.
|
|
|
|
qLog(Info) << "Updating podcasts for the first time";
|
|
|
|
UpdateAllPodcastsNow();
|
|
|
|
} else {
|
2014-02-07 16:34:20 +01:00
|
|
|
const QDateTime next_update =
|
|
|
|
last_full_update_.addSecs(update_interval_secs_);
|
|
|
|
const int secs_until_next_update =
|
|
|
|
QDateTime::currentDateTime().secsTo(next_update);
|
2012-03-09 13:15:24 +01:00
|
|
|
|
|
|
|
if (secs_until_next_update < 0) {
|
2014-02-07 16:34:20 +01:00
|
|
|
qLog(Info) << "Updating podcasts" << (-secs_until_next_update)
|
|
|
|
<< "seconds late";
|
2012-03-09 13:15:24 +01:00
|
|
|
UpdateAllPodcastsNow();
|
|
|
|
} else {
|
2014-02-07 16:34:20 +01:00
|
|
|
qLog(Info) << "Updating podcasts at" << next_update << "(in"
|
|
|
|
<< secs_until_next_update << "seconds)";
|
2012-03-09 13:15:24 +01:00
|
|
|
update_timer_->start(secs_until_next_update * kMsecPerSec);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-09 17:47:56 +01:00
|
|
|
void PodcastUpdater::SubscriptionAdded(const Podcast& podcast) {
|
|
|
|
// Only update a new podcast immediately if it doesn't have an episode list.
|
|
|
|
// We assume that the episode list has already been fetched recently
|
|
|
|
// otherwise.
|
|
|
|
if (podcast.episodes().isEmpty()) {
|
|
|
|
UpdatePodcastNow(podcast);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-09 13:15:24 +01:00
|
|
|
void PodcastUpdater::UpdatePodcastNow(const Podcast& podcast) {
|
|
|
|
PodcastUrlLoaderReply* reply = loader_->Load(podcast.url());
|
2014-02-07 16:34:20 +01:00
|
|
|
NewClosure(reply, SIGNAL(Finished(bool)), this,
|
|
|
|
SLOT(PodcastLoaded(PodcastUrlLoaderReply*, Podcast, bool)), reply,
|
|
|
|
podcast, false);
|
2012-03-09 13:15:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void PodcastUpdater::UpdateAllPodcastsNow() {
|
2014-02-10 14:29:07 +01:00
|
|
|
for (const Podcast& podcast :
|
|
|
|
app_->podcast_backend()->GetAllSubscriptions()) {
|
2012-03-09 13:15:24 +01:00
|
|
|
PodcastUrlLoaderReply* reply = loader_->Load(podcast.url());
|
2014-02-07 16:34:20 +01:00
|
|
|
NewClosure(reply, SIGNAL(Finished(bool)), this,
|
|
|
|
SLOT(PodcastLoaded(PodcastUrlLoaderReply*, Podcast, bool)),
|
2012-03-09 13:15:24 +01:00
|
|
|
reply, podcast, true);
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
pending_replies_++;
|
2012-03-09 13:15:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
void PodcastUpdater::PodcastLoaded(PodcastUrlLoaderReply* reply,
|
|
|
|
const Podcast& podcast, bool one_of_many) {
|
2012-03-09 13:15:24 +01:00
|
|
|
reply->deleteLater();
|
|
|
|
|
|
|
|
if (one_of_many) {
|
|
|
|
if (--pending_replies_ == 0) {
|
2012-03-10 13:32:35 +01:00
|
|
|
// This was the last reply we were waiting for. Save this time as being
|
|
|
|
// the last sucessful update and restart the timer.
|
|
|
|
last_full_update_ = QDateTime::currentDateTime();
|
|
|
|
SaveSettings();
|
2012-03-09 13:15:24 +01:00
|
|
|
RestartTimer();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!reply->is_success()) {
|
|
|
|
qLog(Warning) << "Error fetching podcast at" << podcast.url() << ":"
|
|
|
|
<< reply->error_text();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (reply->result_type() != PodcastUrlLoaderReply::Type_Podcast) {
|
2014-02-07 16:34:20 +01:00
|
|
|
qLog(Warning) << "The URL" << podcast.url()
|
|
|
|
<< "no longer contains a podcast";
|
2012-03-09 13:15:24 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the episode URLs we had for this podcast already.
|
|
|
|
QSet<QUrl> existing_urls;
|
2014-02-10 14:29:07 +01:00
|
|
|
for (const PodcastEpisode& episode :
|
|
|
|
app_->podcast_backend()->GetEpisodes(podcast.database_id())) {
|
2012-03-09 13:15:24 +01:00
|
|
|
existing_urls.insert(episode.url());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add any new episodes
|
|
|
|
PodcastEpisodeList new_episodes;
|
2014-02-10 14:29:07 +01:00
|
|
|
for (const Podcast& reply_podcast : reply->podcast_results()) {
|
|
|
|
for (const PodcastEpisode& episode : reply_podcast.episodes()) {
|
2012-03-09 13:15:24 +01:00
|
|
|
if (!existing_urls.contains(episode.url())) {
|
|
|
|
PodcastEpisode episode_copy(episode);
|
|
|
|
episode_copy.set_podcast_database_id(podcast.database_id());
|
|
|
|
new_episodes.append(episode_copy);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
app_->podcast_backend()->AddEpisodes(&new_episodes);
|
2014-02-07 16:34:20 +01:00
|
|
|
qLog(Info) << "Added" << new_episodes.count() << "new episodes for"
|
|
|
|
<< podcast.url();
|
2012-03-09 13:15:24 +01:00
|
|
|
}
|