From d1cad6cbc1707055387633e4b9c323359f2f5442 Mon Sep 17 00:00:00 2001 From: Krzysztof Sobiecki Date: Tue, 2 Dec 2014 23:22:11 +0100 Subject: [PATCH 01/12] Remove podcast episode automatic deleter out of PodcastDownloader --- src/podcasts/podcastdownloader.cpp | 29 +---------------------------- src/podcasts/podcastdownloader.h | 4 ---- 2 files changed, 1 insertion(+), 32 deletions(-) diff --git a/src/podcasts/podcastdownloader.cpp b/src/podcasts/podcastdownloader.cpp index fb7207db3..616355703 100644 --- a/src/podcasts/podcastdownloader.cpp +++ b/src/podcasts/podcastdownloader.cpp @@ -36,8 +36,6 @@ #include const char* PodcastDownloader::kSettingsGroup = "Podcasts"; -const int PodcastDownloader::kAutoDeleteCheckIntervalMsec = - 15 * 60 * kMsecPerSec; // 15 minutes struct PodcastDownloader::Task { Task() : file(nullptr) {} @@ -56,17 +54,12 @@ PodcastDownloader::PodcastDownloader(Application* app, QObject* parent) auto_download_(false), delete_after_secs_(0), current_task_(nullptr), - last_progress_signal_(0), - auto_delete_timer_(new QTimer(this)) { + last_progress_signal_(0) { connect(backend_, SIGNAL(EpisodesAdded(PodcastEpisodeList)), SLOT(EpisodesAdded(PodcastEpisodeList))); connect(backend_, SIGNAL(SubscriptionAdded(Podcast)), SLOT(SubscriptionAdded(Podcast))); connect(app_, SIGNAL(SettingsChanged()), SLOT(ReloadSettings())); - connect(auto_delete_timer_, SIGNAL(timeout()), SLOT(AutoDelete())); - - auto_delete_timer_->setInterval(kAutoDeleteCheckIntervalMsec); - auto_delete_timer_->start(); ReloadSettings(); } @@ -291,23 +284,3 @@ void PodcastDownloader::EpisodesAdded(const PodcastEpisodeList& episodes) { } } -void PodcastDownloader::AutoDelete() { - if (delete_after_secs_ <= 0) { - return; - } - - QDateTime max_date = QDateTime::currentDateTime(); - max_date.addSecs(-delete_after_secs_); - - PodcastEpisodeList old_episodes = - backend_->GetOldDownloadedEpisodes(max_date); - if (old_episodes.isEmpty()) return; - - qLog(Info) << "Deleting" << old_episodes.count() - << "episodes because they were last listened to" - << (delete_after_secs_ / kSecsPerDay) << "days ago"; - - for (const PodcastEpisode& episode : old_episodes) { - DeleteEpisode(episode); - } -} diff --git a/src/podcasts/podcastdownloader.h b/src/podcasts/podcastdownloader.h index 356d45359..083f37519 100644 --- a/src/podcasts/podcastdownloader.h +++ b/src/podcasts/podcastdownloader.h @@ -74,8 +74,6 @@ class PodcastDownloader : public QObject { void ReplyFinished(); void ReplyDownloadProgress(qint64 received, qint64 total); - void AutoDelete(); - private: struct Task; @@ -103,8 +101,6 @@ class PodcastDownloader : public QObject { QSet downloading_episode_ids_; time_t last_progress_signal_; - - QTimer* auto_delete_timer_; }; #endif // PODCASTS_PODCASTDOWNLOADER_H_ From 970e86aac597205a45ab927dacab4c38718ac828 Mon Sep 17 00:00:00 2001 From: Krzysztof Sobiecki Date: Sat, 6 Dec 2014 19:44:12 +0100 Subject: [PATCH 02/12] Allows to download multiple podcasts at the same time A new PodcastDeleter, it deletes podcast episodes --- src/CMakeLists.txt | 2 + src/core/application.cpp | 6 + src/core/application.h | 3 + src/podcasts/podcastbackend.cpp | 21 +++ src/podcasts/podcastbackend.h | 1 + src/podcasts/podcastdeleter.cpp | 113 +++++++++++++ src/podcasts/podcastdeleter.h | 66 ++++++++ src/podcasts/podcastdownloader.cpp | 244 ++++++++++++----------------- src/podcasts/podcastdownloader.h | 56 ++++--- src/podcasts/podcastepisode.cpp | 4 +- src/podcasts/podcastservice.cpp | 63 ++++++-- src/podcasts/podcastservice.h | 9 +- 12 files changed, 410 insertions(+), 178 deletions(-) create mode 100644 src/podcasts/podcastdeleter.cpp create mode 100644 src/podcasts/podcastdeleter.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6ccc2dd75..195010201 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -282,6 +282,7 @@ set(SOURCES podcasts/podcast.cpp podcasts/podcastbackend.cpp podcasts/podcastdiscoverymodel.cpp + podcasts/podcastdeleter.cpp podcasts/podcastdownloader.cpp podcasts/podcastepisode.cpp podcasts/podcastinfowidget.cpp @@ -578,6 +579,7 @@ set(HEADERS podcasts/itunessearchpage.h podcasts/podcastbackend.h podcasts/podcastdiscoverymodel.h + podcasts/podcastdeleter.h podcasts/podcastdownloader.h podcasts/podcastinfowidget.h podcasts/podcastservice.h diff --git a/src/core/application.cpp b/src/core/application.cpp index 29b345eb8..6f39b866e 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -41,6 +41,7 @@ #include "playlist/playlistmanager.h" #include "podcasts/gpoddersync.h" #include "podcasts/podcastbackend.h" +#include "podcasts/podcastdeleter.h" #include "podcasts/podcastdownloader.h" #include "podcasts/podcastupdater.h" @@ -73,6 +74,7 @@ Application::Application(QObject* parent) library_(nullptr), device_manager_(nullptr), podcast_updater_(nullptr), + podcast_deleter_(nullptr), podcast_downloader_(nullptr), gpodder_sync_(nullptr), moodbar_loader_(nullptr), @@ -107,6 +109,10 @@ Application::Application(QObject* parent) library_ = new Library(this, this); device_manager_ = new DeviceManager(this, this); podcast_updater_ = new PodcastUpdater(this, this); + + podcast_deleter_ = new PodcastDeleter(this, this); + MoveToNewThread(podcast_deleter_); + podcast_downloader_ = new PodcastDownloader(this, this); gpodder_sync_ = new GPodderSync(this, this); diff --git a/src/core/application.h b/src/core/application.h index f2af62953..5157cb3ea 100644 --- a/src/core/application.h +++ b/src/core/application.h @@ -44,6 +44,7 @@ class NetworkRemote; class NetworkRemoteHelper; class Player; class PlaylistBackend; +class PodcastDeleter; class PodcastDownloader; class PlaylistManager; class PodcastBackend; @@ -83,6 +84,7 @@ class Application : public QObject { Library* library() const { return library_; } DeviceManager* device_manager() const { return device_manager_; } PodcastUpdater* podcast_updater() const { return podcast_updater_; } + PodcastDeleter* podcast_deleter() const { return podcast_deleter_; } PodcastDownloader* podcast_downloader() const { return podcast_downloader_; } GPodderSync* gpodder_sync() const { return gpodder_sync_; } MoodbarLoader* moodbar_loader() const { return moodbar_loader_; } @@ -128,6 +130,7 @@ class Application : public QObject { Library* library_; DeviceManager* device_manager_; PodcastUpdater* podcast_updater_; + PodcastDeleter* podcast_deleter_; PodcastDownloader* podcast_downloader_; GPodderSync* gpodder_sync_; MoodbarLoader* moodbar_loader_; diff --git a/src/podcasts/podcastbackend.cpp b/src/podcasts/podcastbackend.cpp index b35f79f4f..540dbe0d8 100644 --- a/src/podcasts/podcastbackend.cpp +++ b/src/podcasts/podcastbackend.cpp @@ -322,6 +322,27 @@ PodcastEpisodeList PodcastBackend::GetOldDownloadedEpisodes( return ret; } +PodcastEpisode PodcastBackend::GetOldestDownloadedListenedEpisode() { + PodcastEpisode ret; + PodcastEpisodeList list_; + + QMutexLocker l(db_->Mutex()); + QSqlDatabase db(db_->Connect()); + + QSqlQuery q("SELECT ROWID, " + PodcastEpisode::kColumnSpec + + " FROM podcast_episodes" + " WHERE downloaded = 'true'" + " AND listened = 'true'" + " ORDER BY listened_date ASC", + db); + q.exec(); + if (db_->CheckErrors(q)) return ret; + q.next(); + ret.InitFromQuery(q); + + return ret; +} + PodcastEpisodeList PodcastBackend::GetNewDownloadedEpisodes() { PodcastEpisodeList ret; diff --git a/src/podcasts/podcastbackend.h b/src/podcasts/podcastbackend.h index 08dd9c98a..1fe178c2c 100644 --- a/src/podcasts/podcastbackend.h +++ b/src/podcasts/podcastbackend.h @@ -56,6 +56,7 @@ class PodcastBackend : public QObject { PodcastEpisode GetEpisodeById(int id); PodcastEpisode GetEpisodeByUrl(const QUrl& url); PodcastEpisode GetEpisodeByUrlOrLocalUrl(const QUrl& url); + PodcastEpisode GetOldestDownloadedListenedEpisode(); // Returns a list of episodes that have local data (downloaded=true) but were // last listened to before the given QDateTime. This query is NOT indexed so diff --git a/src/podcasts/podcastdeleter.cpp b/src/podcasts/podcastdeleter.cpp new file mode 100644 index 000000000..2a199aaf5 --- /dev/null +++ b/src/podcasts/podcastdeleter.cpp @@ -0,0 +1,113 @@ +/* This file is part of Clementine. + Copyright 2012, David Sansome + Copyright 2014, Krzysztof A. Sobiecki + Copyright 2014, John Maguire + + 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. + + 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. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . +*/ + +#include "podcastbackend.h" +#include "podcastdeleter.h" +#include "core/application.h" +#include "core/logging.h" +#include "core/timeconstants.h" +#include "core/utilities.h" +#include "library/librarydirectorymodel.h" +#include "library/librarymodel.h" + +#include +#include +#include +#include +#include +#include + +const char* PodcastDeleter::kSettingsGroup = "Podcasts"; +const int PodcastDeleter::kAutoDeleteCheckIntervalMsec = + 60 * 6 * 60 * kMsecPerSec; + +PodcastDeleter::PodcastDeleter(Application* app, QObject* parent) + : QObject(parent), + app_(app), + backend_(app_->podcast_backend()), + delete_after_secs_(0), + auto_delete_timer_(new QTimer(this)) { + + ReloadSettings(); + AutoDelete(); + connect(auto_delete_timer_, SIGNAL(timeout()), SLOT(AutoDelete())); + connect(app_, SIGNAL(SettingsChanged()), SLOT(ReloadSettings())); +} + +void PodcastDeleter::DeleteEpisode(const PodcastEpisode& episode) { + // Delete the local file + if (!QFile::remove(episode.local_url().toLocalFile())) { + qLog(Warning) << "The local file" << episode.local_url().toLocalFile() + << "could not be removed"; + } + + // Update the episode in the DB + PodcastEpisode episode_copy(episode); + episode_copy.set_downloaded(false); + episode_copy.set_local_url(QUrl()); + episode_copy.set_listened_date(QDateTime()); + backend_->UpdateEpisodes(PodcastEpisodeList() << episode_copy); +} + +void PodcastDeleter::ReloadSettings() { + QSettings s; + s.beginGroup(kSettingsGroup); + delete_after_secs_ = s.value("delete_after", 0).toInt(); + AutoDelete(); +} + +void PodcastDeleter::AutoDelete() { + if (delete_after_secs_ <= 0) { + return; + } + auto_delete_timer_->stop(); + QDateTime max_date = QDateTime::currentDateTime(); + QDateTime time_out; + PodcastEpisode oldest_episode; + QDateTime oldest_episode_time; + max_date = max_date.addSecs(-delete_after_secs_); + + PodcastEpisodeList old_episodes = + backend_->GetOldDownloadedEpisodes(max_date); + + qLog(Info) << "Deleting" << old_episodes.count() + << "episodes because they were last listened to" + << (delete_after_secs_ / kSecsPerDay) << "days ago"; + + for (const PodcastEpisode& episode : old_episodes) { + DeleteEpisode(episode); + } + + oldest_episode = backend_->GetOldestDownloadedListenedEpisode(); + if (!oldest_episode.listened_date().isValid()) { + oldest_episode_time = QDateTime::currentDateTime(); + } else { + oldest_episode_time = oldest_episode.listened_date(); + } + + time_out = QDateTime::currentDateTime(); + time_out = time_out.addMSecs(-oldest_episode_time.toMSecsSinceEpoch()); + time_out.setTime_t(delete_after_secs_ - time_out.toTime_t()); + if (time_out.isValid()) { + auto_delete_timer_->setInterval(time_out.toMSecsSinceEpoch()); + } else { + auto_delete_timer_->setInterval(kAutoDeleteCheckIntervalMsec); + } + auto_delete_timer_->start(); +} diff --git a/src/podcasts/podcastdeleter.h b/src/podcasts/podcastdeleter.h new file mode 100644 index 000000000..a75fc3ad3 --- /dev/null +++ b/src/podcasts/podcastdeleter.h @@ -0,0 +1,66 @@ +/* This file is part of Clementine. + Copyright 2012, David Sansome + Copyright 2014, John Maguire + Copyright 2014, Krzysztof Sobiecki + + 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. + + 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. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . +*/ + +#ifndef PODCASTS_PODCASTDELETER_H_ +#define PODCASTS_PODCASTDELETER_H_ + +#include "core/network.h" +#include "podcast.h" +#include "podcastepisode.h" + +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_WIN +#include +#else +#include +#endif + +class Application; +class PodcastBackend; + +class QNetworkAccessManager; + +class PodcastDeleter : public QObject { + Q_OBJECT + + public: + explicit PodcastDeleter(Application* app, QObject* parent = nullptr); + static const char* kSettingsGroup; + static const int kAutoDeleteCheckIntervalMsec; + + public slots: + // Deletes downloaded data for this episode + void DeleteEpisode(const PodcastEpisode& episode); + void AutoDelete(); + void ReloadSettings(); + + private: + Application* app_; + PodcastBackend* backend_; + int delete_after_secs_; + QTimer* auto_delete_timer_; +}; + +#endif // PODCASTS_PODCASTDELETER_H_ diff --git a/src/podcasts/podcastdownloader.cpp b/src/podcasts/podcastdownloader.cpp index 616355703..2594aa838 100644 --- a/src/podcasts/podcastdownloader.cpp +++ b/src/podcasts/podcastdownloader.cpp @@ -37,13 +37,77 @@ const char* PodcastDownloader::kSettingsGroup = "Podcasts"; -struct PodcastDownloader::Task { - Task() : file(nullptr) {} - ~Task() { delete file; } +Task::Task(PodcastEpisode episode, QFile* file, PodcastBackend* backend) + : file_(file), + episode_(episode), + backend_(backend), + network_(new NetworkAccessManager(this)) { + req_ = QNetworkRequest(episode_.url()); + repl = new RedirectFollower(network_->get(req_)); + connect(repl, SIGNAL(readyRead()), SLOT(reading())); + connect(repl, SIGNAL(finished()), SLOT(finished_())); + connect(repl, SIGNAL(downloadProgress(qint64, qint64)), + SLOT(downloadProgress_(qint64, qint64))); + emit ProgressChanged(episode_, PodcastDownload::Downloading, 0); +} - PodcastEpisode episode; - QFile* file; -}; +Task::~Task() { + delete repl; + delete file_; + delete network_; +} + +PodcastEpisode Task::episode() { + return episode_; +} + +void Task::reading() { + qint64 bytes = 0; + forever { + bytes = repl->bytesAvailable(); + if (bytes <= 0) break; + + file_->write(repl->reply()->read(bytes)); + } +} + +void Task::finished_() { + if (repl->error() != QNetworkReply::NoError) { + qLog(Warning) << "Error downloading episode:" << repl->errorString(); + emit ProgressChanged(episode_, PodcastDownload::NotDownloading, 0); + // Delete the file + file_->remove(); + emit finished(); + return; + } + + qLog(Info) << "Download of" << file_->fileName() << "finished"; + + // Tell the database the episode has been updated. Get it from the DB again + // in case the listened field changed in the mean time. + PodcastEpisode episode = episode_; + episode.set_downloaded(true); + episode.set_local_url(QUrl::fromLocalFile(file_->fileName())); + backend_->UpdateEpisodes(PodcastEpisodeList() << episode); + Podcast podcast = + backend_->GetSubscriptionById(episode.podcast_database_id()); + Song song = episode_.ToSong(podcast); + + emit ProgressChanged(episode_, PodcastDownload::Finished, 0); + emit finished(); + + // I didn't ecountered even a single podcast with a corect metadata + TagReaderClient::Instance()->SaveFileBlocking(file_->fileName(), song); +} + +void Task::downloadProgress_(qint64 received, qint64 total) { + if (total <= 0) { + emit ProgressChanged(episode_, PodcastDownload::Downloading, 0); + } else { + emit ProgressChanged(episode_, PodcastDownload::Downloading, + static_cast(received) / total * 100); + } +} PodcastDownloader::PodcastDownloader(Application* app, QObject* parent) : QObject(parent), @@ -51,10 +115,7 @@ PodcastDownloader::PodcastDownloader(Application* app, QObject* parent) backend_(app_->podcast_backend()), network_(new NetworkAccessManager(this)), disallowed_filename_characters_("[^a-zA-Z0-9_~ -]"), - auto_download_(false), - delete_after_secs_(0), - current_task_(nullptr), - last_progress_signal_(0) { + auto_download_(false) { connect(backend_, SIGNAL(EpisodesAdded(PodcastEpisodeList)), SLOT(EpisodesAdded(PodcastEpisodeList))); connect(backend_, SIGNAL(SubscriptionAdded(Podcast)), @@ -82,61 +143,10 @@ void PodcastDownloader::ReloadSettings() { auto_download_ = s.value("auto_download", false).toBool(); download_dir_ = s.value("download_dir", DefaultDownloadDir()).toString(); - delete_after_secs_ = s.value("delete_after", 0).toInt(); } -void PodcastDownloader::DownloadEpisode(const PodcastEpisode& episode) { - if (downloading_episode_ids_.contains(episode.database_id())) return; - downloading_episode_ids_.insert(episode.database_id()); - - Task* task = new Task; - task->episode = episode; - - if (current_task_) { - // Add it to the queue - queued_tasks_.enqueue(task); - emit ProgressChanged(episode, Queued, 0); - } else { - // Start downloading now - StartDownloading(task); - } -} - -void PodcastDownloader::DeleteEpisode(const PodcastEpisode& episode) { - if (!episode.downloaded() || - downloading_episode_ids_.contains(episode.database_id())) - return; - - // Delete the local file - if (!QFile::remove(episode.local_url().toLocalFile())) { - qLog(Warning) << "The local file" << episode.local_url().toLocalFile() - << "could not be removed"; - } - - // Update the episode in the DB - PodcastEpisode episode_copy(episode); - episode_copy.set_downloaded(false); - episode_copy.set_local_url(QUrl()); - backend_->UpdateEpisodes(PodcastEpisodeList() << episode_copy); -} - -void PodcastDownloader::FinishAndDelete(Task* task) { - Podcast podcast = - backend_->GetSubscriptionById(task->episode.podcast_database_id()); - Song song = task->episode.ToSong(podcast); - - downloading_episode_ids_.remove(task->episode.database_id()); - emit ProgressChanged(task->episode, Finished, 0); - - // I didn't ecountered even a single podcast with a corect metadata - TagReaderClient::Instance()->SaveFileBlocking(task->file->fileName(), song); - delete task; - - NextTask(); -} - -QString PodcastDownloader::FilenameForEpisode( - const QString& directory, const PodcastEpisode& episode) const { +QString PodcastDownloader::FilenameForEpisode(const QString& directory, + const PodcastEpisode& episode) const { const QString file_extension = QFileInfo(episode.url().path()).suffix(); int count = 0; @@ -166,103 +176,49 @@ QString PodcastDownloader::FilenameForEpisode( } } -void PodcastDownloader::StartDownloading(Task* task) { - current_task_ = task; +void PodcastDownloader::DownloadEpisode(const PodcastEpisode& episode) { + QFile* file = nullptr; + Task* task = nullptr; - // Need to get the name of the podcast to use in the directory name. - Podcast podcast = - backend_->GetSubscriptionById(task->episode.podcast_database_id()); - if (!podcast.is_valid()) { - qLog(Warning) << "The podcast that contains episode" << task->episode.url() - << "doesn't exist any more"; - FinishAndDelete(task); - return; + for ( Task* tas : list_tasks_ ) { + if (tas->episode().database_id() == episode.database_id()) { + return; + } } + Podcast podcast = + backend_->GetSubscriptionById(episode.podcast_database_id()); + if (!podcast.is_valid()) { + qLog(Warning) << "The podcast that contains episode" << task->episode().url() + << "doesn't exist any more"; + return; + } const QString directory = download_dir_ + "/" + SanitiseFilenameComponent(podcast.title()); - const QString filepath = FilenameForEpisode(directory, task->episode); + const QString filepath = FilenameForEpisode(directory, episode); // Open the output file QDir().mkpath(directory); - task->file = new QFile(filepath); - if (!task->file->open(QIODevice::WriteOnly)) { + file = new QFile(filepath); + if (!file->open(QIODevice::WriteOnly)) { qLog(Warning) << "Could not open the file" << filepath << "for writing"; - FinishAndDelete(task); return; } - qLog(Info) << "Downloading" << task->episode.url() << "to" << filepath; + task = new Task(episode, file, backend_); - // Get the URL - QNetworkRequest req(task->episode.url()); - RedirectFollower* reply = new RedirectFollower(network_->get(req)); - connect(reply, SIGNAL(readyRead()), SLOT(ReplyReadyRead())); - connect(reply, SIGNAL(finished()), SLOT(ReplyFinished())); - connect(reply, SIGNAL(downloadProgress(qint64, qint64)), - SLOT(ReplyDownloadProgress(qint64, qint64))); - - emit ProgressChanged(task->episode, Downloading, 0); -} - -void PodcastDownloader::NextTask() { - current_task_ = nullptr; - - if (!queued_tasks_.isEmpty()) { - StartDownloading(queued_tasks_.dequeue()); - } -} - -void PodcastDownloader::ReplyReadyRead() { - QNetworkReply* reply = qobject_cast(sender())->reply(); - if (!reply || !current_task_ || !current_task_->file) return; - - forever { - const qint64 bytes = reply->bytesAvailable(); - if (bytes <= 0) break; - - current_task_->file->write(reply->read(bytes)); - } -} - -void PodcastDownloader::ReplyDownloadProgress(qint64 received, qint64 total) { - if (!current_task_ || !current_task_->file || total < 1024) return; - - const time_t current_time = QDateTime::currentDateTime().toTime_t(); - if (last_progress_signal_ == current_time) return; - last_progress_signal_ = current_time; - - emit ProgressChanged(current_task_->episode, Downloading, - static_cast(received) / total * 100); + list_tasks_ << task; + qLog(Info) << "Downloading" << task->episode().url() << "to" << filepath; + connect(task, SIGNAL(finished()), SLOT(ReplyFinished())); + connect(task, SIGNAL(ProgressChanged(const PodcastEpisode&, + PodcastDownload::State, int)), + SIGNAL(ProgressChanged(const PodcastEpisode&, + PodcastDownload::State, int))); } void PodcastDownloader::ReplyFinished() { - RedirectFollower* reply = qobject_cast(sender()); - if (!reply || !current_task_ || !current_task_->file) return; - - reply->deleteLater(); - - if (reply->error() != QNetworkReply::NoError) { - qLog(Warning) << "Error downloading episode:" << reply->errorString(); - - // Delete the file - current_task_->file->remove(); - - FinishAndDelete(current_task_); - return; - } - - qLog(Info) << "Download of" << current_task_->file->fileName() << "finished"; - - // Tell the database the episode has been updated. Get it from the DB again - // in case the listened field changed in the mean time. - PodcastEpisode episode = - backend_->GetEpisodeById(current_task_->episode.database_id()); - episode.set_downloaded(true); - episode.set_local_url(QUrl::fromLocalFile(current_task_->file->fileName())); - backend_->UpdateEpisodes(PodcastEpisodeList() << episode); - - FinishAndDelete(current_task_); + Task* task = qobject_cast(sender()); + list_tasks_.removeAll(task); } QString PodcastDownloader::SanitiseFilenameComponent(const QString& text) diff --git a/src/podcasts/podcastdownloader.h b/src/podcasts/podcastdownloader.h index 083f37519..9e1e2e6e4 100644 --- a/src/podcasts/podcastdownloader.h +++ b/src/podcasts/podcastdownloader.h @@ -20,9 +20,11 @@ #ifndef PODCASTS_PODCASTDOWNLOADER_H_ #define PODCASTS_PODCASTDOWNLOADER_H_ +#include "core/network.h" #include "podcast.h" #include "podcastepisode.h" +#include #include #include #include @@ -40,16 +42,44 @@ class PodcastBackend; class QNetworkAccessManager; +namespace PodcastDownload { + enum State { NotDownloading, Queued, Downloading, Finished }; +} + +class Task : public QObject { + Q_OBJECT + + public: + Task(PodcastEpisode episode, QFile* file, PodcastBackend* backend); + ~Task(); + PodcastEpisode episode(); + + signals: + void ProgressChanged(const PodcastEpisode& episode, + PodcastDownload::State state, int percent); + void finished(); + + private slots: + void reading(); + void finished_(); + void downloadProgress_(qint64 received, qint64 total); + + private: + QFile* file_; + PodcastEpisode episode_; + QNetworkRequest req_; + PodcastBackend* backend_; + NetworkAccessManager* network_; + RedirectFollower* repl; +}; + class PodcastDownloader : public QObject { Q_OBJECT public: explicit PodcastDownloader(Application* app, QObject* parent = nullptr); - enum State { NotDownloading, Queued, Downloading, Finished }; - static const char* kSettingsGroup; - static const int kAutoDeleteCheckIntervalMsec; QString DefaultDownloadDir() const; @@ -57,12 +87,9 @@ class PodcastDownloader : public QObject { // Adds the episode to the download queue void DownloadEpisode(const PodcastEpisode& episode); - // Deletes downloaded data for this episode - void DeleteEpisode(const PodcastEpisode& episode); - signals: void ProgressChanged(const PodcastEpisode& episode, - PodcastDownloader::State state, int percent); + PodcastDownload::State state, int percent); private slots: void ReloadSettings(); @@ -70,17 +97,9 @@ class PodcastDownloader : public QObject { void SubscriptionAdded(const Podcast& podcast); void EpisodesAdded(const PodcastEpisodeList& episodes); - void ReplyReadyRead(); void ReplyFinished(); - void ReplyDownloadProgress(qint64 received, qint64 total); private: - struct Task; - - void StartDownloading(Task* task); - void NextTask(); - void FinishAndDelete(Task* task); - QString FilenameForEpisode(const QString& directory, const PodcastEpisode& episode) const; QString SanitiseFilenameComponent(const QString& text) const; @@ -94,13 +113,8 @@ class PodcastDownloader : public QObject { bool auto_download_; QString download_dir_; - int delete_after_secs_; - Task* current_task_; - QQueue queued_tasks_; - QSet downloading_episode_ids_; - - time_t last_progress_signal_; + QList list_tasks_; }; #endif // PODCASTS_PODCASTDOWNLOADER_H_ diff --git a/src/podcasts/podcastepisode.cpp b/src/podcasts/podcastepisode.cpp index 450095ad0..abfafba8a 100644 --- a/src/podcasts/podcastepisode.cpp +++ b/src/podcasts/podcastepisode.cpp @@ -183,8 +183,10 @@ Song PodcastEpisode::ToSong(const Podcast& podcast) const { ret.set_comment(description()); ret.set_id(database_id()); ret.set_ctime(publication_date().toTime_t()); + ret.set_genre(QString("Podcast")); + ret.set_genre_id3(186); - if (listened()) { + if (listened() && listened_date().isValid()) { ret.set_mtime(listened_date().toTime_t()); } else { ret.set_mtime(publication_date().toTime_t()); diff --git a/src/podcasts/podcastservice.cpp b/src/podcasts/podcastservice.cpp index 50842d35c..3b22b1e56 100644 --- a/src/podcasts/podcastservice.cpp +++ b/src/podcasts/podcastservice.cpp @@ -20,6 +20,7 @@ #include "addpodcastdialog.h" #include "opmlcontainer.h" #include "podcastbackend.h" +#include "podcastdeleter.h" #include "podcastdownloader.h" #include "podcastservice.h" #include "podcastservicemodel.h" @@ -192,8 +193,8 @@ void PodcastService::PopulatePodcastList(QStandardItem* parent) { // Do this here since the downloader won't be created yet in the ctor. connect( app_->podcast_downloader(), - SIGNAL(ProgressChanged(PodcastEpisode, PodcastDownloader::State, int)), - SLOT(DownloadProgressChanged(PodcastEpisode, PodcastDownloader::State, + SIGNAL(ProgressChanged(PodcastEpisode, PodcastDownload::State, int)), + SLOT(DownloadProgressChanged(PodcastEpisode, PodcastDownload::State, int))); if (default_icon_.isNull()) { @@ -225,7 +226,7 @@ void PodcastService::UpdatePodcastText(QStandardItem* item, } void PodcastService::UpdateEpisodeText(QStandardItem* item, - PodcastDownloader::State state, + PodcastDownload::State state, int percent) { const PodcastEpisode episode = item->data(Role_Episode).value(); @@ -250,7 +251,7 @@ void PodcastService::UpdateEpisodeText(QStandardItem* item, // Queued or downloading episodes get icons, tooltips, and maybe a title. switch (state) { - case PodcastDownloader::Queued: + case PodcastDownload::Queued: if (queued_icon_.isNull()) { queued_icon_ = QIcon(":icons/22x22/user-away.png"); } @@ -258,7 +259,7 @@ void PodcastService::UpdateEpisodeText(QStandardItem* item, tooltip = tr("Download queued"); break; - case PodcastDownloader::Downloading: + case PodcastDownload::Downloading: if (downloading_icon_.isNull()) { downloading_icon_ = IconLoader::Load("go-down"); } @@ -268,8 +269,8 @@ void PodcastService::UpdateEpisodeText(QStandardItem* item, QString("[ %1% ] %2").arg(QString::number(percent), episode.title()); break; - case PodcastDownloader::Finished: - case PodcastDownloader::NotDownloading: + case PodcastDownload::Finished: + case PodcastDownload::NotDownloading: break; } @@ -278,6 +279,46 @@ void PodcastService::UpdateEpisodeText(QStandardItem* item, item->setIcon(icon); } +void PodcastService::UpdatePodcastText(QStandardItem* item, + PodcastDownload::State state, + int percent) { + const Podcast podcast = + item->data(Role_Podcast).value(); + + QString tooltip; + QIcon icon; + + // Queued or downloading podcasts get icons, tooltips, and maybe a title. + switch (state) { + case PodcastDownload::Queued: + if (queued_icon_.isNull()) { + queued_icon_ = QIcon(":icons/22x22/user-away.png"); + } + icon = queued_icon_; + item->setIcon(icon); + tooltip = tr("Download queued"); + break; + + case PodcastDownload::Downloading: + if (downloading_icon_.isNull()) { + downloading_icon_ = IconLoader::Load("go-down"); + } + icon = downloading_icon_; + item->setIcon(icon); + tooltip = tr("Downloading (%1%)...").arg(percent); + break; + + case PodcastDownload::Finished: + case PodcastDownload::NotDownloading: + if (podcast.ImageUrlSmall().isValid()) { + icon_loader_->LoadIcon(podcast.ImageUrlSmall().toString(), QString(), item); + } else { + item->setIcon(default_icon_); + } + break; + } +} + QStandardItem* PodcastService::CreatePodcastItem(const Podcast& podcast) { QStandardItem* item = new QStandardItem; @@ -583,18 +624,20 @@ void PodcastService::DownloadSelectedEpisode() { void PodcastService::DeleteDownloadedData() { for (const QModelIndex& index : selected_episodes_) { - app_->podcast_downloader()->DeleteEpisode( + app_->podcast_deleter()->DeleteEpisode( index.data(Role_Episode).value()); } } void PodcastService::DownloadProgressChanged(const PodcastEpisode& episode, - PodcastDownloader::State state, + PodcastDownload::State state, int percent) { QStandardItem* item = episodes_by_database_id_[episode.database_id()]; - if (!item) return; + QStandardItem* item2 = podcasts_by_database_id_[episode.podcast_database_id()]; + if (!item || !item2) return; UpdateEpisodeText(item, state, percent); + UpdatePodcastText(item2, state, percent); } void PodcastService::ShowConfig() { diff --git a/src/podcasts/podcastservice.h b/src/podcasts/podcastservice.h index 92fcf9140..02e8cfbb6 100644 --- a/src/podcasts/podcastservice.h +++ b/src/podcasts/podcastservice.h @@ -20,6 +20,7 @@ #ifndef PODCASTS_PODCASTSERVICE_H_ #define PODCASTS_PODCASTSERVICE_H_ +#include "podcastdeleter.h" #include "podcastdownloader.h" #include "internet/internetmodel.h" #include "internet/internetservice.h" @@ -84,7 +85,7 @@ class PodcastService : public InternetService { void EpisodesUpdated(const PodcastEpisodeList& episodes); void DownloadProgressChanged(const PodcastEpisode& episode, - PodcastDownloader::State state, int percent); + PodcastDownload::State state, int percent); void CurrentSongChanged(const Song& metadata); @@ -101,7 +102,11 @@ class PodcastService : public InternetService { void UpdatePodcastText(QStandardItem* item, int unlistened_count) const; void UpdateEpisodeText( QStandardItem* item, - PodcastDownloader::State state = PodcastDownloader::NotDownloading, + PodcastDownload::State state = PodcastDownload::NotDownloading, + int percent = 0); + void UpdatePodcastText( + QStandardItem* item, + PodcastDownload::State state = PodcastDownload::NotDownloading, int percent = 0); QStandardItem* CreatePodcastItem(const Podcast& podcast); From a07f115088b774fcc8c768a66f85cab1866b76cc Mon Sep 17 00:00:00 2001 From: Krzysztof Sobiecki Date: Wed, 10 Dec 2014 01:23:43 +0100 Subject: [PATCH 03/12] Fix copyright notices --- src/podcasts/podcastdeleter.cpp | 4 +--- src/podcasts/podcastdeleter.h | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/podcasts/podcastdeleter.cpp b/src/podcasts/podcastdeleter.cpp index 2a199aaf5..80bf653c6 100644 --- a/src/podcasts/podcastdeleter.cpp +++ b/src/podcasts/podcastdeleter.cpp @@ -1,7 +1,5 @@ /* This file is part of Clementine. - Copyright 2012, David Sansome - Copyright 2014, Krzysztof A. Sobiecki - Copyright 2014, John Maguire + Copyright 2014, Krzysztof Sobiecki Clementine is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/podcasts/podcastdeleter.h b/src/podcasts/podcastdeleter.h index a75fc3ad3..f3a831a4e 100644 --- a/src/podcasts/podcastdeleter.h +++ b/src/podcasts/podcastdeleter.h @@ -1,6 +1,4 @@ /* This file is part of Clementine. - Copyright 2012, David Sansome - Copyright 2014, John Maguire Copyright 2014, Krzysztof Sobiecki Clementine is free software: you can redistribute it and/or modify From 793ece163ac8e2beb1c94584c916fb6982dda0e9 Mon Sep 17 00:00:00 2001 From: Krzysztof Sobiecki Date: Wed, 10 Dec 2014 01:24:51 +0100 Subject: [PATCH 04/12] Fix copyright notices --- src/podcasts/podcastservice.cpp | 3 ++- src/podcasts/podcastservice.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/podcasts/podcastservice.cpp b/src/podcasts/podcastservice.cpp index 3b22b1e56..a9f2b92f1 100644 --- a/src/podcasts/podcastservice.cpp +++ b/src/podcasts/podcastservice.cpp @@ -1,7 +1,8 @@ /* This file is part of Clementine. Copyright 2012, 2014, John Maguire Copyright 2012-2013, David Sansome - Copyright 2013-2014, Krzysztof Sobiecki + Copyright 2013-2014, Krzysztof A. Sobiecki + Copyright 2014, Simeon Bird Clementine is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/podcasts/podcastservice.h b/src/podcasts/podcastservice.h index 02e8cfbb6..537c08aa4 100644 --- a/src/podcasts/podcastservice.h +++ b/src/podcasts/podcastservice.h @@ -1,7 +1,8 @@ /* This file is part of Clementine. Copyright 2012-2013, David Sansome - Copyright 2013-2014, Krzysztof Sobiecki + Copyright 2013-2014, Krzysztof A. Sobiecki Copyright 2014, John Maguire + Copyright 2014, Simeon Bird Clementine is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by From 2d2783ab045e798571163b4fa8058ea132818b52 Mon Sep 17 00:00:00 2001 From: Krzysztof Sobiecki Date: Wed, 10 Dec 2014 01:25:52 +0100 Subject: [PATCH 05/12] Fix copyright notices --- src/core/mac_startup.mm | 4 +++- src/core/macfslistener.mm | 1 + src/core/macglobalshortcutbackend.mm | 3 ++- src/core/organise.cpp | 1 + src/core/organise.h | 1 + src/core/song.cpp | 2 +- src/core/song.h | 1 + 7 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/core/mac_startup.mm b/src/core/mac_startup.mm index 9d90ce941..7cc49af85 100644 --- a/src/core/mac_startup.mm +++ b/src/core/mac_startup.mm @@ -1,5 +1,7 @@ /* This file is part of Clementine. - Copyright 2010, David Sansome + Copyright 2010-2011, David Sansome + Copyright 2010-2012, 2014, John Maguire + Copyright 2011, Tyler Rhodes Clementine is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/core/macfslistener.mm b/src/core/macfslistener.mm index 746109808..da2ab00e3 100644 --- a/src/core/macfslistener.mm +++ b/src/core/macfslistener.mm @@ -1,5 +1,6 @@ /* This file is part of Clementine. Copyright 2012, David Sansome + Copyright 2012, 2014, John Maguire Clementine is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/core/macglobalshortcutbackend.mm b/src/core/macglobalshortcutbackend.mm index 566a93cc9..50fe41f1e 100644 --- a/src/core/macglobalshortcutbackend.mm +++ b/src/core/macglobalshortcutbackend.mm @@ -1,5 +1,6 @@ /* This file is part of Clementine. - Copyright 2010, David Sansome + Copyright 2010, David Sansome + Copyright 2010-2011, 2014, John Maguire Clementine is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/core/organise.cpp b/src/core/organise.cpp index 928a6fb97..abec61718 100644 --- a/src/core/organise.cpp +++ b/src/core/organise.cpp @@ -1,6 +1,7 @@ /* This file is part of Clementine. Copyright 2010-2011, David Sansome Copyright 2014, Arnaud Bienner + Copyright 2014, Andreas Copyright 2014, Krzysztof A. Sobiecki Copyright 2014, John Maguire diff --git a/src/core/organise.h b/src/core/organise.h index 5aa5128fa..09905860f 100644 --- a/src/core/organise.h +++ b/src/core/organise.h @@ -1,6 +1,7 @@ /* This file is part of Clementine. Copyright 2010, David Sansome Copyright 2014, Arnaud Bienner + Copyright 2014, Andreas Copyright 2014, Krzysztof Sobiecki Copyright 2014, John Maguire diff --git a/src/core/song.cpp b/src/core/song.cpp index 8b5f89b7d..9ecc895e6 100644 --- a/src/core/song.cpp +++ b/src/core/song.cpp @@ -6,7 +6,7 @@ Copyright 2011, Angus Gratton Copyright 2012, Kacper "mattrick" Banasik Copyright 2013, Martin Brodbeck - Copyright 2013, Andreas + Copyright 2013-2014, Andreas Copyright 2013, Joel Bradshaw Copyright 2013, Uwe Klotz Copyright 2013, Mateusz Kowalczyk diff --git a/src/core/song.h b/src/core/song.h index f8247aa91..938429fbd 100644 --- a/src/core/song.h +++ b/src/core/song.h @@ -9,6 +9,7 @@ Copyright 2013, Joel Bradshaw Copyright 2013, Uwe Klotz Copyright 2013, Mateusz Kowalczyk + Copyright 2014, Andreas Copyright 2014, Krzysztof Sobiecki Clementine is free software: you can redistribute it and/or modify From 8cbe742d9f0c9333dd430f7c98a05a5568a72329 Mon Sep 17 00:00:00 2001 From: Krzysztof Sobiecki Date: Wed, 10 Dec 2014 21:57:09 +0100 Subject: [PATCH 06/12] Fix errors, needed for a pull --- src/podcasts/podcastbackend.cpp | 1 - src/podcasts/podcastdeleter.cpp | 17 ++++++++------- src/podcasts/podcastdownloader.cpp | 35 +++++++++++------------------- src/podcasts/podcastdownloader.h | 12 +++++----- 4 files changed, 28 insertions(+), 37 deletions(-) diff --git a/src/podcasts/podcastbackend.cpp b/src/podcasts/podcastbackend.cpp index 540dbe0d8..7aace691d 100644 --- a/src/podcasts/podcastbackend.cpp +++ b/src/podcasts/podcastbackend.cpp @@ -324,7 +324,6 @@ PodcastEpisodeList PodcastBackend::GetOldDownloadedEpisodes( PodcastEpisode PodcastBackend::GetOldestDownloadedListenedEpisode() { PodcastEpisode ret; - PodcastEpisodeList list_; QMutexLocker l(db_->Mutex()); QSqlDatabase db(db_->Connect()); diff --git a/src/podcasts/podcastdeleter.cpp b/src/podcasts/podcastdeleter.cpp index 80bf653c6..93d6d87c3 100644 --- a/src/podcasts/podcastdeleter.cpp +++ b/src/podcasts/podcastdeleter.cpp @@ -15,14 +15,14 @@ along with Clementine. If not, see . */ -#include "podcastbackend.h" -#include "podcastdeleter.h" #include "core/application.h" #include "core/logging.h" #include "core/timeconstants.h" #include "core/utilities.h" #include "library/librarydirectorymodel.h" #include "library/librarymodel.h" +#include "podcastbackend.h" +#include "podcastdeleter.h" #include #include @@ -43,6 +43,7 @@ PodcastDeleter::PodcastDeleter(Application* app, QObject* parent) auto_delete_timer_(new QTimer(this)) { ReloadSettings(); + auto_delete_timer_->setSingleShot(true); AutoDelete(); connect(auto_delete_timer_, SIGNAL(timeout()), SLOT(AutoDelete())); connect(app_, SIGNAL(SettingsChanged()), SLOT(ReloadSettings())); @@ -76,7 +77,7 @@ void PodcastDeleter::AutoDelete() { } auto_delete_timer_->stop(); QDateTime max_date = QDateTime::currentDateTime(); - QDateTime time_out; + qint64 time_out; PodcastEpisode oldest_episode; QDateTime oldest_episode_time; max_date = max_date.addSecs(-delete_after_secs_); @@ -99,11 +100,11 @@ void PodcastDeleter::AutoDelete() { oldest_episode_time = oldest_episode.listened_date(); } - time_out = QDateTime::currentDateTime(); - time_out = time_out.addMSecs(-oldest_episode_time.toMSecsSinceEpoch()); - time_out.setTime_t(delete_after_secs_ - time_out.toTime_t()); - if (time_out.isValid()) { - auto_delete_timer_->setInterval(time_out.toMSecsSinceEpoch()); + time_out = QDateTime::currentDateTime().toMSecsSinceEpoch(); + time_out -= oldest_episode_time.toMSecsSinceEpoch(); + time_out = (delete_after_secs_ * kMsecPerSec) - time_out; + if (time_out >= 0) { + auto_delete_timer_->setInterval(time_out); } else { auto_delete_timer_->setInterval(kAutoDeleteCheckIntervalMsec); } diff --git a/src/podcasts/podcastdownloader.cpp b/src/podcasts/podcastdownloader.cpp index 2594aa838..525f58a50 100644 --- a/src/podcasts/podcastdownloader.cpp +++ b/src/podcasts/podcastdownloader.cpp @@ -17,8 +17,6 @@ along with Clementine. If not, see . */ -#include "podcastbackend.h" -#include "podcastdownloader.h" #include "core/application.h" #include "core/logging.h" #include "core/network.h" @@ -27,6 +25,8 @@ #include "core/utilities.h" #include "library/librarydirectorymodel.h" #include "library/librarymodel.h" +#include "podcastbackend.h" +#include "podcastdownloader.h" #include #include @@ -40,24 +40,18 @@ const char* PodcastDownloader::kSettingsGroup = "Podcasts"; Task::Task(PodcastEpisode episode, QFile* file, PodcastBackend* backend) : file_(file), episode_(episode), + req_(QNetworkRequest(episode.url())), backend_(backend), - network_(new NetworkAccessManager(this)) { - req_ = QNetworkRequest(episode_.url()); - repl = new RedirectFollower(network_->get(req_)); - connect(repl, SIGNAL(readyRead()), SLOT(reading())); - connect(repl, SIGNAL(finished()), SLOT(finished_())); - connect(repl, SIGNAL(downloadProgress(qint64, qint64)), + network_(new NetworkAccessManager(this)), + repl(new RedirectFollower(network_->get(req_))) { + connect(repl.get(), SIGNAL(readyRead()), SLOT(reading())); + connect(repl.get(), SIGNAL(finished()), SLOT(finishedInternal())); + connect(repl.get(), SIGNAL(downloadProgress(qint64, qint64)), SLOT(downloadProgress_(qint64, qint64))); emit ProgressChanged(episode_, PodcastDownload::Downloading, 0); } -Task::~Task() { - delete repl; - delete file_; - delete network_; -} - -PodcastEpisode Task::episode() { +const PodcastEpisode Task::episode() { return episode_; } @@ -71,7 +65,7 @@ void Task::reading() { } } -void Task::finished_() { +void Task::finishedInternal() { if (repl->error() != QNetworkReply::NoError) { qLog(Warning) << "Error downloading episode:" << repl->errorString(); emit ProgressChanged(episode_, PodcastDownload::NotDownloading, 0); @@ -177,9 +171,6 @@ QString PodcastDownloader::FilenameForEpisode(const QString& directory, } void PodcastDownloader::DownloadEpisode(const PodcastEpisode& episode) { - QFile* file = nullptr; - Task* task = nullptr; - for ( Task* tas : list_tasks_ ) { if (tas->episode().database_id() == episode.database_id()) { return; @@ -189,7 +180,7 @@ void PodcastDownloader::DownloadEpisode(const PodcastEpisode& episode) { Podcast podcast = backend_->GetSubscriptionById(episode.podcast_database_id()); if (!podcast.is_valid()) { - qLog(Warning) << "The podcast that contains episode" << task->episode().url() + qLog(Warning) << "The podcast that contains episode" << episode.url() << "doesn't exist any more"; return; } @@ -199,13 +190,13 @@ void PodcastDownloader::DownloadEpisode(const PodcastEpisode& episode) { // Open the output file QDir().mkpath(directory); - file = new QFile(filepath); + QFile* file = new QFile(filepath); if (!file->open(QIODevice::WriteOnly)) { qLog(Warning) << "Could not open the file" << filepath << "for writing"; return; } - task = new Task(episode, file, backend_); + Task* task = new Task(episode, file, backend_); list_tasks_ << task; qLog(Info) << "Downloading" << task->episode().url() << "to" << filepath; diff --git a/src/podcasts/podcastdownloader.h b/src/podcasts/podcastdownloader.h index 9e1e2e6e4..cfdb4044a 100644 --- a/src/podcasts/podcastdownloader.h +++ b/src/podcasts/podcastdownloader.h @@ -24,6 +24,7 @@ #include "podcast.h" #include "podcastepisode.h" +#include #include #include #include @@ -51,8 +52,7 @@ class Task : public QObject { public: Task(PodcastEpisode episode, QFile* file, PodcastBackend* backend); - ~Task(); - PodcastEpisode episode(); + const PodcastEpisode episode(); signals: void ProgressChanged(const PodcastEpisode& episode, @@ -61,16 +61,16 @@ class Task : public QObject { private slots: void reading(); - void finished_(); + void finishedInternal(); void downloadProgress_(qint64 received, qint64 total); private: - QFile* file_; + std::unique_ptr file_; PodcastEpisode episode_; QNetworkRequest req_; PodcastBackend* backend_; - NetworkAccessManager* network_; - RedirectFollower* repl; + std::unique_ptr network_; + std::unique_ptr repl; }; class PodcastDownloader : public QObject { From 3bdf765e3d0efd65e92dd3750f30129fb53c1a7d Mon Sep 17 00:00:00 2001 From: Krzysztof Sobiecki Date: Wed, 10 Dec 2014 22:13:01 +0100 Subject: [PATCH 07/12] Fix errors, cherry picked make format fixes --- src/podcasts/podcastdeleter.cpp | 1 - src/podcasts/podcastdownloader.cpp | 20 +++++++++----------- src/podcasts/podcastservice.cpp | 23 ++++++++++------------- 3 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/podcasts/podcastdeleter.cpp b/src/podcasts/podcastdeleter.cpp index 93d6d87c3..611f2d94f 100644 --- a/src/podcasts/podcastdeleter.cpp +++ b/src/podcasts/podcastdeleter.cpp @@ -41,7 +41,6 @@ PodcastDeleter::PodcastDeleter(Application* app, QObject* parent) backend_(app_->podcast_backend()), delete_after_secs_(0), auto_delete_timer_(new QTimer(this)) { - ReloadSettings(); auto_delete_timer_->setSingleShot(true); AutoDelete(); diff --git a/src/podcasts/podcastdownloader.cpp b/src/podcasts/podcastdownloader.cpp index 525f58a50..369d7a084 100644 --- a/src/podcasts/podcastdownloader.cpp +++ b/src/podcasts/podcastdownloader.cpp @@ -38,12 +38,12 @@ const char* PodcastDownloader::kSettingsGroup = "Podcasts"; Task::Task(PodcastEpisode episode, QFile* file, PodcastBackend* backend) - : file_(file), - episode_(episode), - req_(QNetworkRequest(episode.url())), - backend_(backend), - network_(new NetworkAccessManager(this)), - repl(new RedirectFollower(network_->get(req_))) { + : file_(file), + episode_(episode), + req_(QNetworkRequest(episode.url())), + backend_(backend), + network_(new NetworkAccessManager(this)), + repl(new RedirectFollower(network_->get(req_))) { connect(repl.get(), SIGNAL(readyRead()), SLOT(reading())); connect(repl.get(), SIGNAL(finished()), SLOT(finishedInternal())); connect(repl.get(), SIGNAL(downloadProgress(qint64, qint64)), @@ -51,9 +51,7 @@ Task::Task(PodcastEpisode episode, QFile* file, PodcastBackend* backend) emit ProgressChanged(episode_, PodcastDownload::Downloading, 0); } -const PodcastEpisode Task::episode() { - return episode_; -} +const PodcastEpisode Task::episode() { return episode_; } void Task::reading() { qint64 bytes = 0; @@ -84,7 +82,7 @@ void Task::finishedInternal() { episode.set_local_url(QUrl::fromLocalFile(file_->fileName())); backend_->UpdateEpisodes(PodcastEpisodeList() << episode); Podcast podcast = - backend_->GetSubscriptionById(episode.podcast_database_id()); + backend_->GetSubscriptionById(episode.podcast_database_id()); Song song = episode_.ToSong(podcast); emit ProgressChanged(episode_, PodcastDownload::Finished, 0); @@ -171,7 +169,7 @@ QString PodcastDownloader::FilenameForEpisode(const QString& directory, } void PodcastDownloader::DownloadEpisode(const PodcastEpisode& episode) { - for ( Task* tas : list_tasks_ ) { + for (Task* tas : list_tasks_) { if (tas->episode().database_id() == episode.database_id()) { return; } diff --git a/src/podcasts/podcastservice.cpp b/src/podcasts/podcastservice.cpp index a9f2b92f1..8e5e4fc95 100644 --- a/src/podcasts/podcastservice.cpp +++ b/src/podcasts/podcastservice.cpp @@ -192,11 +192,9 @@ void PodcastService::LazyPopulate(QStandardItem* parent) { void PodcastService::PopulatePodcastList(QStandardItem* parent) { // Do this here since the downloader won't be created yet in the ctor. - connect( - app_->podcast_downloader(), - SIGNAL(ProgressChanged(PodcastEpisode, PodcastDownload::State, int)), - SLOT(DownloadProgressChanged(PodcastEpisode, PodcastDownload::State, - int))); + connect(app_->podcast_downloader(), + SIGNAL(ProgressChanged(PodcastEpisode, PodcastDownload::State, int)), + SLOT(DownloadProgressChanged(PodcastEpisode, PodcastDownload::State, int))); if (default_icon_.isNull()) { default_icon_ = QIcon(":providers/podcast16.png"); @@ -260,7 +258,7 @@ void PodcastService::UpdateEpisodeText(QStandardItem* item, tooltip = tr("Download queued"); break; - case PodcastDownload::Downloading: + case PodcastDownload::Downloading: if (downloading_icon_.isNull()) { downloading_icon_ = IconLoader::Load("go-down"); } @@ -270,8 +268,8 @@ void PodcastService::UpdateEpisodeText(QStandardItem* item, QString("[ %1% ] %2").arg(QString::number(percent), episode.title()); break; - case PodcastDownload::Finished: - case PodcastDownload::NotDownloading: + case PodcastDownload::Finished: + case PodcastDownload::NotDownloading: break; } @@ -283,8 +281,7 @@ void PodcastService::UpdateEpisodeText(QStandardItem* item, void PodcastService::UpdatePodcastText(QStandardItem* item, PodcastDownload::State state, int percent) { - const Podcast podcast = - item->data(Role_Podcast).value(); + const Podcast podcast = item->data(Role_Podcast).value(); QString tooltip; QIcon icon; @@ -300,7 +297,7 @@ void PodcastService::UpdatePodcastText(QStandardItem* item, tooltip = tr("Download queued"); break; - case PodcastDownload::Downloading: + case PodcastDownload::Downloading: if (downloading_icon_.isNull()) { downloading_icon_ = IconLoader::Load("go-down"); } @@ -309,8 +306,8 @@ void PodcastService::UpdatePodcastText(QStandardItem* item, tooltip = tr("Downloading (%1%)...").arg(percent); break; - case PodcastDownload::Finished: - case PodcastDownload::NotDownloading: + case PodcastDownload::Finished: + case PodcastDownload::NotDownloading: if (podcast.ImageUrlSmall().isValid()) { icon_loader_->LoadIcon(podcast.ImageUrlSmall().toString(), QString(), item); } else { From a43a39b6699a59dbdb0420bb4164fcfe4e33d276 Mon Sep 17 00:00:00 2001 From: Krzysztof Sobiecki Date: Wed, 10 Dec 2014 22:17:10 +0100 Subject: [PATCH 08/12] Fix errors, newline was here --- src/podcasts/podcastdownloader.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/podcasts/podcastdownloader.cpp b/src/podcasts/podcastdownloader.cpp index 369d7a084..8d42f4741 100644 --- a/src/podcasts/podcastdownloader.cpp +++ b/src/podcasts/podcastdownloader.cpp @@ -228,4 +228,3 @@ void PodcastDownloader::EpisodesAdded(const PodcastEpisodeList& episodes) { } } } - From 741bceab021bc3386385968dd09b4e8af016436c Mon Sep 17 00:00:00 2001 From: Krzysztof Sobiecki Date: Fri, 12 Dec 2014 15:38:34 +0100 Subject: [PATCH 09/12] Ability to cancel downloads in progress Fixes #3661 --- src/podcasts/podcastbackend.cpp | 5 ++-- src/podcasts/podcastdeleter.cpp | 27 +++++++++--------- src/podcasts/podcastdownloader.cpp | 45 +++++++++++++++++++++++++++--- src/podcasts/podcastdownloader.h | 10 +++++-- src/podcasts/podcastepisode.cpp | 9 +++--- src/podcasts/podcastservice.cpp | 41 +++++++++++++++++++++++---- src/podcasts/podcastservice.h | 4 +++ 7 files changed, 110 insertions(+), 31 deletions(-) diff --git a/src/podcasts/podcastbackend.cpp b/src/podcasts/podcastbackend.cpp index 7aace691d..e4a5a7fda 100644 --- a/src/podcasts/podcastbackend.cpp +++ b/src/podcasts/podcastbackend.cpp @@ -18,13 +18,14 @@ */ #include "podcastbackend.h" + +#include + #include "core/application.h" #include "core/database.h" #include "core/logging.h" #include "core/scopedtransaction.h" -#include - PodcastBackend::PodcastBackend(Application* app, QObject* parent) : QObject(parent), app_(app), db_(app->database()) {} diff --git a/src/podcasts/podcastdeleter.cpp b/src/podcasts/podcastdeleter.cpp index 611f2d94f..11f35b037 100644 --- a/src/podcasts/podcastdeleter.cpp +++ b/src/podcasts/podcastdeleter.cpp @@ -15,13 +15,6 @@ along with Clementine. If not, see . */ -#include "core/application.h" -#include "core/logging.h" -#include "core/timeconstants.h" -#include "core/utilities.h" -#include "library/librarydirectorymodel.h" -#include "library/librarymodel.h" -#include "podcastbackend.h" #include "podcastdeleter.h" #include @@ -31,6 +24,14 @@ #include #include +#include "core/application.h" +#include "core/logging.h" +#include "core/timeconstants.h" +#include "core/utilities.h" +#include "library/librarydirectorymodel.h" +#include "library/librarymodel.h" +#include "podcastbackend.h" + const char* PodcastDeleter::kSettingsGroup = "Podcasts"; const int PodcastDeleter::kAutoDeleteCheckIntervalMsec = 60 * 6 * 60 * kMsecPerSec; @@ -76,7 +77,7 @@ void PodcastDeleter::AutoDelete() { } auto_delete_timer_->stop(); QDateTime max_date = QDateTime::currentDateTime(); - qint64 time_out; + qint64 timeout_ms; PodcastEpisode oldest_episode; QDateTime oldest_episode_time; max_date = max_date.addSecs(-delete_after_secs_); @@ -99,11 +100,11 @@ void PodcastDeleter::AutoDelete() { oldest_episode_time = oldest_episode.listened_date(); } - time_out = QDateTime::currentDateTime().toMSecsSinceEpoch(); - time_out -= oldest_episode_time.toMSecsSinceEpoch(); - time_out = (delete_after_secs_ * kMsecPerSec) - time_out; - if (time_out >= 0) { - auto_delete_timer_->setInterval(time_out); + timeout_ms = QDateTime::currentDateTime().toMSecsSinceEpoch(); + timeout_ms -= oldest_episode_time.toMSecsSinceEpoch(); + timeout_ms = (delete_after_secs_ * kMsecPerSec) - timeout_ms; + if (timeout_ms >= 0) { + auto_delete_timer_->setInterval(timeout_ms); } else { auto_delete_timer_->setInterval(kAutoDeleteCheckIntervalMsec); } diff --git a/src/podcasts/podcastdownloader.cpp b/src/podcasts/podcastdownloader.cpp index 8d42f4741..3be213628 100644 --- a/src/podcasts/podcastdownloader.cpp +++ b/src/podcasts/podcastdownloader.cpp @@ -47,11 +47,11 @@ Task::Task(PodcastEpisode episode, QFile* file, PodcastBackend* backend) connect(repl.get(), SIGNAL(readyRead()), SLOT(reading())); connect(repl.get(), SIGNAL(finished()), SLOT(finishedInternal())); connect(repl.get(), SIGNAL(downloadProgress(qint64, qint64)), - SLOT(downloadProgress_(qint64, qint64))); + SLOT(downloadProgressInternal(qint64, qint64))); emit ProgressChanged(episode_, PodcastDownload::Downloading, 0); } -const PodcastEpisode Task::episode() { return episode_; } +PodcastEpisode Task::episode() const { return episode_; } void Task::reading() { qint64 bytes = 0; @@ -62,6 +62,15 @@ void Task::reading() { file_->write(repl->reply()->read(bytes)); } } +void Task::finishedPublic() { + disconnect(repl.get(), SIGNAL(readyRead()), 0, 0); + disconnect(repl.get(), SIGNAL(downloadProgress(qint64, qint64)), 0, 0); + disconnect(repl.get(), SIGNAL(finished()), 0, 0); + emit ProgressChanged(episode_, PodcastDownload::NotDownloading, 0); + // Delete the file + file_->remove(); + emit finished(); +} void Task::finishedInternal() { if (repl->error() != QNetworkReply::NoError) { @@ -86,13 +95,13 @@ void Task::finishedInternal() { Song song = episode_.ToSong(podcast); emit ProgressChanged(episode_, PodcastDownload::Finished, 0); - emit finished(); // I didn't ecountered even a single podcast with a corect metadata TagReaderClient::Instance()->SaveFileBlocking(file_->fileName(), song); + emit finished(); } -void Task::downloadProgress_(qint64 received, qint64 total) { +void Task::downloadProgressInternal(qint64 received, qint64 total) { if (total <= 0) { emit ProgressChanged(episode_, PodcastDownload::Downloading, 0); } else { @@ -208,6 +217,7 @@ void PodcastDownloader::DownloadEpisode(const PodcastEpisode& episode) { void PodcastDownloader::ReplyFinished() { Task* task = qobject_cast(sender()); list_tasks_.removeAll(task); + delete task; } QString PodcastDownloader::SanitiseFilenameComponent(const QString& text) @@ -228,3 +238,30 @@ void PodcastDownloader::EpisodesAdded(const PodcastEpisodeList& episodes) { } } } + +PodcastEpisodeList PodcastDownloader::EpisodesDownloading(const PodcastEpisodeList& episodes) { + PodcastEpisodeList ret; + for (Task* tas : list_tasks_) { + for (PodcastEpisode episode : episodes) { + if (tas->episode().database_id() == episode.database_id()) { + ret << episode; + } + } + } + return ret; +} + +void PodcastDownloader::cancelDownload(const PodcastEpisodeList& episodes) { + QList ta; + for (Task* tas : list_tasks_) { + for (PodcastEpisode episode : episodes) { + if (tas->episode().database_id() == episode.database_id()) { + ta << tas; + } + } + } + for (Task* tas : ta) { + tas->finishedPublic(); + list_tasks_.removeAll(tas); + } +} diff --git a/src/podcasts/podcastdownloader.h b/src/podcasts/podcastdownloader.h index cfdb4044a..4f3e2c3dd 100644 --- a/src/podcasts/podcastdownloader.h +++ b/src/podcasts/podcastdownloader.h @@ -52,17 +52,20 @@ class Task : public QObject { public: Task(PodcastEpisode episode, QFile* file, PodcastBackend* backend); - const PodcastEpisode episode(); + PodcastEpisode episode() const; signals: void ProgressChanged(const PodcastEpisode& episode, PodcastDownload::State state, int percent); void finished(); + public slots: + void finishedPublic(); + private slots: void reading(); + void downloadProgressInternal(qint64 received, qint64 total); void finishedInternal(); - void downloadProgress_(qint64 received, qint64 total); private: std::unique_ptr file_; @@ -80,12 +83,13 @@ class PodcastDownloader : public QObject { explicit PodcastDownloader(Application* app, QObject* parent = nullptr); static const char* kSettingsGroup; - + PodcastEpisodeList EpisodesDownloading(const PodcastEpisodeList& episodes); QString DefaultDownloadDir() const; public slots: // Adds the episode to the download queue void DownloadEpisode(const PodcastEpisode& episode); + void cancelDownload(const PodcastEpisodeList& episodes); signals: void ProgressChanged(const PodcastEpisode& episode, diff --git a/src/podcasts/podcastepisode.cpp b/src/podcasts/podcastepisode.cpp index abfafba8a..db21cf3a9 100644 --- a/src/podcasts/podcastepisode.cpp +++ b/src/podcasts/podcastepisode.cpp @@ -17,17 +17,18 @@ along with Clementine. If not, see . */ -#include "podcast.h" #include "podcastepisode.h" -#include "core/logging.h" -#include "core/timeconstants.h" -#include "core/utilities.h" #include #include #include #include +#include "podcast.h" +#include "core/logging.h" +#include "core/timeconstants.h" +#include "core/utilities.h" + const QStringList PodcastEpisode::kColumns = QStringList() << "podcast_id" << "title" << "description" diff --git a/src/podcasts/podcastservice.cpp b/src/podcasts/podcastservice.cpp index 8e5e4fc95..352f58cf5 100644 --- a/src/podcasts/podcastservice.cpp +++ b/src/podcasts/podcastservice.cpp @@ -18,12 +18,17 @@ along with Clementine. If not, see . */ +#include "podcastservice.h" + +#include +#include +#include + #include "addpodcastdialog.h" #include "opmlcontainer.h" #include "podcastbackend.h" #include "podcastdeleter.h" #include "podcastdownloader.h" -#include "podcastservice.h" #include "podcastservicemodel.h" #include "podcastupdater.h" #include "core/application.h" @@ -39,10 +44,6 @@ #include "ui/organiseerrordialog.h" #include "ui/standarditemiconloader.h" -#include -#include -#include - const char* PodcastService::kServiceName = "Podcasts"; const char* PodcastService::kSettingsGroup = "Podcasts"; @@ -181,6 +182,32 @@ void PodcastService::CopyToDevice(const QModelIndexList& episode_indexes, if (organise_dialog_->SetSongs(songs)) organise_dialog_->show(); } +void PodcastService::CancelDownload() { + CancelDownload(selected_episodes_, explicitly_selected_podcasts_); +} + +void PodcastService::CancelDownload(const QModelIndexList& episode_indexes, + const QModelIndexList& podcast_indexes) { + PodcastEpisode episode_tmp; + SongList songs; + PodcastEpisodeList episodes; + Podcast podcast; + for (const QModelIndex& index : episode_indexes) { + episode_tmp = index.data(Role_Episode).value(); + episodes << episode_tmp; + } + + for (const QModelIndex& podcast : podcast_indexes) { + for (int i = 0; i < podcast.model()->rowCount(podcast); ++i) { + const QModelIndex& index = podcast.child(i, 0); + episode_tmp = index.data(Role_Episode).value(); + episodes << episode_tmp; + } + } + episodes = app_->podcast_downloader()->EpisodesDownloading(episodes); + app_->podcast_downloader()->cancelDownload(episodes); +} + void PodcastService::LazyPopulate(QStandardItem* parent) { switch (parent->data(InternetModel::Role_Type).toInt()) { case InternetModel::Type_Service: @@ -391,6 +418,9 @@ void PodcastService::ShowContextMenu(const QPoint& global_pos) { copy_to_device_ = context_menu_->addAction( IconLoader::Load("multimedia-player-ipod-mini-blue"), tr("Copy to device..."), this, SLOT(CopyToDevice())); + cancel_download_ = context_menu_->addAction( + IconLoader::Load("cancel"), + tr("Cancel download"), this, SLOT(CancelDownload())); remove_selected_action_ = context_menu_->addAction( IconLoader::Load("list-remove"), tr("Unsubscribe"), this, SLOT(RemoveSelectedPodcast())); @@ -452,6 +482,7 @@ void PodcastService::ShowContextMenu(const QPoint& global_pos) { remove_selected_action_->setEnabled(podcasts); set_new_action_->setEnabled(episodes || podcasts); set_listened_action_->setEnabled(episodes || podcasts); + cancel_download_->setEnabled(episodes || podcasts); if (selected_episodes_.count() == 1) { const PodcastEpisode episode = diff --git a/src/podcasts/podcastservice.h b/src/podcasts/podcastservice.h index 537c08aa4..4ca81be63 100644 --- a/src/podcasts/podcastservice.h +++ b/src/podcasts/podcastservice.h @@ -94,6 +94,9 @@ class PodcastService : public InternetService { void CopyToDevice(const PodcastEpisodeList& episodes_list); void CopyToDevice(const QModelIndexList& episode_indexes, const QModelIndexList& podcast_indexes); + void CancelDownload(); + void CancelDownload(const QModelIndexList& episode_indexes, + const QModelIndexList& podcast_indexes); private: void EnsureAddPodcastDialogCreated(); @@ -145,6 +148,7 @@ class PodcastService : public InternetService { QAction* set_new_action_; QAction* set_listened_action_; QAction* copy_to_device_; + QAction* cancel_download_; QStandardItem* root_; std::unique_ptr organise_dialog_; From 418d65a1a7bf7356b97641acb3d615c2f2f492ab Mon Sep 17 00:00:00 2001 From: Krzysztof Sobiecki Date: Sat, 13 Dec 2014 02:05:08 +0100 Subject: [PATCH 10/12] Fix header files order --- src/podcasts/podcastdownloader.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/podcasts/podcastdownloader.cpp b/src/podcasts/podcastdownloader.cpp index 3be213628..b90160da2 100644 --- a/src/podcasts/podcastdownloader.cpp +++ b/src/podcasts/podcastdownloader.cpp @@ -17,6 +17,15 @@ along with Clementine. If not, see . */ +#include "podcastdownloader.h" + +#include +#include +#include +#include +#include +#include + #include "core/application.h" #include "core/logging.h" #include "core/network.h" @@ -26,14 +35,6 @@ #include "library/librarydirectorymodel.h" #include "library/librarymodel.h" #include "podcastbackend.h" -#include "podcastdownloader.h" - -#include -#include -#include -#include -#include -#include const char* PodcastDownloader::kSettingsGroup = "Podcasts"; From 1922d517801e8dafeaacbbb5a4ca78e1d0edb050 Mon Sep 17 00:00:00 2001 From: Krzysztof Sobiecki Date: Sat, 13 Dec 2014 02:24:20 +0100 Subject: [PATCH 11/12] Fix header files order --- src/podcasts/addpodcastbyurl.cpp | 9 +++++---- src/podcasts/addpodcastdialog.cpp | 9 +++++---- src/podcasts/fixedopmlpage.cpp | 5 +++-- src/podcasts/gpoddersearchpage.cpp | 5 +++-- src/podcasts/gpoddersync.cpp | 13 +++++++------ src/podcasts/gpoddertoptagsmodel.cpp | 8 ++++---- src/podcasts/gpoddertoptagspage.cpp | 7 ++++--- src/podcasts/itunessearchpage.cpp | 14 +++++++------- src/podcasts/podcast.cpp | 4 ++-- src/podcasts/podcastdiscoverymodel.cpp | 11 ++++++----- src/podcasts/podcastparser.cpp | 7 ++++--- src/podcasts/podcastservice.cpp | 12 ++++++------ src/podcasts/podcastsettingspage.cpp | 17 +++++++++-------- src/podcasts/podcastupdater.cpp | 11 ++++++----- 14 files changed, 71 insertions(+), 61 deletions(-) diff --git a/src/podcasts/addpodcastbyurl.cpp b/src/podcasts/addpodcastbyurl.cpp index b278077e1..00aa13095 100644 --- a/src/podcasts/addpodcastbyurl.cpp +++ b/src/podcasts/addpodcastbyurl.cpp @@ -18,15 +18,16 @@ */ #include "addpodcastbyurl.h" -#include "podcastdiscoverymodel.h" -#include "podcasturlloader.h" -#include "ui_addpodcastbyurl.h" -#include "core/closure.h" #include #include #include +#include "podcastdiscoverymodel.h" +#include "podcasturlloader.h" +#include "ui_addpodcastbyurl.h" +#include "core/closure.h" + AddPodcastByUrl::AddPodcastByUrl(Application* app, QWidget* parent) : AddPodcastPage(app, parent), ui_(new Ui_AddPodcastByUrl), diff --git a/src/podcasts/addpodcastdialog.cpp b/src/podcasts/addpodcastdialog.cpp index a12ad505e..1e8c69496 100644 --- a/src/podcasts/addpodcastdialog.cpp +++ b/src/podcasts/addpodcastdialog.cpp @@ -18,6 +18,11 @@ */ #include "addpodcastdialog.h" + +#include +#include +#include + #include "addpodcastbyurl.h" #include "fixedopmlpage.h" #include "gpoddersearchpage.h" @@ -30,10 +35,6 @@ #include "ui/iconloader.h" #include "widgets/widgetfadehelper.h" -#include -#include -#include - const char* AddPodcastDialog::kBbcOpmlUrl = "http://www.bbc.co.uk/podcasts.opml"; diff --git a/src/podcasts/fixedopmlpage.cpp b/src/podcasts/fixedopmlpage.cpp index ec26c0c35..66802b7c2 100644 --- a/src/podcasts/fixedopmlpage.cpp +++ b/src/podcasts/fixedopmlpage.cpp @@ -18,12 +18,13 @@ */ #include "fixedopmlpage.h" + +#include + #include "podcastdiscoverymodel.h" #include "podcasturlloader.h" #include "core/closure.h" -#include - FixedOpmlPage::FixedOpmlPage(const QUrl& opml_url, const QString& title, const QIcon& icon, Application* app, QWidget* parent) diff --git a/src/podcasts/gpoddersearchpage.cpp b/src/podcasts/gpoddersearchpage.cpp index fb1b8f785..835ebb337 100644 --- a/src/podcasts/gpoddersearchpage.cpp +++ b/src/podcasts/gpoddersearchpage.cpp @@ -18,14 +18,15 @@ */ #include "gpoddersearchpage.h" + +#include + #include "podcast.h" #include "podcastdiscoverymodel.h" #include "ui_gpoddersearchpage.h" #include "core/closure.h" #include "core/network.h" -#include - GPodderSearchPage::GPodderSearchPage(Application* app, QWidget* parent) : AddPodcastPage(app, parent), ui_(new Ui_GPodderSearchPage), diff --git a/src/podcasts/gpoddersync.cpp b/src/podcasts/gpoddersync.cpp index ab288bd91..76084ebfc 100644 --- a/src/podcasts/gpoddersync.cpp +++ b/src/podcasts/gpoddersync.cpp @@ -18,6 +18,13 @@ */ #include "gpoddersync.h" + +#include +#include +#include +#include +#include + #include "podcastbackend.h" #include "podcasturlloader.h" #include "core/application.h" @@ -28,12 +35,6 @@ #include "core/timeconstants.h" #include "core/utilities.h" -#include -#include -#include -#include -#include - const char* GPodderSync::kSettingsGroup = "Podcasts"; const int GPodderSync::kFlushUpdateQueueDelay = 30 * kMsecPerSec; // 30 seconds const int GPodderSync::kGetUpdatesInterval = diff --git a/src/podcasts/gpoddertoptagsmodel.cpp b/src/podcasts/gpoddertoptagsmodel.cpp index 3c2ceb713..b1926b09d 100644 --- a/src/podcasts/gpoddertoptagsmodel.cpp +++ b/src/podcasts/gpoddertoptagsmodel.cpp @@ -18,14 +18,14 @@ */ #include "gpoddertoptagsmodel.h" + +#include +#include + #include "gpoddertoptagspage.h" #include "podcast.h" #include "core/closure.h" -#include - -#include - GPodderTopTagsModel::GPodderTopTagsModel(mygpo::ApiRequest* api, Application* app, QObject* parent) : PodcastDiscoveryModel(app, parent), api_(api) {} diff --git a/src/podcasts/gpoddertoptagspage.cpp b/src/podcasts/gpoddertoptagspage.cpp index acc01e542..269cdb9ce 100644 --- a/src/podcasts/gpoddertoptagspage.cpp +++ b/src/podcasts/gpoddertoptagspage.cpp @@ -17,13 +17,14 @@ along with Clementine. If not, see . */ -#include "gpoddertoptagsmodel.h" #include "gpoddertoptagspage.h" -#include "core/closure.h" -#include "core/network.h" #include +#include "gpoddertoptagsmodel.h" +#include "core/closure.h" +#include "core/network.h" + const int GPodderTopTagsPage::kMaxTagCount = 100; GPodderTopTagsPage::GPodderTopTagsPage(Application* app, QWidget* parent) diff --git a/src/podcasts/itunessearchpage.cpp b/src/podcasts/itunessearchpage.cpp index db13c7f76..a17005f95 100644 --- a/src/podcasts/itunessearchpage.cpp +++ b/src/podcasts/itunessearchpage.cpp @@ -18,16 +18,16 @@ */ #include "itunessearchpage.h" + +#include +#include +#include + +#include "core/closure.h" +#include "core/network.h" #include "podcast.h" #include "podcastdiscoverymodel.h" #include "ui_itunessearchpage.h" -#include "core/closure.h" -#include "core/network.h" - -#include - -#include -#include const char* ITunesSearchPage::kUrlBase = "http://ax.phobos.apple.com.edgesuite.net/WebObjects/MZStoreServices.woa/" diff --git a/src/podcasts/podcast.cpp b/src/podcasts/podcast.cpp index 4cdd78912..bae36f761 100644 --- a/src/podcasts/podcast.cpp +++ b/src/podcasts/podcast.cpp @@ -18,13 +18,13 @@ */ #include "podcast.h" -#include "core/utilities.h" #include #include - #include +#include "core/utilities.h" + const QStringList Podcast::kColumns = QStringList() << "url" << "title" << "description" diff --git a/src/podcasts/podcastdiscoverymodel.cpp b/src/podcasts/podcastdiscoverymodel.cpp index f2e0b484b..744dbb9d7 100644 --- a/src/podcasts/podcastdiscoverymodel.cpp +++ b/src/podcasts/podcastdiscoverymodel.cpp @@ -17,16 +17,17 @@ along with Clementine. If not, see . */ -#include "opmlcontainer.h" -#include "podcast.h" #include "podcastdiscoverymodel.h" -#include "core/application.h" -#include "ui/iconloader.h" -#include "ui/standarditemiconloader.h" #include #include +#include "core/application.h" +#include "opmlcontainer.h" +#include "podcast.h" +#include "ui/iconloader.h" +#include "ui/standarditemiconloader.h" + PodcastDiscoveryModel::PodcastDiscoveryModel(Application* app, QObject* parent) : QStandardItemModel(parent), app_(app), diff --git a/src/podcasts/podcastparser.cpp b/src/podcasts/podcastparser.cpp index 3cf61f79a..6b3e81826 100644 --- a/src/podcasts/podcastparser.cpp +++ b/src/podcasts/podcastparser.cpp @@ -17,14 +17,15 @@ along with Clementine. If not, see . */ -#include "opmlcontainer.h" #include "podcastparser.h" -#include "core/logging.h" -#include "core/utilities.h" #include #include +#include "core/logging.h" +#include "core/utilities.h" +#include "opmlcontainer.h" + // Namespace constants must be lower case. const char* PodcastParser::kAtomNamespace = "http://www.w3.org/2005/atom"; const char* PodcastParser::kItunesNamespace = diff --git a/src/podcasts/podcastservice.cpp b/src/podcasts/podcastservice.cpp index 352f58cf5..bd4738ff8 100644 --- a/src/podcasts/podcastservice.cpp +++ b/src/podcasts/podcastservice.cpp @@ -25,12 +25,6 @@ #include #include "addpodcastdialog.h" -#include "opmlcontainer.h" -#include "podcastbackend.h" -#include "podcastdeleter.h" -#include "podcastdownloader.h" -#include "podcastservicemodel.h" -#include "podcastupdater.h" #include "core/application.h" #include "core/logging.h" #include "core/mergedproxymodel.h" @@ -39,6 +33,12 @@ #include "devices/deviceview.h" #include "internet/internetmodel.h" #include "library/libraryview.h" +#include "opmlcontainer.h" +#include "podcastbackend.h" +#include "podcastdeleter.h" +#include "podcastdownloader.h" +#include "podcastservicemodel.h" +#include "podcastupdater.h" #include "ui/iconloader.h" #include "ui/organisedialog.h" #include "ui/organiseerrordialog.h" diff --git a/src/podcasts/podcastsettingspage.cpp b/src/podcasts/podcastsettingspage.cpp index 542b4bc8c..04ef6930e 100644 --- a/src/podcasts/podcastsettingspage.cpp +++ b/src/podcasts/podcastsettingspage.cpp @@ -17,21 +17,22 @@ along with Clementine. If not, see . */ -#include "gpoddersync.h" -#include "podcastdownloader.h" #include "podcastsettingspage.h" #include "ui_podcastsettingspage.h" -#include "core/application.h" -#include "core/closure.h" -#include "core/timeconstants.h" -#include "library/librarydirectorymodel.h" -#include "library/librarymodel.h" -#include "ui/settingsdialog.h" #include #include #include +#include "core/application.h" +#include "core/closure.h" +#include "core/timeconstants.h" +#include "gpoddersync.h" +#include "library/librarydirectorymodel.h" +#include "library/librarymodel.h" +#include "podcastdownloader.h" +#include "ui/settingsdialog.h" + const char* PodcastSettingsPage::kSettingsGroup = "Podcasts"; PodcastSettingsPage::PodcastSettingsPage(SettingsDialog* dialog) diff --git a/src/podcasts/podcastupdater.cpp b/src/podcasts/podcastupdater.cpp index 77450461c..6876faed5 100644 --- a/src/podcasts/podcastupdater.cpp +++ b/src/podcasts/podcastupdater.cpp @@ -17,17 +17,18 @@ along with Clementine. If not, see . */ -#include "podcastbackend.h" #include "podcastupdater.h" -#include "podcasturlloader.h" + +#include +#include + #include "core/application.h" #include "core/closure.h" #include "core/logging.h" #include "core/qhash_qurl.h" #include "core/timeconstants.h" - -#include -#include +#include "podcastbackend.h" +#include "podcasturlloader.h" const char* PodcastUpdater::kSettingsGroup = "Podcasts"; From a7e8c927f4daf80e9fe5b34d51378061ee2316d8 Mon Sep 17 00:00:00 2001 From: Krzysztof Sobiecki Date: Sat, 13 Dec 2014 20:05:45 +0100 Subject: [PATCH 12/12] Now without sender --- src/podcasts/podcastdownloader.cpp | 11 +++++------ src/podcasts/podcastdownloader.h | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/podcasts/podcastdownloader.cpp b/src/podcasts/podcastdownloader.cpp index b90160da2..1ec266fdb 100644 --- a/src/podcasts/podcastdownloader.cpp +++ b/src/podcasts/podcastdownloader.cpp @@ -70,7 +70,7 @@ void Task::finishedPublic() { emit ProgressChanged(episode_, PodcastDownload::NotDownloading, 0); // Delete the file file_->remove(); - emit finished(); + emit finished(this); } void Task::finishedInternal() { @@ -79,7 +79,7 @@ void Task::finishedInternal() { emit ProgressChanged(episode_, PodcastDownload::NotDownloading, 0); // Delete the file file_->remove(); - emit finished(); + emit finished(this); return; } @@ -99,7 +99,7 @@ void Task::finishedInternal() { // I didn't ecountered even a single podcast with a corect metadata TagReaderClient::Instance()->SaveFileBlocking(file_->fileName(), song); - emit finished(); + emit finished(this); } void Task::downloadProgressInternal(qint64 received, qint64 total) { @@ -208,15 +208,14 @@ void PodcastDownloader::DownloadEpisode(const PodcastEpisode& episode) { list_tasks_ << task; qLog(Info) << "Downloading" << task->episode().url() << "to" << filepath; - connect(task, SIGNAL(finished()), SLOT(ReplyFinished())); + connect(task, SIGNAL(finished(Task*)), SLOT(ReplyFinished(Task*))); connect(task, SIGNAL(ProgressChanged(const PodcastEpisode&, PodcastDownload::State, int)), SIGNAL(ProgressChanged(const PodcastEpisode&, PodcastDownload::State, int))); } -void PodcastDownloader::ReplyFinished() { - Task* task = qobject_cast(sender()); +void PodcastDownloader::ReplyFinished(Task* task) { list_tasks_.removeAll(task); delete task; } diff --git a/src/podcasts/podcastdownloader.h b/src/podcasts/podcastdownloader.h index 4f3e2c3dd..7dbff6555 100644 --- a/src/podcasts/podcastdownloader.h +++ b/src/podcasts/podcastdownloader.h @@ -57,7 +57,7 @@ class Task : public QObject { signals: void ProgressChanged(const PodcastEpisode& episode, PodcastDownload::State state, int percent); - void finished(); + void finished(Task* task); public slots: void finishedPublic(); @@ -101,7 +101,7 @@ class PodcastDownloader : public QObject { void SubscriptionAdded(const Podcast& podcast); void EpisodesAdded(const PodcastEpisodeList& episodes); - void ReplyFinished(); + void ReplyFinished(Task* task); private: QString FilenameForEpisode(const QString& directory,