From 82e41f36ee56c6ff076fc6e0c5f30b8ae8383849 Mon Sep 17 00:00:00 2001 From: Christophe Dumez Date: Mon, 7 Mar 2011 19:26:44 +0000 Subject: [PATCH] FEATURE: Added per-torrent ratio limiting (Christian Kandeler) --- Changelog | 1 + src/Icons/skin/ratio.png | Bin 0 -> 483 bytes src/icons.qrc | 1 + src/lang.qrc | 4 +- src/preferences/options_imp.cpp | 4 +- src/preferences/preferences.h | 4 +- src/qtlibtorrent/qbtsession.cpp | 84 +++++++++++++++----- src/qtlibtorrent/qbtsession.h | 9 ++- src/src.pro | 7 +- src/torrentpersistentdata.h | 33 ++++++++ src/transferlistwidget.cpp | 27 +++++++ src/transferlistwidget.h | 1 + src/updownratiodlg.cpp | 45 +++++++++++ src/updownratiodlg.h | 31 ++++++++ src/updownratiodlg.ui | 137 ++++++++++++++++++++++++++++++++ 15 files changed, 356 insertions(+), 32 deletions(-) create mode 100644 src/Icons/skin/ratio.png create mode 100644 src/updownratiodlg.cpp create mode 100644 src/updownratiodlg.h create mode 100644 src/updownratiodlg.ui diff --git a/Changelog b/Changelog index 470c29c23..3567c7fdb 100644 --- a/Changelog +++ b/Changelog @@ -6,6 +6,7 @@ - FEATURE: Inhibit system sleep when torrents are active (Vladimir Golovnev) - FEATURE: Added option to bypass Web UI authentication for localhost - FEATURE: Added option to disable program exit confirmation + - FEATURE: Added per-torrent ratio limiting (Christian Kandeler) - BUGFIX: Fix compilation with namespaced Qt (Christian Kandeler) * Sun Jan 9 2011 - Christophe Dumez - v2.6.0 diff --git a/src/Icons/skin/ratio.png b/src/Icons/skin/ratio.png new file mode 100644 index 0000000000000000000000000000000000000000..40d00c42d8d2db3d9429194bd24be55a5edaac24 GIT binary patch literal 483 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPgg)W_BSZGszDNnt-Mxd%8G=Se#xu z+1}eJQRG%+@f&UyN5{##Ei7Z4vp8DzH8?w;H4P2% zb`6eh37OEDQ8+QXM9SoUsqjf={yCrbo&PLzued_g@M_|N*8i^Sch5S~keIL_YCq#^ zpLLIcons/skin/filteractive.png Icons/skin/bg-handle-horizontal.gif Icons/skin/download.png + Icons/skin/ratio.png Icons/flags/sm.png Icons/flags/lt.png Icons/flags/th.png diff --git a/src/lang.qrc b/src/lang.qrc index 715835ec1..44d40d0da 100644 --- a/src/lang.qrc +++ b/src/lang.qrc @@ -15,6 +15,7 @@ lang/qbittorrent_fr.qm lang/qbittorrent_uk.qm lang/qbittorrent_zh.qm + lang/qbittorrent_lt.qm lang/qbittorrent_ko.qm lang/qbittorrent_nb.qm lang/qbittorrent_sv.qm @@ -32,6 +33,5 @@ lang/qbittorrent_en.qm lang/qbittorrent_hr.qm lang/qbittorrent_ro.qm - lang/qbittorrent_lt.qm - + \ No newline at end of file diff --git a/src/preferences/options_imp.cpp b/src/preferences/options_imp.cpp index a15169773..6d747ee6a 100644 --- a/src/preferences/options_imp.cpp +++ b/src/preferences/options_imp.cpp @@ -391,7 +391,7 @@ void options_imp::saveOptions(){ pref.setDHTPort(getDHTPort()); pref.setLSDEnabled(isLSDEnabled()); pref.setEncryptionSetting(getEncryptionSetting()); - pref.setMaxRatio(getMaxRatio()); + pref.setGlobalMaxRatio(getMaxRatio()); pref.setMaxRatioAction(comboRatioLimitAct->currentIndex()); // End Bittorrent preferences // Misc preferences @@ -624,7 +624,7 @@ void options_imp::loadOptions(){ checkLSD->setChecked(pref.isLSDEnabled()); comboEncryption->setCurrentIndex(pref.getEncryptionSetting()); // Ratio limit - floatValue = pref.getMaxRatio(); + floatValue = pref.getGlobalMaxRatio(); if(floatValue >= 0.) { // Enable checkMaxRatio->setChecked(true); diff --git a/src/preferences/preferences.h b/src/preferences/preferences.h index 3f86e7c15..6aeaee5f5 100644 --- a/src/preferences/preferences.h +++ b/src/preferences/preferences.h @@ -547,11 +547,11 @@ public: setValue(QString::fromUtf8("Preferences/Bittorrent/Encryption"), val); } - qreal getMaxRatio() const { + qreal getGlobalMaxRatio() const { return value(QString::fromUtf8("Preferences/Bittorrent/MaxRatio"), -1).toDouble(); } - void setMaxRatio(qreal ratio) { + void setGlobalMaxRatio(qreal ratio) { setValue(QString::fromUtf8("Preferences/Bittorrent/MaxRatio"), ratio); } diff --git a/src/qtlibtorrent/qbtsession.cpp b/src/qtlibtorrent/qbtsession.cpp index f17605cef..e1609390e 100644 --- a/src/qtlibtorrent/qbtsession.cpp +++ b/src/qtlibtorrent/qbtsession.cpp @@ -88,7 +88,7 @@ enum VersionType { NORMAL,ALPHA,BETA,RELEASE_CANDIDATE,DEVEL }; // Main constructor QBtSession::QBtSession() : m_scanFolders(ScanFoldersModel::instance(this)), - preAllocateAll(false), addInPause(false), ratio_limit(-1), + preAllocateAll(false), addInPause(false), global_ratio_limit(-1), UPnPEnabled(false), LSDEnabled(false), DHTEnabled(false), current_dht_port(0), queueingEnabled(false), torrentExport(false) @@ -97,6 +97,9 @@ QBtSession::QBtSession() #endif , m_tracker(0) { + BigRatioTimer = new QTimer(this); + BigRatioTimer->setInterval(10000); + connect(BigRatioTimer, SIGNAL(timeout()), SLOT(processBigRatios())); Preferences pref; // To avoid some exceptions boost::filesystem::path::default_name_check(boost::filesystem::no_check); @@ -130,19 +133,19 @@ QBtSession::QBtSession() } s->add_extension(&create_smart_ban_plugin); timerAlerts = new QTimer(this); - connect(timerAlerts, SIGNAL(timeout()), this, SLOT(readAlerts())); + connect(timerAlerts, SIGNAL(timeout()), SLOT(readAlerts())); timerAlerts->start(3000); - connect(&resumeDataTimer, SIGNAL(timeout()), this, SLOT(saveTempFastResumeData())); + connect(&resumeDataTimer, SIGNAL(timeout()), SLOT(saveTempFastResumeData())); resumeDataTimer.start(180000); // 3min // To download from urls downloader = new DownloadThread(this); - connect(downloader, SIGNAL(downloadFinished(QString, QString)), this, SLOT(processDownloadedFile(QString, QString))); - connect(downloader, SIGNAL(downloadFailure(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString))); + connect(downloader, SIGNAL(downloadFinished(QString, QString)), SLOT(processDownloadedFile(QString, QString))); + connect(downloader, SIGNAL(downloadFailure(QString, QString)), SLOT(handleDownloadFailure(QString, QString))); appendLabelToSavePath = pref.appendTorrentLabel(); #if LIBTORRENT_VERSION_MINOR > 14 appendqBExtension = pref.useIncompleteFilesExtension(); #endif - connect(m_scanFolders, SIGNAL(torrentsAdded(QStringList&)), this, SLOT(addTorrentsFromScanFolder(QStringList&))); + connect(m_scanFolders, SIGNAL(torrentsAdded(QStringList&)), SLOT(addTorrentsFromScanFolder(QStringList&))); // Apply user settings to Bittorrent session configureSession(); // Torrent speed monitor @@ -190,7 +193,6 @@ void QBtSession::preAllocateAllFiles(bool b) { } void QBtSession::processBigRatios() { - if(ratio_limit < 0) return; qDebug("Process big ratios..."); std::vector torrents = s->get_torrents(); std::vector::iterator torrentIT; @@ -200,7 +202,13 @@ void QBtSession::processBigRatios() { if(h.is_seed()) { const QString hash = h.hash(); const qreal ratio = getRealRatio(hash); + qreal ratio_limit = TorrentPersistentData::getRatioLimit(hash); + if(ratio_limit == TorrentPersistentData::NO_RATIO_LIMIT) + continue; + if(ratio_limit == TorrentPersistentData::USE_GLOBAL_RATIO) + ratio_limit = global_ratio_limit; qDebug("Ratio: %f (limit: %f)", ratio, ratio_limit); + Q_ASSERT(ratio_limit >= 0.f); if(ratio <= MAX_RATIO && ratio >= ratio_limit) { if(high_ratio_action == REMOVE_ACTION) { addConsoleMessage(tr("%1 reached the maximum ratio you set.").arg(h.name())); @@ -502,7 +510,8 @@ void QBtSession::configureSession() { applyEncryptionSettings(encryptionSettings); // * Maximum ratio high_ratio_action = pref.getMaxRatioAction(); - setMaxRatio(pref.getMaxRatio()); + setGlobalMaxRatio(pref.getGlobalMaxRatio()); + updateRatioTimer(); // Ip Filter FilterParserThread::processFilterList(s, pref.bannedIPs()); if(pref.isFilteringEnabled()) { @@ -1774,22 +1783,53 @@ void QBtSession::setUploadRateLimit(long rate) { // Torrents will a ratio superior to the given value will // be automatically deleted -void QBtSession::setMaxRatio(qreal ratio) { +void QBtSession::setGlobalMaxRatio(qreal ratio) { if(ratio < 0) ratio = -1.; - if(ratio_limit == -1 && ratio != -1) { - Q_ASSERT(!BigRatioTimer); - BigRatioTimer = new QTimer(this); - connect(BigRatioTimer, SIGNAL(timeout()), this, SLOT(processBigRatios())); - BigRatioTimer->start(10000); - } else { - if(ratio_limit != -1 && ratio == -1) { - delete BigRatioTimer; - } + if(global_ratio_limit != ratio) { + global_ratio_limit = ratio; + qDebug("* Set global deleteRatio to %.1f", global_ratio_limit); + updateRatioTimer(); } - if(ratio_limit != ratio) { - ratio_limit = ratio; - qDebug("* Set deleteRatio to %.1f", ratio_limit); - processBigRatios(); +} + +void QBtSession::setMaxRatioPerTorrent(const QString &hash, qreal ratio) +{ + if (ratio < 0) + ratio = -1; + if (ratio > MAX_RATIO) + ratio = MAX_RATIO; + qDebug("* Set individual max ratio for torrent %s to %.1f.", + qPrintable(hash), ratio); + TorrentPersistentData::setRatioLimit(hash, ratio); + updateRatioTimer(); +} + +void QBtSession::removeRatioPerTorrent(const QString &hash) +{ + qDebug("* Remove individual max ratio for torrent %s.", qPrintable(hash)); + TorrentPersistentData::setRatioLimit(hash, TorrentPersistentData::USE_GLOBAL_RATIO); + updateRatioTimer(); +} + +qreal QBtSession::getMaxRatioPerTorrent(const QString &hash, bool *usesGlobalRatio) const +{ + qreal ratio_limit = TorrentPersistentData::getRatioLimit(hash); + if(ratio_limit == TorrentPersistentData::USE_GLOBAL_RATIO) { + ratio_limit = global_ratio_limit; + *usesGlobalRatio = true; + } else { + *usesGlobalRatio = false; + } + return ratio_limit; +} + +void QBtSession::updateRatioTimer() +{ + if (global_ratio_limit == -1 && !TorrentPersistentData::hasPerTorrentRatioLimit()) { + if (BigRatioTimer->isActive()) + BigRatioTimer->stop(); + } else if (!BigRatioTimer->isActive()) { + BigRatioTimer->start(); } } diff --git a/src/qtlibtorrent/qbtsession.h b/src/qtlibtorrent/qbtsession.h index 6f32b3c8c..9f0ab9294 100644 --- a/src/qtlibtorrent/qbtsession.h +++ b/src/qtlibtorrent/qbtsession.h @@ -129,7 +129,11 @@ public slots: void setMaxUploadsPerTorrent(int max); void setDownloadRateLimit(long rate); void setUploadRateLimit(long rate); - void setMaxRatio(qreal ratio); + void setGlobalMaxRatio(qreal ratio); + qreal getGlobalMaxRatio() const { return global_ratio_limit; } + void setMaxRatioPerTorrent(const QString &hash, qreal ratio); + qreal getMaxRatioPerTorrent(const QString &hash, bool *usesGlobalRatio) const; + void removeRatioPerTorrent(const QString &hash); void setDHTPort(int dht_port); void setProxySettings(const libtorrent::proxy_settings &proxySettings); void setSessionSettings(const libtorrent::session_settings &sessionSettings); @@ -168,6 +172,7 @@ private: void loadTorrentTempData(QTorrentHandle &h, QString savePath, bool magnet); libtorrent::add_torrent_params initializeAddTorrentParams(const QString &hash); libtorrent::entry generateFilePriorityResumeData(boost::intrusive_ptr &t, const std::vector &fp); + void updateRatioTimer(); private slots: void addTorrentsFromScanFolder(QStringList&); @@ -233,7 +238,7 @@ private: // Settings bool preAllocateAll; bool addInPause; - qreal ratio_limit; + qreal global_ratio_limit; int high_ratio_action; bool UPnPEnabled; bool LSDEnabled; diff --git a/src/src.pro b/src/src.pro index 58df1c96f..5cc6aa507 100644 --- a/src/src.pro +++ b/src/src.pro @@ -101,13 +101,15 @@ HEADERS += misc.h \ filesystemwatcher.h \ scannedfoldersmodel.h \ qinisettings.h \ - smtp.h + smtp.h \ + updownratiodlg.h SOURCES += main.cpp \ downloadthread.cpp \ scannedfoldersmodel.cpp \ misc.cpp \ - smtp.cpp + smtp.cpp \ + updownratiodlg.cpp nox { HEADERS += headlessloader.h @@ -164,6 +166,7 @@ nox { downloadfromurldlg.ui \ torrentadditiondlg.ui \ bandwidth_limit.ui \ + updownratiodlg.ui \ confirmdeletiondlg.ui \ torrentimportdlg.ui \ executionlog.ui diff --git a/src/torrentpersistentdata.h b/src/torrentpersistentdata.h index 2f7588204..7a5909aa0 100644 --- a/src/torrentpersistentdata.h +++ b/src/torrentpersistentdata.h @@ -171,7 +171,12 @@ public: class TorrentPersistentData { public: + enum RatioLimit { + USE_GLOBAL_RATIO = -2, + NO_RATIO_LIMIT = -1 + }; +public: static bool isKnownTorrent(QString hash) { QIniSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent-resume")); const QHash all_data = settings.value("torrents").toHash(); @@ -184,6 +189,34 @@ public: return all_data.keys(); } + static void setRatioLimit(const QString &hash, qreal ratio) { + QIniSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent-resume")); + QHash all_data = settings.value("torrents").toHash(); + QHash data = all_data.value(hash).toHash(); + data["max_ratio"] = ratio; + all_data[hash] = data; + settings.setValue("torrents", all_data); + } + + static qreal getRatioLimit(const QString &hash) { + QIniSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent-resume")); + const QHash all_data = settings.value("torrents").toHash(); + const QHash data = all_data.value(hash).toHash(); + return data.value("max_ratio", USE_GLOBAL_RATIO).toReal(); + } + + static bool hasPerTorrentRatioLimit() { + QIniSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent-resume")); + const QHash all_data = settings.value("torrents").toHash(); + QHash::ConstIterator it; + for(it = all_data.constBegin(); it != all_data.constEnd(); it++) { + if(it.value().toHash().value("max_ratio", USE_GLOBAL_RATIO).toReal() >= 0) { + return true; + } + } + return false; + } + static void setAddedDate(QString hash) { QIniSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent-resume")); QHash all_data = settings.value("torrents").toHash(); diff --git a/src/transferlistwidget.cpp b/src/transferlistwidget.cpp index 5790f489e..7161c5b32 100644 --- a/src/transferlistwidget.cpp +++ b/src/transferlistwidget.cpp @@ -51,6 +51,7 @@ #include "transferlistdelegate.h" #include "previewselect.h" #include "speedlimitdlg.h" +#include "updownratiodlg.h" #include "options_imp.h" #include "mainwindow.h" #include "preferences.h" @@ -482,6 +483,29 @@ void TransferListWidget::setUpLimitSelectedTorrents() { } } +void TransferListWidget::setMaxRatioSelectedTorrents() { + const QStringList hashes = getSelectedTorrentsHashes(); + if (hashes.isEmpty()) + return; + bool useGlobalValue; + qreal currentMaxRatio; + if (hashes.count() == 1) { + currentMaxRatio = BTSession->getMaxRatioPerTorrent(hashes.first(), &useGlobalValue); + } else { + useGlobalValue = true; + currentMaxRatio = BTSession->getGlobalMaxRatio(); + } + UpDownRatioDlg dlg(useGlobalValue, currentMaxRatio, QBtSession::MAX_RATIO, this); + if (dlg.exec() != QDialog::Accepted) + return; + foreach (const QString &hash, hashes) { + if (dlg.useDefault()) + BTSession->removeRatioPerTorrent(hash); + else + BTSession->setMaxRatioPerTorrent(hash, dlg.ratio()); + } +} + void TransferListWidget::recheckSelectedTorrents() { const QStringList hashes = getSelectedTorrentsHashes(); foreach(const QString &hash, hashes) { @@ -620,6 +644,8 @@ void TransferListWidget::displayListMenu(const QPoint&) { connect(&actionDelete, SIGNAL(triggered()), this, SLOT(deleteSelectedTorrents())); QAction actionPreview_file(IconProvider::instance()->getIcon("view-preview"), tr("Preview file..."), 0); connect(&actionPreview_file, SIGNAL(triggered()), this, SLOT(previewSelectedTorrents())); + QAction actionSet_max_ratio(QIcon(QString::fromUtf8(":/Icons/skin/ratio.png")), tr("Limit share ratio..."), 0); + connect(&actionSet_max_ratio, SIGNAL(triggered()), this, SLOT(setMaxRatioSelectedTorrents())); QAction actionSet_upload_limit(QIcon(QString::fromUtf8(":/Icons/skin/seeding.png")), tr("Limit upload rate..."), 0); connect(&actionSet_upload_limit, SIGNAL(triggered()), this, SLOT(setUpLimitSelectedTorrents())); QAction actionSet_download_limit(QIcon(QString::fromUtf8(":/Icons/skin/download.png")), tr("Limit download rate..."), 0); @@ -742,6 +768,7 @@ void TransferListWidget::displayListMenu(const QPoint&) { listMenu.addSeparator(); if(one_not_seed) listMenu.addAction(&actionSet_download_limit); + listMenu.addAction(&actionSet_max_ratio); listMenu.addAction(&actionSet_upload_limit); #if LIBTORRENT_VERSION_MINOR > 14 if(!one_not_seed && all_same_super_seeding && one_has_metadata) { diff --git a/src/transferlistwidget.h b/src/transferlistwidget.h index 80f55edbd..f2d672318 100644 --- a/src/transferlistwidget.h +++ b/src/transferlistwidget.h @@ -74,6 +74,7 @@ public slots: void recheckSelectedTorrents(); void setDlLimitSelectedTorrents(); void setUpLimitSelectedTorrents(); + void setMaxRatioSelectedTorrents(); void previewSelectedTorrents(); void hidePriorityColumn(bool hide); void displayDLHoSMenu(const QPoint&); diff --git a/src/updownratiodlg.cpp b/src/updownratiodlg.cpp new file mode 100644 index 000000000..3cf97bdf3 --- /dev/null +++ b/src/updownratiodlg.cpp @@ -0,0 +1,45 @@ +#include "updownratiodlg.h" +#include "ui_updownratiodlg.h" + +#include "preferences.h" + +UpDownRatioDlg::UpDownRatioDlg(bool useDefault, qreal initialValue, + qreal maxValue, QWidget *parent) + : QDialog(parent), ui(new Ui::UpDownRatioDlg) +{ + ui->setupUi(this); + if (useDefault) { + ui->useDefaultButton->setChecked(true); + } else if (initialValue == -1) { + ui->noLimitButton->setChecked(true); + initialValue = Preferences().getGlobalMaxRatio(); + } else { + ui->torrentLimitButton->setChecked(true); + } + ui->ratioSpinBox->setMinimum(0); + ui->ratioSpinBox->setMaximum(maxValue); + ui->ratioSpinBox->setValue(initialValue); + connect(ui->buttonGroup, SIGNAL(buttonClicked(int)), + SLOT(handleRatioTypeChanged())); + handleRatioTypeChanged(); +} + +bool UpDownRatioDlg::useDefault() const +{ + return ui->useDefaultButton->isChecked(); +} + +qreal UpDownRatioDlg::ratio() const +{ + return ui->noLimitButton->isChecked() ? -1 : ui->ratioSpinBox->value(); +} + +void UpDownRatioDlg::handleRatioTypeChanged() +{ + ui->ratioSpinBox->setEnabled(ui->torrentLimitButton->isChecked()); +} + +UpDownRatioDlg::~UpDownRatioDlg() +{ + delete ui; +} diff --git a/src/updownratiodlg.h b/src/updownratiodlg.h new file mode 100644 index 000000000..16227493c --- /dev/null +++ b/src/updownratiodlg.h @@ -0,0 +1,31 @@ +#ifndef UPDOWNRATIODLG_H +#define UPDOWNRATIODLG_H + +#include + +QT_BEGIN_NAMESPACE +namespace Ui { + class UpDownRatioDlg; +} +QT_END_NAMESPACE + +class UpDownRatioDlg : public QDialog +{ + Q_OBJECT + +public: + explicit UpDownRatioDlg(bool useDefault, qreal initialValue, qreal maxValue, + QWidget *parent = 0); + ~UpDownRatioDlg(); + + bool useDefault() const; + qreal ratio() const; + +private slots: + void handleRatioTypeChanged(); + +private: + Ui::UpDownRatioDlg *ui; +}; + +#endif // UPDOWNRATIODLG_H diff --git a/src/updownratiodlg.ui b/src/updownratiodlg.ui new file mode 100644 index 000000000..8cf822a3a --- /dev/null +++ b/src/updownratiodlg.ui @@ -0,0 +1,137 @@ + + + UpDownRatioDlg + + + + 0 + 0 + 317 + 152 + + + + Torrent Upload/Download Ratio Limiting + + + + + + Use global ratio limit + + + buttonGroup + + + + + + + Set no ratio limit + + + buttonGroup + + + + + + + + + Set ratio limit to + + + buttonGroup + + + + + + + 1 + + + 0.100000000000000 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + UpDownRatioDlg + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + UpDownRatioDlg + reject() + + + 316 + 260 + + + 286 + 274 + + + + + + + +