Ability to cancel downloads in progress

Fixes #3661
This commit is contained in:
Krzysztof Sobiecki 2014-12-12 15:38:34 +01:00
parent a43a39b669
commit 741bceab02
7 changed files with 110 additions and 31 deletions

View File

@ -18,13 +18,14 @@
*/
#include "podcastbackend.h"
#include <QMutexLocker>
#include "core/application.h"
#include "core/database.h"
#include "core/logging.h"
#include "core/scopedtransaction.h"
#include <QMutexLocker>
PodcastBackend::PodcastBackend(Application* app, QObject* parent)
: QObject(parent), app_(app), db_(app->database()) {}

View File

@ -15,13 +15,6 @@
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#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 <QDateTime>
@ -31,6 +24,14 @@
#include <QSettings>
#include <QTimer>
#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);
}

View File

@ -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<Task*>(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<Task*> 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);
}
}

View File

@ -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<QFile> 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,

View File

@ -17,17 +17,18 @@
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "podcast.h"
#include "podcastepisode.h"
#include "core/logging.h"
#include "core/timeconstants.h"
#include "core/utilities.h"
#include <QDataStream>
#include <QDateTime>
#include <QFile>
#include <QFileInfo>
#include "podcast.h"
#include "core/logging.h"
#include "core/timeconstants.h"
#include "core/utilities.h"
const QStringList PodcastEpisode::kColumns = QStringList() << "podcast_id"
<< "title"
<< "description"

View File

@ -18,12 +18,17 @@
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "podcastservice.h"
#include <QMenu>
#include <QSortFilterProxyModel>
#include <QtConcurrentRun>
#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 <QMenu>
#include <QSortFilterProxyModel>
#include <QtConcurrentRun>
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<PodcastEpisode>();
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<PodcastEpisode>();
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 =

View File

@ -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<OrganiseDialog> organise_dialog_;