diff --git a/src/base/bittorrent/addtorrentparams.h b/src/base/bittorrent/addtorrentparams.h index 82882fa13..4427bc7fc 100644 --- a/src/base/bittorrent/addtorrentparams.h +++ b/src/base/bittorrent/addtorrentparams.h @@ -46,7 +46,7 @@ namespace BitTorrent TriStateBool addForced; TriStateBool addPaused; QVector filePriorities; // used if TorrentInfo is set - bool ignoreShareRatio = false; + bool ignoreShareLimits = false; bool skipChecking = false; TriStateBool createSubfolder; }; diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index 6620667b9..02d361a82 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -235,6 +235,7 @@ Session::Session(QObject *parent) , m_isAddTrackersEnabled(BITTORRENT_SESSION_KEY("AddTrackersEnabled"), false) , m_additionalTrackers(BITTORRENT_SESSION_KEY("AdditionalTrackers")) , m_globalMaxRatio(BITTORRENT_SESSION_KEY("GlobalMaxRatio"), -1, [](qreal r) { return r < 0 ? -1. : r;}) + , m_globalMaxSeedingMinutes(BITTORRENT_SESSION_KEY("GlobalMaxSeedingMinutes"), -1, lowerLimited(-1)) , m_isAddTorrentPaused(BITTORRENT_SESSION_KEY("AddTorrentPaused"), false) , m_isCreateTorrentSubfolder(BITTORRENT_SESSION_KEY("CreateTorrentSubfolder"), true) , m_isAppendExtensionEnabled(BITTORRENT_SESSION_KEY("AddExtensionToIncompleteFiles"), false) @@ -287,9 +288,9 @@ Session::Session(QObject *parent) initResumeFolder(); - m_bigRatioTimer = new QTimer(this); - m_bigRatioTimer->setInterval(10000); - connect(m_bigRatioTimer, SIGNAL(timeout()), SLOT(processBigRatios())); + m_seedingLimitTimer = new QTimer(this); + m_seedingLimitTimer->setInterval(10000); + connect(m_seedingLimitTimer, SIGNAL(timeout()), SLOT(processShareLimits())); // Set severity level of libtorrent session int alertMask = libt::alert::error_notification @@ -411,7 +412,7 @@ Session::Session(QObject *parent) m_statistics = new Statistics(this); - updateRatioTimer(); + updateSeedingLimitTimer(); populateAdditionalTrackers(); enableTracker(isTrackerEnabled()); @@ -800,7 +801,23 @@ void Session::setGlobalMaxRatio(qreal ratio) if (ratio != globalMaxRatio()) { m_globalMaxRatio = ratio; - updateRatioTimer(); + updateSeedingLimitTimer(); + } +} + +int Session::globalMaxSeedingMinutes() const +{ + return m_globalMaxSeedingMinutes; +} + +void Session::setGlobalMaxSeedingMinutes(int minutes) +{ + if (minutes < 0) + minutes = -1; + + if (minutes != globalMaxSeedingMinutes()) { + m_globalMaxSeedingMinutes = minutes; + updateSeedingLimitTimer(); } } @@ -1415,36 +1432,56 @@ void Session::populateAdditionalTrackers() } } -void Session::processBigRatios() +void Session::processShareLimits() { - qDebug("Process big ratios..."); + qDebug("Processing share limits..."); - qreal globalMaxRatio = this->globalMaxRatio(); foreach (TorrentHandle *const torrent, m_torrents) { - if (torrent->isSeed() - && (torrent->ratioLimit() != TorrentHandle::NO_RATIO_LIMIT) - && !torrent->isForced()) { - const qreal ratio = torrent->realRatio(); - qreal ratioLimit = torrent->ratioLimit(); - if (ratioLimit == TorrentHandle::USE_GLOBAL_RATIO) { - // If Global Max Ratio is really set... - ratioLimit = globalMaxRatio; - if (ratioLimit < 0) continue; - } - qDebug("Ratio: %f (limit: %f)", ratio, ratioLimit); - Q_ASSERT(ratioLimit >= 0.f); + if (torrent->isSeed() && !torrent->isForced()) { + if (torrent->ratioLimit() != TorrentHandle::NO_RATIO_LIMIT) { + const qreal ratio = torrent->realRatio(); + qreal ratioLimit = torrent->ratioLimit(); + if (ratioLimit == TorrentHandle::USE_GLOBAL_RATIO) + // If Global Max Ratio is really set... + ratioLimit = globalMaxRatio(); - if ((ratio <= TorrentHandle::MAX_RATIO) && (ratio >= ratioLimit)) { - Logger* const logger = Logger::instance(); - if (maxRatioAction() == Remove) { - logger->addMessage(tr("'%1' reached the maximum ratio you set. Removing...").arg(torrent->name())); - deleteTorrent(torrent->hash()); + if (ratioLimit >= 0) { + qDebug("Ratio: %f (limit: %f)", ratio, ratioLimit); + + if ((ratio <= TorrentHandle::MAX_RATIO) && (ratio >= ratioLimit)) { + Logger* const logger = Logger::instance(); + if (m_maxRatioAction == Remove) { + deleteTorrent(torrent->hash()); + logger->addMessage(tr("'%1' reached the maximum ratio you set. Removed.").arg(torrent->name())); + } + else if (!torrent->isPaused()) { + torrent->pause(); + logger->addMessage(tr("'%1' reached the maximum ratio you set. Paused.").arg(torrent->name())); + } + } } - else { - // Pause it - if (!torrent->isPaused()) { - logger->addMessage(tr("'%1' reached the maximum ratio you set. Pausing...").arg(torrent->name())); - torrent->pause(); + } + + if (torrent->seedingTimeLimit() != TorrentHandle::NO_SEEDING_TIME_LIMIT) { + const int seedingTimeInMinutes = torrent->seedingTime() / 60; + int seedingTimeLimit = torrent->seedingTimeLimit(); + if (seedingTimeLimit == TorrentHandle::USE_GLOBAL_SEEDING_TIME) + // If Global Seeding Time Limit is really set... + seedingTimeLimit = globalMaxSeedingMinutes(); + + if (seedingTimeLimit >= 0) { + qDebug("Seeding Time: %d (limit: %d)", seedingTimeInMinutes, seedingTimeLimit); + + if ((seedingTimeInMinutes <= TorrentHandle::MAX_SEEDING_TIME) && (seedingTimeInMinutes >= seedingTimeLimit)) { + Logger* const logger = Logger::instance(); + if (m_maxRatioAction == Remove) { + deleteTorrent(torrent->hash()); + logger->addMessage(tr("'%1' reached the maximum seeding time you set. Removed.").arg(torrent->name())); + } + else if (!torrent->isPaused()) { + torrent->pause(); + logger->addMessage(tr("'%1' reached the maximum seeding time you set. Paused.").arg(torrent->name())); + } } } } @@ -2926,21 +2963,22 @@ bool Session::isKnownTorrent(const InfoHash &hash) const || m_loadedMetadata.contains(hash)); } -void Session::updateRatioTimer() +void Session::updateSeedingLimitTimer() { - if ((globalMaxRatio() == -1) && !hasPerTorrentRatioLimit()) { - if (m_bigRatioTimer->isActive()) - m_bigRatioTimer->stop(); + if ((globalMaxRatio() == -1) && !hasPerTorrentRatioLimit() + && (globalMaxSeedingMinutes() == TorrentHandle::NO_SEEDING_TIME_LIMIT) && !hasPerTorrentSeedingTimeLimit()) { + if (m_seedingLimitTimer->isActive()) + m_seedingLimitTimer->stop(); } - else if (!m_bigRatioTimer->isActive()) { - m_bigRatioTimer->start(); + else if (!m_seedingLimitTimer->isActive()) { + m_seedingLimitTimer->start(); } } -void Session::handleTorrentRatioLimitChanged(TorrentHandle *const torrent) +void Session::handleTorrentShareLimitChanged(TorrentHandle *const torrent) { Q_UNUSED(torrent); - updateRatioTimer(); + updateSeedingLimitTimer(); } void Session::saveTorrentResumeData(TorrentHandle *const torrent, bool finalSave) @@ -3119,6 +3157,14 @@ bool Session::hasPerTorrentRatioLimit() const return false; } +bool Session::hasPerTorrentSeedingTimeLimit() const +{ + foreach (TorrentHandle *const torrent, m_torrents) + if (torrent->seedingTimeLimit() >= 0) return true; + + return false; +} + void Session::initResumeFolder() { m_resumeFolderPath = Utils::Fs::expandPathAbs(specialFolderLocation(SpecialFolder::Data) + RESUME_FOLDER); @@ -3528,8 +3574,9 @@ void Session::createTorrentHandle(const libt::torrent_handle &nativeHandle) saveTorrentResumeData(torrent); } - if ((torrent->ratioLimit() >= 0) && !m_bigRatioTimer->isActive()) - m_bigRatioTimer->start(); + if (((torrent->ratioLimit() >= 0) || (torrent->seedingTimeLimit() >= 0)) + && !m_seedingLimitTimer->isActive()) + m_seedingLimitTimer->start(); // Send torrent addition signal emit torrentAdded(torrent); @@ -3875,6 +3922,7 @@ namespace torrentData.savePath = Profile::instance().fromPortablePath( Utils::Fs::fromNativePath(QString::fromStdString(fast.dict_find_string_value("qBt-savePath")))); torrentData.ratioLimit = QString::fromStdString(fast.dict_find_string_value("qBt-ratioLimit")).toDouble(); + torrentData.seedingTimeLimit = fast.dict_find_int_value("qBt-seedingTimeLimit", TorrentHandle::USE_GLOBAL_SEEDING_TIME); // ************************************************************************************** // Workaround to convert legacy label to category // TODO: Should be removed in future diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h index 63609ca55..eda074d31 100644 --- a/src/base/bittorrent/session.h +++ b/src/base/bittorrent/session.h @@ -245,6 +245,8 @@ namespace BitTorrent qreal globalMaxRatio() const; void setGlobalMaxRatio(qreal ratio); + int globalMaxSeedingMinutes() const; + void setGlobalMaxSeedingMinutes(int minutes); bool isDHTEnabled() const; void setDHTEnabled(bool enabled); bool isLSDEnabled() const; @@ -395,7 +397,7 @@ namespace BitTorrent void bottomTorrentsPriority(const QStringList &hashes); // TorrentHandle interface - void handleTorrentRatioLimitChanged(TorrentHandle *const torrent); + void handleTorrentShareLimitChanged(TorrentHandle *const torrent); void handleTorrentSavePathChanged(TorrentHandle *const torrent); void handleTorrentCategoryChanged(TorrentHandle *const torrent, const QString &oldCategory); void handleTorrentSavingModeChanged(TorrentHandle *const torrent); @@ -455,7 +457,7 @@ namespace BitTorrent void configureDeferred(); void readAlerts(); void refresh(); - void processBigRatios(); + void processShareLimits(); void generateResumeData(bool final = false); void handleIPFilterParsed(int ruleCount); void handleIPFilterError(); @@ -473,6 +475,7 @@ namespace BitTorrent ~Session(); bool hasPerTorrentRatioLimit() const; + bool hasPerTorrentSeedingTimeLimit() const; void initResumeFolder(); @@ -503,7 +506,7 @@ namespace BitTorrent const QByteArray &fastresumeData = QByteArray()); bool findIncompleteFiles(TorrentInfo &torrentInfo, QString &savePath) const; - void updateRatioTimer(); + void updateSeedingLimitTimer(); void exportTorrentFile(TorrentHandle *const torrent, TorrentExportFolder folder = TorrentExportFolder::Regular); void saveTorrentResumeData(TorrentHandle *const torrent, bool finalSave = false); @@ -578,6 +581,7 @@ namespace BitTorrent CachedSettingValue m_isAddTrackersEnabled; CachedSettingValue m_additionalTrackers; CachedSettingValue m_globalMaxRatio; + CachedSettingValue m_globalMaxSeedingMinutes; CachedSettingValue m_isAddTorrentPaused; CachedSettingValue m_isCreateTorrentSubfolder; CachedSettingValue m_isAppendExtensionEnabled; @@ -628,7 +632,7 @@ namespace BitTorrent bool m_useProxy; QTimer *m_refreshTimer; - QTimer *m_bigRatioTimer; + QTimer *m_seedingLimitTimer; QTimer *m_resumeDataTimer; Statistics *m_statistics; // IP filtering diff --git a/src/base/bittorrent/torrenthandle.cpp b/src/base/bittorrent/torrenthandle.cpp index bb3ea5bce..6214aeb58 100644 --- a/src/base/bittorrent/torrenthandle.cpp +++ b/src/base/bittorrent/torrenthandle.cpp @@ -81,6 +81,7 @@ AddTorrentData::AddTorrentData() , addForced(false) , addPaused(false) , ratioLimit(TorrentHandle::USE_GLOBAL_RATIO) + , seedingTimeLimit(TorrentHandle::USE_GLOBAL_SEEDING_TIME) { } @@ -102,7 +103,8 @@ AddTorrentData::AddTorrentData(const AddTorrentParams ¶ms) ? Session::instance()->isAddTorrentPaused() : params.addPaused == TriStateBool::True) , filePriorities(params.filePriorities) - , ratioLimit(params.ignoreShareRatio ? TorrentHandle::NO_RATIO_LIMIT : TorrentHandle::USE_GLOBAL_RATIO) + , ratioLimit(params.ignoreShareLimits ? TorrentHandle::NO_RATIO_LIMIT : TorrentHandle::USE_GLOBAL_RATIO) + , seedingTimeLimit(params.ignoreShareLimits ? TorrentHandle::NO_SEEDING_TIME_LIMIT : TorrentHandle::USE_GLOBAL_SEEDING_TIME) { } @@ -169,7 +171,11 @@ TorrentState::operator int() const const qreal TorrentHandle::USE_GLOBAL_RATIO = -2.; const qreal TorrentHandle::NO_RATIO_LIMIT = -1.; +const int TorrentHandle::USE_GLOBAL_SEEDING_TIME = -2; +const int TorrentHandle::NO_SEEDING_TIME_LIMIT = -1; + const qreal TorrentHandle::MAX_RATIO = 9999.; +const int TorrentHandle::MAX_SEEDING_TIME = 525600; // The new libtorrent::create_torrent constructor appeared after 1.0.11 in RC_1_0 // and after 1.1.1 in RC_1_1. Since it fixed an ABI incompatibility with previous versions @@ -209,6 +215,7 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle , m_category(data.category) , m_hasSeedStatus(data.hasSeedStatus) , m_ratioLimit(data.ratioLimit) + , m_seedingTimeLimit(data.seedingTimeLimit) , m_tempPathDisabled(data.disableTempPath) , m_hasMissingFiles(false) , m_hasRootFolder(data.hasRootFolder) @@ -581,6 +588,11 @@ qreal TorrentHandle::ratioLimit() const return m_ratioLimit; } +int TorrentHandle::seedingTimeLimit() const +{ + return m_seedingTimeLimit; +} + QString TorrentHandle::filePath(int index) const { return m_torrentInfo.filePath(index); @@ -907,24 +919,38 @@ qulonglong TorrentHandle::eta() const { if (isPaused()) return MAX_ETA; - const SpeedSampleAvg speed_average = m_speedMonitor.average(); + const SpeedSampleAvg speedAverage = m_speedMonitor.average(); if (isSeed()) { - if (speed_average.upload == 0) return MAX_ETA; + qreal maxRatioValue = maxRatio(); + int maxSeedingTimeValue = maxSeedingTime(); + if ((maxRatioValue < 0) && (maxSeedingTimeValue < 0)) return MAX_ETA; - qreal max_ratio = maxRatio(); - if (max_ratio < 0) return MAX_ETA; + qlonglong ratioEta = MAX_ETA; - qlonglong realDL = totalDownload(); - if (realDL <= 0) - realDL = wantedSize(); + if ((speedAverage.upload > 0) && (maxRatioValue >= 0)) { - return ((realDL * max_ratio) - totalUpload()) / speed_average.upload; + qlonglong realDL = totalDownload(); + if (realDL <= 0) + realDL = wantedSize(); + + ratioEta = ((realDL * maxRatioValue) - totalUpload()) / speedAverage.upload; + } + + qlonglong seedingTimeEta = MAX_ETA; + + if (maxSeedingTimeValue >= 0) { + seedingTimeEta = (maxSeedingTimeValue * 60) - seedingTime(); + if (seedingTimeEta < 0) + seedingTimeEta = 0; + } + + return qMin(ratioEta, seedingTimeEta); } - if (!speed_average.download) return MAX_ETA; + if (!speedAverage.download) return MAX_ETA; - return (wantedSize() - completedSize()) / speed_average.download; + return (wantedSize() - completedSize()) / speedAverage.download; } QVector TorrentHandle::filesProgress() const @@ -1105,6 +1131,23 @@ qreal TorrentHandle::maxRatio(bool *usesGlobalRatio) const return ratioLimit; } +int TorrentHandle::maxSeedingTime(bool *usesGlobalSeedingTime) const +{ + int seedingTimeLimit = m_seedingTimeLimit; + + if (seedingTimeLimit == USE_GLOBAL_SEEDING_TIME) { + seedingTimeLimit = m_session->globalMaxSeedingMinutes(); + if (usesGlobalSeedingTime) + *usesGlobalSeedingTime = true; + } + else { + if (usesGlobalSeedingTime) + *usesGlobalSeedingTime = false; + } + + return seedingTimeLimit; +} + qreal TorrentHandle::realRatio() const { boost::int64_t upload = m_nativeStatus.all_time_upload; @@ -1572,6 +1615,7 @@ void TorrentHandle::handleSaveResumeDataAlert(libtorrent::save_resume_data_alert } resumeData["qBt-savePath"] = m_useAutoTMM ? "" : Profile::instance().toPortablePath(m_savePath).toStdString(); resumeData["qBt-ratioLimit"] = QString::number(m_ratioLimit).toStdString(); + resumeData["qBt-seedingTimeLimit"] = QString::number(m_seedingTimeLimit).toStdString(); resumeData["qBt-category"] = m_category.toStdString(); resumeData["qBt-name"] = m_name.toStdString(); resumeData["qBt-seedStatus"] = m_hasSeedStatus; @@ -1878,7 +1922,21 @@ void TorrentHandle::setRatioLimit(qreal limit) if (m_ratioLimit != limit) { m_ratioLimit = limit; m_needSaveResumeData = true; - m_session->handleTorrentRatioLimitChanged(this); + m_session->handleTorrentShareLimitChanged(this); + } +} + +void TorrentHandle::setSeedingTimeLimit(int limit) +{ + if (limit < USE_GLOBAL_SEEDING_TIME) + limit = NO_SEEDING_TIME_LIMIT; + else if (limit > MAX_SEEDING_TIME) + limit = MAX_SEEDING_TIME; + + if (m_seedingTimeLimit != limit) { + m_seedingTimeLimit = limit; + m_needSaveResumeData = true; + m_session->handleTorrentShareLimitChanged(this); } } diff --git a/src/base/bittorrent/torrenthandle.h b/src/base/bittorrent/torrenthandle.h index 2f97525e7..9de52e4d9 100644 --- a/src/base/bittorrent/torrenthandle.h +++ b/src/base/bittorrent/torrenthandle.h @@ -106,6 +106,7 @@ namespace BitTorrent QVector filePriorities; // for resumed torrents qreal ratioLimit; + int seedingTimeLimit; AddTorrentData(); AddTorrentData(const AddTorrentParams ¶ms); @@ -169,7 +170,11 @@ namespace BitTorrent static const qreal USE_GLOBAL_RATIO; static const qreal NO_RATIO_LIMIT; + static const int USE_GLOBAL_SEEDING_TIME; + static const int NO_SEEDING_TIME_LIMIT; + static const qreal MAX_RATIO; + static const int MAX_SEEDING_TIME; TorrentHandle(Session *session, const libtorrent::torrent_handle &nativeHandle, const AddTorrentData &data); @@ -251,6 +256,7 @@ namespace BitTorrent qreal progress() const; QDateTime addedTime() const; qreal ratioLimit() const; + int seedingTimeLimit() const; QString filePath(int index) const; QString fileName(int index) const; @@ -313,6 +319,7 @@ namespace BitTorrent QVector pieceAvailability() const; qreal distributedCopies() const; qreal maxRatio(bool *usesGlobalRatio = 0) const; + int maxSeedingTime(bool *usesGlobalSeedingTime = 0) const; qreal realRatio() const; int uploadPayloadRate() const; int downloadPayloadRate() const; @@ -341,6 +348,7 @@ namespace BitTorrent void prioritizeFiles(const QVector &priorities); void setFilePriority(int index, int priority); void setRatioLimit(qreal limit); + void setSeedingTimeLimit(int limit); void setUploadLimit(int limit); void setDownloadLimit(int limit); void setSuperSeeding(bool enable); @@ -439,6 +447,7 @@ namespace BitTorrent QString m_category; bool m_hasSeedStatus; qreal m_ratioLimit; + int m_seedingTimeLimit; bool m_tempPathDisabled; bool m_hasMissingFiles; bool m_hasRootFolder; diff --git a/src/gui/optionsdlg.cpp b/src/gui/optionsdlg.cpp index 360fea496..2b3835ad8 100644 --- a/src/gui/optionsdlg.cpp +++ b/src/gui/optionsdlg.cpp @@ -298,9 +298,14 @@ OptionsDialog::OptionsDialog(QWidget *parent) connect(m_ui->checkLSD, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->comboEncryption, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkMaxRatio, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); + connect(m_ui->checkMaxRatio, &QAbstractButton::toggled, this, &ThisType::toggleComboRatioLimitAct); connect(m_ui->spinMaxRatio, static_cast(&QDoubleSpinBox::valueChanged), this, &ThisType::enableApplyButton); connect(m_ui->comboRatioLimitAct, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); + connect(m_ui->checkMaxSeedingMinutes, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); + connect(m_ui->checkMaxSeedingMinutes, &QAbstractButton::toggled, this, &ThisType::toggleComboRatioLimitAct); + connect(m_ui->spinMaxSeedingMinutes, static_cast(&QSpinBox::valueChanged), + this, &ThisType::enableApplyButton); // Proxy tab connect(m_ui->comboProxyType, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); connect(m_ui->textProxyIP, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); @@ -601,6 +606,7 @@ void OptionsDialog::saveOptions() session->setAddTrackersEnabled(m_ui->checkEnableAddTrackers->isChecked()); session->setAdditionalTrackers(m_ui->textTrackers->toPlainText()); session->setGlobalMaxRatio(getMaxRatio()); + session->setGlobalMaxSeedingMinutes(getMaxSeedingMinutes()); session->setMaxRatioAction(static_cast(m_ui->comboRatioLimitAct->currentIndex())); // End Bittorrent preferences @@ -990,8 +996,19 @@ void OptionsDialog::loadOptions() // Disable m_ui->checkMaxRatio->setChecked(false); m_ui->spinMaxRatio->setEnabled(false); - m_ui->comboRatioLimitAct->setEnabled(false); } + if (session->globalMaxSeedingMinutes() >= 0) { + // Enable + m_ui->checkMaxSeedingMinutes->setChecked(true); + m_ui->spinMaxSeedingMinutes->setEnabled(true); + m_ui->spinMaxSeedingMinutes->setValue(session->globalMaxSeedingMinutes()); + } + else { + // Disable + m_ui->checkMaxSeedingMinutes->setChecked(false); + m_ui->spinMaxSeedingMinutes->setEnabled(false); + } + m_ui->comboRatioLimitAct->setEnabled((session->globalMaxSeedingMinutes() >= 0) || (session->globalMaxRatio() >= 0.)); m_ui->comboRatioLimitAct->setCurrentIndex(session->maxRatioAction()); // End Bittorrent preferences @@ -1122,6 +1139,14 @@ qreal OptionsDialog::getMaxRatio() const return -1; } +// Return Seeding Minutes +int OptionsDialog::getMaxSeedingMinutes() const +{ + if (m_ui->checkMaxSeedingMinutes->isChecked()) + return m_ui->spinMaxSeedingMinutes->value(); + return -1; +} + // Return max connections number int OptionsDialog::getMaxConnecs() const { @@ -1211,6 +1236,12 @@ void OptionsDialog::enableApplyButton() applyButton->setEnabled(true); } +void OptionsDialog::toggleComboRatioLimitAct() +{ + // Verify if the share action button must be enabled + m_ui->comboRatioLimitAct->setEnabled(m_ui->checkMaxRatio->isChecked() || m_ui->checkMaxSeedingMinutes->isChecked()); +} + void OptionsDialog::enableProxy(int index) { if (index) { diff --git a/src/gui/optionsdlg.h b/src/gui/optionsdlg.h index c55f47884..666490dc4 100644 --- a/src/gui/optionsdlg.h +++ b/src/gui/optionsdlg.h @@ -90,6 +90,7 @@ private slots: void on_buttonBox_rejected(); void applySettings(QAbstractButton* button); void enableApplyButton(); + void toggleComboRatioLimitAct(); void changePage(QListWidgetItem*, QListWidgetItem*); void loadWindowState(); void saveWindowState() const; @@ -145,6 +146,7 @@ private: bool isLSDEnabled() const; int getEncryptionSetting() const; qreal getMaxRatio() const; + int getMaxSeedingMinutes() const; // Proxy options bool isProxyEnabled() const; bool isProxyAuthEnabled() const; diff --git a/src/gui/optionsdlg.ui b/src/gui/optionsdlg.ui index 9ab7a0d38..7458c9d4b 100644 --- a/src/gui/optionsdlg.ui +++ b/src/gui/optionsdlg.ui @@ -1852,9 +1852,6 @@ true - - hh:mm - + + hh:mm + @@ -1876,12 +1876,6 @@ true - - hh:mm - - - false - + + hh:mm + + + false + @@ -2374,66 +2374,144 @@ Share Ratio Limiting - - - 9 - - - + + + + + Seed torrents until their ratio reaches + + + + + + + Qt::Horizontal + + + + 109 + 20 + + + + + + + + then + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + false + - - - Seed torrents until their ratio reaches - - + + Pause them + - - - false - - - Qt::AlignHCenter - - - 0.000000000000000 - - - 9998.000000000000000 - - - 0.050000000000000 - - - 1.000000000000000 - - + + Remove them + - - - - then - - - - - - - false - - - - Pause them - - - - - Remove them - - - - - + + + + + + Qt::Horizontal + + + + 121 + 28 + + + + + + + + Qt::Horizontal + + + + 100 + 42 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + Qt::AlignCenter + + + 9999999 + + + 1440 + + + + + + + false + + + Qt::AlignHCenter + + + 0.000000000000000 + + + 9998.000000000000000 + + + 0.050000000000000 + + + 1.000000000000000 + + + + + + + minutes + + + + + + + Seed torrents until their seeding time reaches + + @@ -2506,7 +2584,7 @@ - + Feeds refresh interval: @@ -3035,16 +3113,129 @@ tabOption comboI18n + browseSaveDirButton checkStartPaused spinPort checkUPnP + textWebUiUsername + checkWebUi + textSavePath + scrollArea_7 + scrollArea_2 + spinWebUiPort + textWebUiPassword + buttonBox + tabSelection + scrollArea + confirmDeletion + checkAltRowColors + actionTorrentDlOnDblClBox + actionTorrentFnOnDblClBox + checkStartup + checkShowSplash + checkStartMinimized + checkProgramExitConfirm + checkShowSystray + checkMinimizeToSysTray + checkCloseToSystray + comboTrayIcon + checkAssociateTorrents + checkAssociateMagnetLinks + checkPreventFromSuspend + checkAdditionDialog + checkAdditionDialogFront + checkPreallocateAll + checkTempFolder + textTempPath + browseTempDirButton + checkAppendqB + scanFoldersView + addScanFolderButton + removeScanFolderButton + checkExportDir + textExportDir + browseExportDirButton + checkExportDirFin + textExportDirFin + browseExportDirFinButton + groupMailNotification + dest_email_txt + smtp_server_txt + groupMailNotifAuth + mailNotifUsername + mailNotifPassword + checkSmtpSSL + autoRunBox + autoRun_txt + scrollArea_3 + randomButton + checkRandomPort + checkMaxConnecs + spinMaxConnec + checkMaxConnecsPerTorrent + spinMaxConnecPerTorrent + checkMaxUploadsPerTorrent + spinMaxUploadsPerTorrent + checkMaxUploads + spinMaxUploads + comboProxyType + textProxyIP + spinProxyPort + checkProxyPeerConnecs + checkForceProxy + isProxyOnlyForTorrents + checkProxyAuth + textProxyUsername + textProxyPassword + checkIPFilter + textFilterPath + browseFilterButton + IpFilterRefreshBtn + checkIpFilterTrackers + scrollArea_9 + spinUploadLimit + checkUploadLimit + spinDownloadLimit + checkDownloadLimit + check_schedule + schedule_to + schedule_from + schedule_days + checkUploadLimitAlt + checkDownloadLimitAlt + spinUploadLimitAlt + spinDownloadLimitAlt + checkLimitLocalPeerRate + checkLimitTransportOverhead + checkuTP + checkLimituTPConnections + scrollArea_4 + checkDHT + checkPeX checkLSD comboEncryption + checkAnonymousMode + checkEnableQueueing + spinMaxActiveDownloads + spinMaxActiveUploads + spinMaxActiveTorrents + checkIgnoreSlowTorrentsForQueueing checkMaxRatio spinMaxRatio - spinWebUiPort - textWebUiUsername - textWebUiPassword + checkMaxSeedingMinutes + spinMaxSeedingMinutes + comboRatioLimitAct + checkWebUIUPnP + checkWebUiHttps + btnWebUiCrt + btnWebUiKey + checkBypassLocalAuth + checkDynDNS + comboDNSService + registerDNSBtn + domainNameTxt + DNSUsernameTxt + DNSPasswordTxt @@ -3131,9 +3322,9 @@ - checkMaxRatio + checkMaxUploads toggled(bool) - spinMaxRatio + spinMaxUploads setEnabled(bool) @@ -3147,9 +3338,9 @@ - checkMaxRatio + checkDownloadLimitAlt toggled(bool) - comboRatioLimitAct + spinDownloadLimitAlt setEnabled(bool) @@ -3163,9 +3354,9 @@ - checkMaxUploads + checkUploadLimitAlt toggled(bool) - spinMaxUploads + spinUploadLimitAlt setEnabled(bool) @@ -3179,9 +3370,9 @@ - checkDownloadLimitAlt + checkMaxRatio toggled(bool) - spinDownloadLimitAlt + spinMaxRatio setEnabled(bool) @@ -3195,9 +3386,9 @@ - checkUploadLimitAlt + checkMaxSeedingMinutes toggled(bool) - spinUploadLimitAlt + spinMaxSeedingMinutes setEnabled(bool) diff --git a/src/gui/torrentcreatordlg.cpp b/src/gui/torrentcreatordlg.cpp index 1dec48678..668ee401a 100644 --- a/src/gui/torrentcreatordlg.cpp +++ b/src/gui/torrentcreatordlg.cpp @@ -169,7 +169,7 @@ void TorrentCreatorDlg::handleCreationSuccess(QString path, QString branch_path) BitTorrent::AddTorrentParams params; params.savePath = branch_path; params.skipChecking = true; - params.ignoreShareRatio = checkIgnoreShareLimits->isChecked(); + params.ignoreShareLimits = checkIgnoreShareLimits->isChecked(); BitTorrent::Session::instance()->addTorrent(t, params); } diff --git a/src/gui/transferlistwidget.cpp b/src/gui/transferlistwidget.cpp index 584e52fdf..7a7dcf823 100644 --- a/src/gui/transferlistwidget.cpp +++ b/src/gui/transferlistwidget.cpp @@ -487,16 +487,24 @@ void TransferListWidget::setMaxRatioSelectedTorrents() if (torrents.isEmpty()) return; bool useGlobalValue = true; - qreal currentMaxRatio = BitTorrent::Session::instance()->globalMaxRatio();; + qreal currentMaxRatio = BitTorrent::Session::instance()->globalMaxRatio(); if (torrents.count() == 1) currentMaxRatio = torrents[0]->maxRatio(&useGlobalValue); - UpDownRatioDlg dlg(useGlobalValue, currentMaxRatio, BitTorrent::TorrentHandle::MAX_RATIO, this); + int currentMaxSeedingTime = BitTorrent::Session::instance()->globalMaxSeedingMinutes(); + if (torrents.count() == 1) + currentMaxSeedingTime = torrents[0]->maxSeedingTime(&useGlobalValue); + + UpDownRatioDlg dlg(useGlobalValue, currentMaxRatio, BitTorrent::TorrentHandle::MAX_RATIO, + currentMaxSeedingTime, BitTorrent::TorrentHandle::MAX_SEEDING_TIME, this); if (dlg.exec() != QDialog::Accepted) return; foreach (BitTorrent::TorrentHandle *const torrent, torrents) { qreal ratio = (dlg.useDefault() ? BitTorrent::TorrentHandle::USE_GLOBAL_RATIO : dlg.ratio()); torrent->setRatioLimit(ratio); + + int seedingTime = (dlg.useDefault() ? BitTorrent::TorrentHandle::USE_GLOBAL_SEEDING_TIME : dlg.seedingTime()); + torrent->setSeedingTimeLimit(seedingTime); } } diff --git a/src/gui/updownratiodlg.cpp b/src/gui/updownratiodlg.cpp index 5d9400b19..efd14f523 100644 --- a/src/gui/updownratiodlg.cpp +++ b/src/gui/updownratiodlg.cpp @@ -30,11 +30,15 @@ #include "updownratiodlg.h" +#include + #include "base/bittorrent/session.h" + #include "ui_updownratiodlg.h" -UpDownRatioDlg::UpDownRatioDlg(bool useDefault, qreal initialValue, - qreal maxValue, QWidget *parent) +UpDownRatioDlg::UpDownRatioDlg(bool useDefault, qreal initialRatioValue, + qreal maxRatioValue, int initialTimeValue, + int maxTimeValue, QWidget *parent) : QDialog(parent) , ui(new Ui::UpDownRatioDlg) { @@ -43,21 +47,45 @@ UpDownRatioDlg::UpDownRatioDlg(bool useDefault, qreal initialValue, if (useDefault) { ui->useDefaultButton->setChecked(true); } - else if (initialValue == -1) { + else if ((initialRatioValue == -1) && (initialTimeValue == -1)) { ui->noLimitButton->setChecked(true); - initialValue = BitTorrent::Session::instance()->globalMaxRatio(); + initialRatioValue = BitTorrent::Session::instance()->globalMaxRatio(); + initialTimeValue = BitTorrent::Session::instance()->globalMaxSeedingMinutes(); } else { ui->torrentLimitButton->setChecked(true); + + if (initialRatioValue >= 0) + ui->checkMaxRatio->setChecked(true); + + if (initialTimeValue >= 0) + ui->checkMaxTime->setChecked(true); } ui->ratioSpinBox->setMinimum(0); - ui->ratioSpinBox->setMaximum(maxValue); - ui->ratioSpinBox->setValue(initialValue); + ui->ratioSpinBox->setMaximum(maxRatioValue); + ui->ratioSpinBox->setValue(initialRatioValue); + + ui->timeSpinBox->setMinimum(0); + ui->timeSpinBox->setMaximum(maxTimeValue); + ui->timeSpinBox->setValue(initialTimeValue); + connect(ui->buttonGroup, SIGNAL(buttonClicked(int)), SLOT(handleRatioTypeChanged())); + connect(ui->checkMaxRatio, SIGNAL(toggled(bool)), this, SLOT(enableRatioSpin())); + connect(ui->checkMaxTime, SIGNAL(toggled(bool)), this, SLOT(enableTimeSpin())); + handleRatioTypeChanged(); } +void UpDownRatioDlg::accept() +{ + if (ui->torrentLimitButton->isChecked() && !ui->checkMaxRatio->isChecked() && !ui->checkMaxTime->isChecked()) + QMessageBox::critical(this, tr("No share limit method selected"), + tr("Please select a limit method first")); + else + QDialog::accept(); +} + bool UpDownRatioDlg::useDefault() const { return ui->useDefaultButton->isChecked(); @@ -65,12 +93,32 @@ bool UpDownRatioDlg::useDefault() const qreal UpDownRatioDlg::ratio() const { - return ui->noLimitButton->isChecked() ? -1 : ui->ratioSpinBox->value(); + return (ui->noLimitButton->isChecked() || !ui->checkMaxRatio->isChecked()) ? -1 : ui->ratioSpinBox->value(); +} + +int UpDownRatioDlg::seedingTime() const +{ + return (ui->noLimitButton->isChecked() || !ui->checkMaxTime->isChecked()) ? -1 : ui->timeSpinBox->value(); } void UpDownRatioDlg::handleRatioTypeChanged() { - ui->ratioSpinBox->setEnabled(ui->torrentLimitButton->isChecked()); + // ui->ratioSpinBox->setEnabled(ui->torrentLimitButton->isChecked()); + ui->checkMaxRatio->setEnabled(ui->torrentLimitButton->isChecked()); + ui->checkMaxTime->setEnabled(ui->torrentLimitButton->isChecked()); + + ui->ratioSpinBox->setEnabled(ui->torrentLimitButton->isChecked() && ui->checkMaxRatio->isChecked()); + ui->timeSpinBox->setEnabled(ui->torrentLimitButton->isChecked() && ui->checkMaxTime->isChecked()); +} + +void UpDownRatioDlg::enableRatioSpin() +{ + ui->ratioSpinBox->setEnabled(ui->checkMaxRatio->isChecked()); +} + +void UpDownRatioDlg::enableTimeSpin() +{ + ui->timeSpinBox->setEnabled(ui->checkMaxTime->isChecked()); } UpDownRatioDlg::~UpDownRatioDlg() diff --git a/src/gui/updownratiodlg.h b/src/gui/updownratiodlg.h index 31721b707..b7a59de81 100644 --- a/src/gui/updownratiodlg.h +++ b/src/gui/updownratiodlg.h @@ -45,14 +45,21 @@ class UpDownRatioDlg : public QDialog public: explicit UpDownRatioDlg(bool useDefault, qreal initialValue, qreal maxValue, + int initialTimeValue, int maxTimeValue, QWidget *parent = 0); ~UpDownRatioDlg(); bool useDefault() const; qreal ratio() const; + int seedingTime() const; + +public slots: + void accept(); private slots: void handleRatioTypeChanged(); + void enableRatioSpin(); + void enableTimeSpin(); private: Ui::UpDownRatioDlg *ui; diff --git a/src/gui/updownratiodlg.ui b/src/gui/updownratiodlg.ui index 326fbaf5f..6774d8a0d 100644 --- a/src/gui/updownratiodlg.ui +++ b/src/gui/updownratiodlg.ui @@ -6,8 +6,8 @@ 0 0 - 317 - 152 + 399 + 195 @@ -17,7 +17,7 @@ - Use global ratio limit + Use global share limit buttonGroup @@ -27,7 +27,7 @@ - Set no ratio limit + Set no share limit buttonGroup @@ -35,18 +35,18 @@ - - + + - Set ratio limit to + Set share limit to buttonGroup - + 9998.000000000000000 @@ -59,7 +59,29 @@ - + + + + + 0 + 0 + + + + 0 + + + 525600.000000000000000 + + + 1.000000000000000 + + + 1440.000000000000000 + + + + Qt::Horizontal @@ -72,6 +94,20 @@ + + + + ratio + + + + + + + minutes + + + @@ -135,6 +171,7 @@ + diff --git a/src/webui/extra_translations.h b/src/webui/extra_translations.h index 5027a2fa9..a634d0f60 100644 --- a/src/webui/extra_translations.h +++ b/src/webui/extra_translations.h @@ -74,6 +74,7 @@ static const char *__TRANSLATIONS__[] = { QT_TRANSLATE_NOOP("HttpServer", "Unknown"), QT_TRANSLATE_NOOP("HttpServer", "Hard Disk"), QT_TRANSLATE_NOOP("HttpServer", "Share ratio limit must be between 0 and 9998.") + QT_TRANSLATE_NOOP("HttpServer", "Seeding time limit must be between 0 and 525600 minutes.") }; static const struct { const char *source; const char *comment; } __COMMENTED_TRANSLATIONS__[] = { diff --git a/src/webui/prefjson.cpp b/src/webui/prefjson.cpp index 1d02bbdae..8acf605da 100644 --- a/src/webui/prefjson.cpp +++ b/src/webui/prefjson.cpp @@ -151,6 +151,8 @@ QByteArray prefjson::getPreferences() // Share Ratio Limiting data["max_ratio_enabled"] = (session->globalMaxRatio() >= 0.); data["max_ratio"] = session->globalMaxRatio(); + data["max_seeding_time_enabled"] = (session->globalMaxSeedingMinutes() >= 0.); + data["max_seeding_time"] = session->globalMaxSeedingMinutes(); data["max_ratio_act"] = session->maxRatioAction(); // Add trackers data["add_trackers_enabled"] = session->isAddTrackersEnabled(); @@ -367,6 +369,10 @@ void prefjson::setPreferences(const QString& json) session->setGlobalMaxRatio(m["max_ratio"].toReal()); else session->setGlobalMaxRatio(-1); + if (m.contains("max_seeding_time_enabled")) + session->setGlobalMaxSeedingMinutes(m["max_seeding_time"].toInt()); + else + session->setGlobalMaxSeedingMinutes(-1); if (m.contains("max_ratio_act")) session->setMaxRatioAction(static_cast(m["max_ratio_act"].toInt())); // Add trackers diff --git a/src/webui/www/public/preferences_content.html b/src/webui/www/public/preferences_content.html index acf172bbc..6edd9fdf4 100644 --- a/src/webui/www/public/preferences_content.html +++ b/src/webui/www/public/preferences_content.html @@ -307,14 +307,36 @@
QBT_TR(Share Ratio Limiting)QBT_TR[CONTEXT=OptionsDialog] - + + + + + + + + + + + + +
+ + +
+ + + + QBT_TR(minutes)QBT_TR[CONTEXT=OptionsDialog] +
QBT_TR(then)QBT_TR[CONTEXT=OptionsDialog] + +
@@ -724,12 +746,20 @@ updateQueueingSystem = function() { } } -updateMaxRatioEnabled = function() { +updateMaxRatioTimeEnabled = function() { if($('max_ratio_checkbox').getProperty('checked')) { $('max_ratio_value').setProperty('disabled', false); - $('max_ratio_act').setProperty('disabled', false); } else { $('max_ratio_value').setProperty('disabled', true); + } + if($('max_seeding_time_checkbox').getProperty('checked')) { + $('max_seeding_time_value').setProperty('disabled', false); + } else { + $('max_seeding_time_value').setProperty('disabled', true); + } + if($('max_ratio_checkbox').getProperty('checked') || $('max_seeding_time_checkbox').getProperty('checked')) { + $('max_ratio_act').setProperty('disabled', false); + } else { $('max_ratio_act').setProperty('disabled', true); } } @@ -992,7 +1022,7 @@ loadPreferences = function() { $('dont_count_slow_torrents_checkbox').setProperty('checked', pref.dont_count_slow_torrents); updateQueueingSystem(); - // Share Ratio Limiting + // Share Limiting $('max_ratio_checkbox').setProperty('checked', pref.max_ratio_enabled); if (pref.max_ratio_enabled) $('max_ratio_value').setProperty('value', pref.max_ratio); @@ -1000,7 +1030,14 @@ loadPreferences = function() { $('max_ratio_value').setProperty('value', 1); var max_ratio_act = pref.max_ratio_act.toInt(); $('max_ratio_act').getChildren('option')[max_ratio_act].setAttribute('selected', ''); - updateMaxRatioEnabled(); + $('max_seeding_time_checkbox').setProperty('checked', pref.max_seeding_time_enabled); + if (pref.max_seeding_time_enabled) + $('max_seeding_time_value').setProperty('value', pref.max_seeding_time.toInt()); + else + $('max_seeding_time_value').setProperty('value', 1440); + var max_ratio_act = pref.max_ratio_act.toInt(); + $('max_ratio_act').getChildren('option')[max_ratio_act].setAttribute('selected', ''); + updateMaxRatioTimeEnabled(); // Add trackers $('add_trackers_checkbox').setProperty('checked', pref.add_trackers_enabled); @@ -1258,6 +1295,18 @@ applyPreferences = function() { settings.set('max_ratio', max_ratio); settings.set('max_ratio_act', $('max_ratio_act').getProperty('value').toInt()); + var max_seeding_time = -1; + if($('max_seeding_time_checkbox').getProperty('checked')) { + max_seeding_time = $('max_seeding_time_value').getProperty('value').toInt(); + if(isNaN(max_seeding_time) || max_seeding_time < 0 || max_seeding_time > 525600) { + alert("QBT_TR(Seeding time limit must be between 0 and 525600 minutes.)QBT_TR[CONTEXT=HttpServer]"); + return; + } + } + settings.set('max_seeding_time_enabled', $('max_seeding_time_checkbox').getProperty('checked')); + settings.set('max_seeding_time', max_seeding_time); + settings.set('max_ratio_act', $('max_ratio_act').getProperty('value').toInt()); + // Add trackers settings.set('add_trackers_enabled', $('add_trackers_checkbox').getProperty('checked')); settings.set('add_trackers', $('add_trackers_textarea').getProperty('value'));