From 2eb48c6fb404885517562fed545e657a7daceecf Mon Sep 17 00:00:00 2001 From: Ivailo Monev Date: Thu, 12 Aug 2021 16:55:15 +0300 Subject: [PATCH] kget: implement partial torrent download via priority Signed-off-by: Ivailo Monev --- kget/transfer-plugins/torrent/transferTorrent.cpp | 105 +++++++++++++++++++--- kget/transfer-plugins/torrent/transferTorrent.h | 9 ++ 2 files changed, 103 insertions(+), 11 deletions(-) diff --git a/kget/transfer-plugins/torrent/transferTorrent.cpp b/kget/transfer-plugins/torrent/transferTorrent.cpp index cccf720f..ef05551a 100644 --- a/kget/transfer-plugins/torrent/transferTorrent.cpp +++ b/kget/transfer-plugins/torrent/transferTorrent.cpp @@ -18,6 +18,7 @@ #include "transferTorrent.h" +#include #include #include @@ -34,6 +35,13 @@ static const int LTPollInterval = 1000; +enum LTPriorities { + Disabled = 0, + LowPriority = 1, + NormalPriority = 4, + HighPriority = 7, +}; + static QString translatelterror(lt::error_code lterror) { if (lterror == lt::errors::no_error) { @@ -338,7 +346,7 @@ TransferTorrent::TransferTorrent(TransferGroup* parent, TransferFactory* factory Scheduler* scheduler, const KUrl &source, const KUrl &dest, const QDomElement* e) : Transfer(parent, factory, scheduler, source, dest, e), - m_timerid(0), m_ltsession(nullptr), m_filemodel(nullptr) + m_timerid(0), m_ltsession(nullptr), m_filemodel(nullptr), m_recreatefilemodel(false) { setCapabilities(Transfer::Cap_SpeedLimit | Transfer::Cap_Resuming); @@ -434,6 +442,9 @@ void TransferTorrent::start() m_lthandle = m_ltsession->add_torrent(ltparams); m_lthandle.set_upload_limit(m_uploadLimit * 1024); m_lthandle.set_download_limit(m_downloadLimit * 1024); + if (m_priorities.size() > 0) { + m_lthandle.prioritize_files(m_priorities); + } setStatus(Job::Running); setTransferChange(Transfer::Tc_Status, true); @@ -500,27 +511,40 @@ QList TransferTorrent::files() const FileModel* TransferTorrent::fileModel() { + if (m_recreatefilemodel && m_filemodel) { + delete m_filemodel; + m_filemodel = nullptr; + } if (!m_filemodel) { m_filemodel = new FileModel(files(), directory(), this); + connect(m_filemodel, SIGNAL(checkStateChanged()), this, SLOT(slotCheckStateChanged())); - // TODO: disable downloading based on check state - // TODO: file status should not be based on global transfer status if (m_lthandle.torrent_file()) { const lt::file_storage ltstorage = m_lthandle.torrent_file()->files(); if (ltstorage.is_valid()) { for (int i = 0; i < ltstorage.num_files(); i++) { - const KUrl filepath = KUrl(ltstorage.file_path(i).c_str()); - - const Qt::CheckState filestate = Qt::Checked; - QModelIndex fileindex = m_filemodel->index(filepath, FileItem::File); + const KUrl fileurl = KUrl(ltstorage.file_path(i).c_str()); + + Job::Status filestatus = status(); + const int ltpriority = m_lthandle.file_priority(i); + // priority has no effect on finished/seeded torrents + const lt::torrent_status ltstatus = m_lthandle.status(); + if (ltstatus.state == lt::torrent_status::seeding || ltstatus.state == lt::torrent_status::finished) { + filestatus = Job::Finished; + // TODO: disable checkbox via custom file model class + } else if (ltpriority == LTPriorities::Disabled) { + filestatus = Job::Stopped; + } + + const Qt::CheckState filestate = (ltpriority == LTPriorities::Disabled ? Qt::Unchecked : Qt::Checked); + QModelIndex fileindex = m_filemodel->index(fileurl, FileItem::File); m_filemodel->setData(fileindex, filestate, Qt::CheckStateRole); - const Job::Status filestatus = status(); - QModelIndex statusindex = m_filemodel->index(filepath, FileItem::Status); + QModelIndex statusindex = m_filemodel->index(fileurl, FileItem::Status); m_filemodel->setData(statusindex, filestatus); const qlonglong filesize = ltstorage.file_size(i); - QModelIndex sizeindex = m_filemodel->index(filepath, FileItem::Size); + QModelIndex sizeindex = m_filemodel->index(fileurl, FileItem::Size); m_filemodel->setData(sizeindex, filesize); } } @@ -530,15 +554,71 @@ FileModel* TransferTorrent::fileModel() return m_filemodel; } +void TransferTorrent::slotCheckStateChanged() +{ + Q_ASSERT(m_filemodel); + + int counter = 0; + foreach (const KUrl &url, files()) { + const QModelIndex fileindex = m_filemodel->index(url, FileItem::File); + const int checkstate = m_filemodel->data(fileindex, Qt::CheckStateRole).toInt(); + if (checkstate != int(Qt::Unchecked)) { + m_lthandle.file_priority(counter, LTPriorities::NormalPriority); + kDebug(5001) << "will downloand" << url; + } else { + m_lthandle.file_priority(counter, LTPriorities::Disabled); + kDebug(5001) << "will not downloand" << url; + } + counter++; + } +} + +void TransferTorrent::save(const QDomElement &element) +{ + QDomElement elementcopy = element; + QString prioritiesstring; + const std::vector priorities = m_lthandle.file_priorities(); + for (int i = 0; i < priorities.size(); i++) { + const int priority = priorities.at(i); + if (i == 0) { + prioritiesstring.append(QString::number(priority)); + } else { + prioritiesstring.append(QString::fromLatin1(",") + QString::number(priority)); + } + } + elementcopy.setAttribute("FilePriorities", prioritiesstring); + + Transfer::save(elementcopy); +} + +void TransferTorrent::load(const QDomElement *element) +{ + Transfer::load(element); + + m_priorities.clear(); + if (element) { + const QStringList priorities = element->attribute("FilePriorities").split(","); + foreach (const QString priority, priorities) { + m_priorities.push_back(priority.toInt()); + } + } +} + void TransferTorrent::init() { // start even if transfer is finished so that torrent is seeded const bool shouldstart = (policy() != Job::Stop); if (shouldstart) { - start(); + // do it after load(), this is racy + QTimer::singleShot(2000, this, SLOT(slotDelayedStart())); } } +void TransferTorrent::slotDelayedStart() +{ + start(); +} + void TransferTorrent::timerEvent(QTimerEvent *event) { if (event->timerId() != m_timerid) { @@ -594,6 +674,9 @@ void TransferTorrent::timerEvent(QTimerEvent *event) , true ); } else if (lt::alert_cast(ltalert)) { + // see note in TransferTorrent::fileModel() + m_recreatefilemodel = true; + m_lthandle.save_resume_data(); setStatus(Job::FinishedKeepAlive); diff --git a/kget/transfer-plugins/torrent/transferTorrent.h b/kget/transfer-plugins/torrent/transferTorrent.h index 69998589..ca0a3094 100644 --- a/kget/transfer-plugins/torrent/transferTorrent.h +++ b/kget/transfer-plugins/torrent/transferTorrent.h @@ -24,6 +24,7 @@ #include +#include #include #include @@ -42,6 +43,8 @@ public: void deinit(Transfer::DeleteOptions options) final; QList files() const final; FileModel* fileModel() final; + void save(const QDomElement &element) final; + void load(const QDomElement *element) final; // Job reimplementations void start() final; @@ -55,11 +58,17 @@ protected: // Transfer reimplementation void setSpeedLimits(int uploadLimit, int downloadLimit) final; +private Q_SLOTS: + void slotDelayedStart(); + void slotCheckStateChanged(); + private: int m_timerid; lt::session* m_ltsession; lt::torrent_handle m_lthandle; FileModel* m_filemodel; + std::vector m_priorities; + bool m_recreatefilemodel; }; #endif // TRANSFER_TORRENT_H -- 2.11.0