OSDN Git Service

kget: implement partial torrent download via priority
authorIvailo Monev <xakepa10@gmail.com>
Thu, 12 Aug 2021 13:55:15 +0000 (16:55 +0300)
committerIvailo Monev <xakepa10@gmail.com>
Thu, 12 Aug 2021 13:55:15 +0000 (16:55 +0300)
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
kget/transfer-plugins/torrent/transferTorrent.cpp
kget/transfer-plugins/torrent/transferTorrent.h

index cccf720..ef05551 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "transferTorrent.h"
 
+#include <QTimer>
 #include <klocale.h>
 #include <kdebug.h>
 
 
 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<KUrl> 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<int> 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<lt::torrent_finished_alert>(ltalert)) {
+            // see note in TransferTorrent::fileModel()
+            m_recreatefilemodel = true;
+
             m_lthandle.save_resume_data();
 
             setStatus(Job::FinishedKeepAlive);
index 6999858..ca0a309 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <QTimerEvent>
 
+#include <vector>
 #include <libtorrent/session.hpp>
 #include <libtorrent/torrent_handle.hpp>
 
@@ -42,6 +43,8 @@ public:
     void deinit(Transfer::DeleteOptions options) final;
     QList<KUrl> 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<int> m_priorities;
+    bool m_recreatefilemodel;
 };
 
 #endif // TRANSFER_TORRENT_H