diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h index 7e1b9dc6c55..781a195e55a 100644 --- a/src/base/bittorrent/session.h +++ b/src/base/bittorrent/session.h @@ -237,6 +237,12 @@ namespace BitTorrent virtual Path finishedTorrentExportDirectory() const = 0; virtual void setFinishedTorrentExportDirectory(const Path &path) = 0; + virtual bool isAddTrackersFromURLEnabled() const = 0; + virtual void setAddTrackersFromURLEnabled(bool enabled) = 0; + virtual QString additionalTrackersURL() const = 0; + virtual void setAdditionalTrackersURL(const QString &url) = 0; + virtual QString additionalTrackersFromURL() const = 0; + virtual int globalDownloadSpeedLimit() const = 0; virtual void setGlobalDownloadSpeedLimit(int limit) = 0; virtual int globalUploadSpeedLimit() const = 0; diff --git a/src/base/bittorrent/sessionimpl.cpp b/src/base/bittorrent/sessionimpl.cpp index cc0796df89c..d83dd4faafc 100644 --- a/src/base/bittorrent/sessionimpl.cpp +++ b/src/base/bittorrent/sessionimpl.cpp @@ -77,6 +77,7 @@ #include "base/algorithm.h" #include "base/global.h" #include "base/logger.h" +#include "base/net/downloadmanager.h" #include "base/net/proxyconfigurationmanager.h" #include "base/preferences.h" #include "base/profile.h" @@ -460,6 +461,8 @@ SessionImpl::SessionImpl(QObject *parent) , m_blockPeersOnPrivilegedPorts(BITTORRENT_SESSION_KEY(u"BlockPeersOnPrivilegedPorts"_s), false) , m_isAddTrackersEnabled(BITTORRENT_SESSION_KEY(u"AddTrackersEnabled"_s), false) , m_additionalTrackers(BITTORRENT_SESSION_KEY(u"AdditionalTrackers"_s)) + , m_isAddTrackersFromURLEnabled(BITTORRENT_SESSION_KEY(u"AddTrackersFromURLEnabled"_s), false) + , m_additionalTrackersURL(BITTORRENT_SESSION_KEY(u"AdditionalTrackersURL"_s)) , m_globalMaxRatio(BITTORRENT_SESSION_KEY(u"GlobalMaxRatio"_s), -1, [](qreal r) { return r < 0 ? -1. : r;}) , m_globalMaxSeedingMinutes(BITTORRENT_SESSION_KEY(u"GlobalMaxSeedingMinutes"_s), -1, lowerLimited(-1)) , m_globalMaxInactiveSeedingMinutes(BITTORRENT_SESSION_KEY(u"GlobalMaxInactiveSeedingMinutes"_s), -1, lowerLimited(-1)) @@ -614,6 +617,15 @@ SessionImpl::SessionImpl(QObject *parent) enableTracker(isTrackerEnabled()); prepareStartup(); + + m_updateTrackersFromURLTimer = new QTimer(this); + m_updateTrackersFromURLTimer->setInterval(24h); + connect(m_updateTrackersFromURLTimer, &QTimer::timeout, this, &SessionImpl::updateTrackersFromURL); + if (isAddTrackersFromURLEnabled()) + { + updateTrackersFromURL(); + m_updateTrackersFromURLTimer->start(); + } } SessionImpl::~SessionImpl() @@ -2261,6 +2273,11 @@ void SessionImpl::populateAdditionalTrackers() m_additionalTrackerEntries = parseTrackerEntries(additionalTrackers()); } +void SessionImpl::populateAdditionalTrackersFromURL() +{ + m_additionalTrackerEntriesFromURL = parseTrackerEntries(additionalTrackersFromURL()); +} + void SessionImpl::processTorrentShareLimits(TorrentImpl *torrent) { if (!torrent->isFinished() || torrent->isForced()) @@ -2880,6 +2897,21 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr } } + if (isAddTrackersFromURLEnabled() && !(hasMetadata && p.ti->priv())) + { + const auto maxTierIter = std::max_element(p.tracker_tiers.cbegin(), p.tracker_tiers.cend()); + const int baseTier = (maxTierIter != p.tracker_tiers.cend()) ? (*maxTierIter + 1) : 0; + + p.trackers.reserve(p.trackers.size() + static_cast(m_additionalTrackerEntriesFromURL.size())); + p.tracker_tiers.reserve(p.trackers.size() + static_cast(m_additionalTrackerEntriesFromURL.size())); + p.tracker_tiers.resize(p.trackers.size(), 0); + for (const TrackerEntry &trackerEntry : asConst(m_additionalTrackerEntriesFromURL)) + { + p.trackers.emplace_back(trackerEntry.url.toStdString()); + p.tracker_tiers.emplace_back(Utils::Number::clampingAdd(trackerEntry.tier, baseTier)); + } + } + p.upload_limit = addTorrentParams.uploadLimit; p.download_limit = addTorrentParams.downloadLimit; @@ -3872,6 +3904,86 @@ void SessionImpl::setAdditionalTrackers(const QString &trackers) populateAdditionalTrackers(); } +bool SessionImpl::isAddTrackersFromURLEnabled() const +{ + return m_isAddTrackersFromURLEnabled; +} + +void SessionImpl::setAddTrackersFromURLEnabled(const bool enabled) +{ + if (enabled != isAddTrackersFromURLEnabled()) + { + m_isAddTrackersFromURLEnabled = enabled; + if (enabled) + { + updateTrackersFromURL(); + m_updateTrackersFromURLTimer->start(); + } + else + { + m_updateTrackersFromURLTimer->stop(); + setAdditionalTrackersFromURL({}); + } + } +} + +QString SessionImpl::additionalTrackersURL() const +{ + return m_additionalTrackersURL; +} + +void SessionImpl::setAdditionalTrackersURL(const QString &url) +{ + if (url != additionalTrackersURL()) + { + m_additionalTrackersURL = url.trimmed(); + if (isAddTrackersFromURLEnabled()) + updateTrackersFromURL(); + } +} + +QString SessionImpl::additionalTrackersFromURL() const +{ + return m_additionalTrackersFromURL; +} + +void SessionImpl::setAdditionalTrackersFromURL(const QString &trackers) +{ + if (trackers != additionalTrackersFromURL()) + { + m_additionalTrackersFromURL = trackers; + populateAdditionalTrackersFromURL(); + } +} + +void SessionImpl::updateTrackersFromURL() +{ + const QString &url = additionalTrackersURL(); + if (url.isEmpty()) + { + setAdditionalTrackersFromURL({}); + } + else + { + Preferences *const pref = Preferences::instance(); + Net::DownloadManager::instance()->download(Net::DownloadRequest(url) + , pref->useProxyForGeneralPurposes(), this, &SessionImpl::handleTrackersFromURLDownloadFinished); + } +} + +void SessionImpl::handleTrackersFromURLDownloadFinished(const Net::DownloadResult &result) +{ + if (result.status == Net::DownloadStatus::Success) + { + setAdditionalTrackersFromURL(QString::fromUtf8(result.data)); + LogMsg(tr("Tracker list updated"), Log::INFO); + } + else + { + LogMsg(tr("Failed to update tracker list. Reason: \"%1\"").arg(result.errorString), Log::WARNING); + } +} + bool SessionImpl::isIPFilteringEnabled() const { return m_isIPFilteringEnabled; diff --git a/src/base/bittorrent/sessionimpl.h b/src/base/bittorrent/sessionimpl.h index 0a1d13693fa..beef738f2d2 100644 --- a/src/base/bittorrent/sessionimpl.h +++ b/src/base/bittorrent/sessionimpl.h @@ -66,6 +66,11 @@ class FileSearcher; class FilterParserThread; class NativeSessionExtension; +namespace Net +{ + struct DownloadResult; +} + namespace BitTorrent { enum class MoveStorageMode; @@ -490,6 +495,12 @@ namespace BitTorrent m_asyncWorker->start(std::forward(func)); } + bool isAddTrackersFromURLEnabled() const override; + void setAddTrackersFromURLEnabled(bool enabled) override; + QString additionalTrackersURL() const override; + void setAdditionalTrackersURL(const QString &url) override; + QString additionalTrackersFromURL() const override; + signals: void addTorrentAlertsReceived(qsizetype count); @@ -502,6 +513,7 @@ namespace BitTorrent void handleIPFilterError(); void fileSearchFinished(const TorrentID &id, const Path &savePath, const PathList &fileNames); void torrentContentRemovingFinished(const QString &torrentName, const QString &errorMessage); + void handleTrackersFromURLDownloadFinished(const Net::DownloadResult &result); private: struct ResumeSessionContext; @@ -596,6 +608,8 @@ namespace BitTorrent void saveTorrentsQueue(); void removeTorrentsQueue(); + void populateAdditionalTrackersFromURL(); + std::vector getPendingAlerts(lt::time_duration time = lt::time_duration::zero()) const; void moveTorrentStorage(const MoveStorageJob &job) const; @@ -614,6 +628,9 @@ namespace BitTorrent void handleRemovedTorrent(const TorrentID &torrentID, const QString &partfileRemoveError = {}); + void setAdditionalTrackersFromURL(const QString &trackers); + void updateTrackersFromURL(); + CachedSettingValue m_DHTBootstrapNodes; CachedSettingValue m_isDHTEnabled; CachedSettingValue m_isLSDEnabled; @@ -676,6 +693,8 @@ namespace BitTorrent CachedSettingValue m_blockPeersOnPrivilegedPorts; CachedSettingValue m_isAddTrackersEnabled; CachedSettingValue m_additionalTrackers; + CachedSettingValue m_isAddTrackersFromURLEnabled; + CachedSettingValue m_additionalTrackersURL; CachedSettingValue m_globalMaxRatio; CachedSettingValue m_globalMaxSeedingMinutes; CachedSettingValue m_globalMaxInactiveSeedingMinutes; @@ -749,6 +768,9 @@ namespace BitTorrent bool m_IPFilteringConfigured = false; mutable bool m_listenInterfaceConfigured = false; + QString m_additionalTrackersFromURL; + QTimer *m_updateTrackersFromURLTimer = nullptr; + bool m_isRestored = false; bool m_isPaused = isStartPaused(); @@ -759,6 +781,7 @@ namespace BitTorrent int m_numResumeData = 0; QList m_additionalTrackerEntries; + QList m_additionalTrackerEntriesFromURL; QList m_excludedFileNamesRegExpList; // Statistics diff --git a/src/gui/optionsdialog.cpp b/src/gui/optionsdialog.cpp index 63b185ed337..e89dd20e486 100644 --- a/src/gui/optionsdialog.cpp +++ b/src/gui/optionsdialog.cpp @@ -53,6 +53,7 @@ #include "base/bittorrent/sharelimitaction.h" #include "base/exceptions.h" #include "base/global.h" +#include "base/net/downloadmanager.h" #include "base/net/portforwarder.h" #include "base/net/proxyconfigurationmanager.h" #include "base/path.h" @@ -1148,6 +1149,10 @@ void OptionsDialog::loadBittorrentTabOptions() m_ui->checkEnableAddTrackers->setChecked(session->isAddTrackersEnabled()); m_ui->textTrackers->setPlainText(session->additionalTrackers()); + m_ui->checkAddTrackersFromURL->setChecked(session->isAddTrackersFromURLEnabled()); + m_ui->textTrackersURL->setText(session->additionalTrackersURL()); + m_ui->textTrackersFromURL->setPlainText(session->additionalTrackersFromURL()); + connect(m_ui->checkDHT, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkPeX, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkLSD, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); @@ -1181,6 +1186,9 @@ void OptionsDialog::loadBittorrentTabOptions() connect(m_ui->checkEnableAddTrackers, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->textTrackers, &QPlainTextEdit::textChanged, this, &ThisType::enableApplyButton); + + connect(m_ui->checkAddTrackersFromURL, &QGroupBox::toggled, this, &ThisType::enableApplyButton); + connect(m_ui->textTrackersURL, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); } void OptionsDialog::saveBittorrentTabOptions() const @@ -1218,6 +1226,9 @@ void OptionsDialog::saveBittorrentTabOptions() const session->setAddTrackersEnabled(m_ui->checkEnableAddTrackers->isChecked()); session->setAdditionalTrackers(m_ui->textTrackers->toPlainText()); + + session->setAddTrackersFromURLEnabled(m_ui->checkAddTrackersFromURL->isChecked()); + session->setAdditionalTrackersURL(m_ui->textTrackersURL->text()); } void OptionsDialog::loadRSSTabOptions() diff --git a/src/gui/optionsdialog.ui b/src/gui/optionsdialog.ui index b122b429f15..15b284594a1 100644 --- a/src/gui/optionsdialog.ui +++ b/src/gui/optionsdialog.ui @@ -3131,6 +3131,49 @@ Disable encryption: Only connect to peers without protocol encryption + + + + Automatically append trackers from URL to new downloads: + + + true + + + false + + + + + + + + URL: + + + + + + + + + + + + Fetched trackers + + + + + + + true + + + + + + diff --git a/src/webui/api/appcontroller.cpp b/src/webui/api/appcontroller.cpp index 14517be7d03..1422964fc04 100644 --- a/src/webui/api/appcontroller.cpp +++ b/src/webui/api/appcontroller.cpp @@ -306,6 +306,9 @@ void AppController::preferencesAction() // Add trackers data[u"add_trackers_enabled"_s] = session->isAddTrackersEnabled(); data[u"add_trackers"_s] = session->additionalTrackers(); + data[u"add_trackers_from_url_enabled"_s] = session->isAddTrackersFromURLEnabled(); + data[u"add_trackers_url"_s] = session->additionalTrackersURL(); + data[u"add_trackers_url_list"_s] = session->additionalTrackersFromURL(); // WebUI // HTTP Server @@ -860,6 +863,10 @@ void AppController::setPreferencesAction() session->setAddTrackersEnabled(it.value().toBool()); if (hasKey(u"add_trackers"_s)) session->setAdditionalTrackers(it.value().toString()); + if (hasKey(u"add_trackers_from_url_enabled"_s)) + session->setAddTrackersFromURLEnabled(it.value().toBool()); + if (hasKey(u"add_trackers_url"_s)) + session->setAdditionalTrackersURL(it.value().toString()); // WebUI // HTTP Server diff --git a/src/webui/www/private/views/preferences.html b/src/webui/www/private/views/preferences.html index e9bb537e98d..3b8ca5eaec9 100644 --- a/src/webui/www/private/views/preferences.html +++ b/src/webui/www/private/views/preferences.html @@ -820,6 +820,23 @@ + +
+ + + + +
+ + +
+
+
+ QBT_TR(Fetched trackers)QBT_TR[CONTEXT=OptionsDialog] + +
+
+