diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h index 8255f2055f8d..118b02d359fb 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 30813020666a..be0d776f246c 100644 --- a/src/base/bittorrent/sessionimpl.cpp +++ b/src/base/bittorrent/sessionimpl.cpp @@ -78,6 +78,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" @@ -463,6 +464,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)) @@ -617,6 +620,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() @@ -2268,6 +2280,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()) @@ -2887,6 +2904,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; @@ -3879,6 +3911,82 @@ 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 + { + Net::DownloadManager::instance()->download(Net::DownloadRequest(url) + , Preferences::instance()->useProxyForGeneralPurposes(), this, [this](const Net::DownloadResult &result) + { + if (result.status == Net::DownloadStatus::Success) + { + setAdditionalTrackersFromURL(QString::fromUtf8(result.data)); + LogMsg(tr("Tracker list updated"), Log::INFO); + return; + } + + 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 7298afabe8c4..8876b9a31ab5 100644 --- a/src/base/bittorrent/sessionimpl.h +++ b/src/base/bittorrent/sessionimpl.h @@ -491,6 +491,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); @@ -596,6 +602,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 +622,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 +687,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 +762,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 +775,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 a96b353a6191..13e9148fc071 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" @@ -1151,6 +1152,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); @@ -1184,6 +1189,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 @@ -1221,6 +1229,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 80b5eff8c681..16484c71705e 100644 --- a/src/gui/optionsdialog.ui +++ b/src/gui/optionsdialog.ui @@ -3148,6 +3148,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 c56ab79fad6d..a391c8dc9a21 100644 --- a/src/webui/api/appcontroller.cpp +++ b/src/webui/api/appcontroller.cpp @@ -307,6 +307,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 @@ -863,6 +866,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 16dde1347522..816e6f2ac21c 100644 --- a/src/webui/www/private/views/preferences.html +++ b/src/webui/www/private/views/preferences.html @@ -825,6 +825,23 @@ + +
+ + + + +
+ + +
+
+
+ QBT_TR(Fetched trackers)QBT_TR[CONTEXT=OptionsDialog] + +
+
+