From 7e0cd223fd7fa6f1b374c1863bb21c600f93e506 Mon Sep 17 00:00:00 2001 From: Vladimir Golovnev Date: Sun, 22 May 2022 09:09:11 +0300 Subject: [PATCH] Revamp tracker entries handling PR #17017. --- src/base/CMakeLists.txt | 1 + src/base/base.pri | 1 + src/base/bittorrent/extensiondata.h | 45 +++++ src/base/bittorrent/magneturi.cpp | 11 +- .../bittorrent/nativesessionextension.cpp | 7 +- src/base/bittorrent/nativesessionextension.h | 12 +- .../bittorrent/nativetorrentextension.cpp | 13 +- src/base/bittorrent/nativetorrentextension.h | 8 +- src/base/bittorrent/session.cpp | 130 +++--------- src/base/bittorrent/session.h | 14 +- src/base/bittorrent/torrent.h | 6 +- src/base/bittorrent/torrentimpl.cpp | 186 +++++++++--------- src/base/bittorrent/torrentimpl.h | 17 +- src/base/bittorrent/torrentinfo.cpp | 3 +- src/base/bittorrent/torrentinfo.h | 2 +- src/base/bittorrent/trackerentry.cpp | 14 +- src/base/bittorrent/trackerentry.h | 14 +- src/gui/mainwindow.cpp | 1 + src/gui/properties/trackerlistwidget.cpp | 13 +- src/gui/transferlistfilterswidget.cpp | 157 ++++++++++----- src/gui/transferlistfilterswidget.h | 18 +- src/webui/api/torrentscontroller.cpp | 21 +- 22 files changed, 363 insertions(+), 331 deletions(-) create mode 100644 src/base/bittorrent/extensiondata.h diff --git a/src/base/CMakeLists.txt b/src/base/CMakeLists.txt index e59b7431c..4c1fafd85 100644 --- a/src/base/CMakeLists.txt +++ b/src/base/CMakeLists.txt @@ -13,6 +13,7 @@ add_library(qbt_base STATIC bittorrent/customstorage.h bittorrent/dbresumedatastorage.h bittorrent/downloadpriority.h + bittorrent/extensiondata.h bittorrent/filesearcher.h bittorrent/filterparserthread.h bittorrent/infohash.h diff --git a/src/base/base.pri b/src/base/base.pri index fff9e3ace..f6b838536 100644 --- a/src/base/base.pri +++ b/src/base/base.pri @@ -12,6 +12,7 @@ HEADERS += \ $$PWD/bittorrent/customstorage.h \ $$PWD/bittorrent/downloadpriority.h \ $$PWD/bittorrent/dbresumedatastorage.h \ + $$PWD/bittorrent/extensiondata.h \ $$PWD/bittorrent/filesearcher.h \ $$PWD/bittorrent/filterparserthread.h \ $$PWD/bittorrent/infohash.h \ diff --git a/src/base/bittorrent/extensiondata.h b/src/base/bittorrent/extensiondata.h new file mode 100644 index 000000000..e3c380c2e --- /dev/null +++ b/src/base/bittorrent/extensiondata.h @@ -0,0 +1,45 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2022 Vladimir Golovnev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#pragma once + +#include + +#include + +#ifdef QBT_USES_LIBTORRENT2 +#include +using LTClientData = lt::client_data_t; +#else +using LTClientData = void *; +#endif + +struct ExtensionData +{ + std::vector trackers; +}; diff --git a/src/base/bittorrent/magneturi.cpp b/src/base/bittorrent/magneturi.cpp index fb8cee20a..dba8bb392 100644 --- a/src/base/bittorrent/magneturi.cpp +++ b/src/base/bittorrent/magneturi.cpp @@ -100,8 +100,15 @@ MagnetUri::MagnetUri(const QString &source) m_name = QString::fromStdString(m_addTorrentParams.name); m_trackers.reserve(static_cast(m_addTorrentParams.trackers.size())); - for (const std::string &tracker : m_addTorrentParams.trackers) - m_trackers.append({QString::fromStdString(tracker)}); + int tier = 0; + auto tierIter = m_addTorrentParams.tracker_tiers.cbegin(); + for (const std::string &url : m_addTorrentParams.trackers) + { + if (tierIter != m_addTorrentParams.tracker_tiers.cend()) + tier = *tierIter++; + + m_trackers.append({QString::fromStdString(url), tier}); + } m_urlSeeds.reserve(static_cast(m_addTorrentParams.url_seeds.size())); for (const std::string &urlSeed : m_addTorrentParams.url_seeds) diff --git a/src/base/bittorrent/nativesessionextension.cpp b/src/base/bittorrent/nativesessionextension.cpp index 451c7364f..f96028c96 100644 --- a/src/base/bittorrent/nativesessionextension.cpp +++ b/src/base/bittorrent/nativesessionextension.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2020 Vladimir Golovnev + * Copyright (C) 2020-2022 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,6 +30,7 @@ #include +#include "extensiondata.h" #include "nativetorrentextension.h" namespace @@ -49,9 +50,9 @@ lt::feature_flags_t NativeSessionExtension::implemented_features() return alert_feature; } -std::shared_ptr NativeSessionExtension::new_torrent(const lt::torrent_handle &torrentHandle, ClientData) +std::shared_ptr NativeSessionExtension::new_torrent(const lt::torrent_handle &torrentHandle, LTClientData clientData) { - return std::make_shared(torrentHandle); + return std::make_shared(torrentHandle, static_cast(clientData)); } void NativeSessionExtension::on_alert(const lt::alert *alert) diff --git a/src/base/bittorrent/nativesessionextension.h b/src/base/bittorrent/nativesessionextension.h index ffe49b025..7ec291f28 100644 --- a/src/base/bittorrent/nativesessionextension.h +++ b/src/base/bittorrent/nativesessionextension.h @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2020 Vladimir Golovnev + * Copyright (C) 2020-2022 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,15 +30,11 @@ #include +#include "extensiondata.h" + class NativeSessionExtension final : public lt::plugin { -#ifdef QBT_USES_LIBTORRENT2 - using ClientData = lt::client_data_t; -#else - using ClientData = void *; -#endif - lt::feature_flags_t implemented_features() override; - std::shared_ptr new_torrent(const lt::torrent_handle &torrentHandle, ClientData) override; + std::shared_ptr new_torrent(const lt::torrent_handle &torrentHandle, LTClientData clientData) override; void on_alert(const lt::alert *alert) override; }; diff --git a/src/base/bittorrent/nativetorrentextension.cpp b/src/base/bittorrent/nativetorrentextension.cpp index 92583dc91..b520d75de 100644 --- a/src/base/bittorrent/nativetorrentextension.cpp +++ b/src/base/bittorrent/nativetorrentextension.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2020 Vladimir Golovnev + * Copyright (C) 2020-2022 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -38,10 +38,19 @@ namespace } } -NativeTorrentExtension::NativeTorrentExtension(const lt::torrent_handle &torrentHandle) +NativeTorrentExtension::NativeTorrentExtension(const lt::torrent_handle &torrentHandle, ExtensionData *data) : m_torrentHandle {torrentHandle} + , m_data {data} { on_state(m_torrentHandle.status({}).state); + + if (m_data) + m_data->trackers = m_torrentHandle.trackers(); +} + +NativeTorrentExtension::~NativeTorrentExtension() +{ + delete m_data; } bool NativeTorrentExtension::on_pause() diff --git a/src/base/bittorrent/nativetorrentextension.h b/src/base/bittorrent/nativetorrentextension.h index 8e987e03c..8667eec50 100644 --- a/src/base/bittorrent/nativetorrentextension.h +++ b/src/base/bittorrent/nativetorrentextension.h @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2020 Vladimir Golovnev + * Copyright (C) 2020-2022 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -31,10 +31,13 @@ #include #include +#include "extensiondata.h" + class NativeTorrentExtension final : public lt::torrent_plugin { public: - explicit NativeTorrentExtension(const lt::torrent_handle &torrentHandle); + NativeTorrentExtension(const lt::torrent_handle &torrentHandle, ExtensionData *data); + ~NativeTorrentExtension(); private: bool on_pause() override; @@ -42,4 +45,5 @@ private: lt::torrent_handle m_torrentHandle; lt::torrent_status::state_t m_state = lt::torrent_status::checking_resume_data; + ExtensionData *m_data = nullptr; }; diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index 57cb2e399..af7188339 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -95,6 +95,7 @@ #include "customstorage.h" #include "dbresumedatastorage.h" #include "downloadpriority.h" +#include "extensiondata.h" #include "filesearcher.h" #include "filterparserthread.h" #include "loadtorrentparams.h" @@ -292,67 +293,6 @@ namespace return {}; } #endif - -#ifdef QBT_USES_LIBTORRENT2 - TrackerEntryUpdateInfo getTrackerEntryUpdateInfo(const lt::announce_entry &nativeEntry, const lt::info_hash_t &hashes) -#else - TrackerEntryUpdateInfo getTrackerEntryUpdateInfo(const lt::announce_entry &nativeEntry) -#endif - { - TrackerEntryUpdateInfo result {}; - int numUpdating = 0; - int numWorking = 0; - int numNotWorking = 0; -#ifdef QBT_USES_LIBTORRENT2 - const auto numEndpoints = static_cast(nativeEntry.endpoints.size() * ((hashes.has_v1() && hashes.has_v2()) ? 2 : 1)); - for (const lt::announce_endpoint &endpoint : nativeEntry.endpoints) - { - for (const auto protocolVersion : {lt::protocol_version::V1, lt::protocol_version::V2}) - { - if (hashes.has(protocolVersion)) - { - const lt::announce_infohash &infoHash = endpoint.info_hashes[protocolVersion]; - - if (!result.hasMessages) - result.hasMessages = !infoHash.message.empty(); - - if (infoHash.updating) - ++numUpdating; - else if (infoHash.fails > 0) - ++numNotWorking; - else if (nativeEntry.verified) - ++numWorking; - } - } - } -#else - const auto numEndpoints = static_cast(nativeEntry.endpoints.size()); - for (const lt::announce_endpoint &endpoint : nativeEntry.endpoints) - { - if (!result.hasMessages) - result.hasMessages = !endpoint.message.empty(); - - if (endpoint.updating) - ++numUpdating; - else if (endpoint.fails > 0) - ++numNotWorking; - else if (nativeEntry.verified) - ++numWorking; - } -#endif - - if (numEndpoints > 0) - { - if (numUpdating > 0) - result.status = TrackerEntry::Updating; - else if (numWorking > 0) - result.status = TrackerEntry::Working; - else if (numNotWorking == numEndpoints) - result.status = TrackerEntry::NotWorking; - } - - return result; - } } const int addTorrentParamsId = qRegisterMetaType(); @@ -2327,17 +2267,6 @@ bool Session::addTorrent_impl(const std::variant &source for (int i = 0; i < addTorrentParams.filePriorities.size(); ++i) p.file_priorities[LT::toUnderlyingType(nativeIndexes[i])] = LT::toNative(addTorrentParams.filePriorities[i]); - if (isAddTrackersEnabled() && !torrentInfo.isPrivate()) - { - p.trackers.reserve(static_cast(m_additionalTrackerList.size())); - p.tracker_tiers.reserve(static_cast(m_additionalTrackerList.size())); - for (const TrackerEntry &trackerEntry : asConst(m_additionalTrackerList)) - { - p.trackers.push_back(trackerEntry.url.toStdString()); - p.tracker_tiers.push_back(trackerEntry.tier); - } - } - p.ti = torrentInfo.nativeInfo(); } else @@ -2351,6 +2280,18 @@ bool Session::addTorrent_impl(const std::variant &source p.save_path = actualSavePath.toString().toStdString(); + if (isAddTrackersEnabled() && !(hasMetadata && p.ti->priv())) + { + p.trackers.reserve(p.trackers.size() + static_cast(m_additionalTrackerList.size())); + p.tracker_tiers.reserve(p.trackers.size() + static_cast(m_additionalTrackerList.size())); + p.tracker_tiers.resize(p.trackers.size(), 0); + for (const TrackerEntry &trackerEntry : asConst(m_additionalTrackerList)) + { + p.trackers.push_back(trackerEntry.url.toStdString()); + p.tracker_tiers.push_back(trackerEntry.tier); + } + } + p.upload_limit = addTorrentParams.uploadLimit; p.download_limit = addTorrentParams.downloadLimit; @@ -2392,6 +2333,7 @@ bool Session::loadTorrent(LoadTorrentParams params) { lt::add_torrent_params &p = params.ltAddTorrentParams; + p.userdata = LTClientData(new ExtensionData); #ifndef QBT_USES_LIBTORRENT2 p.storage = customStorageConstructor; #endif @@ -4147,12 +4089,12 @@ void Session::handleTorrentTrackersAdded(TorrentImpl *const torrent, const QVect emit trackersChanged(torrent); } -void Session::handleTorrentTrackersRemoved(TorrentImpl *const torrent, const QVector &deletedTrackers) +void Session::handleTorrentTrackersRemoved(TorrentImpl *const torrent, const QStringList &deletedTrackers) { - for (const TrackerEntry &deletedTracker : deletedTrackers) - LogMsg(tr("Removed tracker from torrent. Torrent: \"%1\". Tracker: \"%2\"").arg(torrent->name(), deletedTracker.url)); + for (const QString &deletedTracker : deletedTrackers) + LogMsg(tr("Removed tracker from torrent. Torrent: \"%1\". Tracker: \"%2\"").arg(torrent->name(), deletedTracker)); emit trackersRemoved(torrent, deletedTrackers); - if (torrent->trackers().empty()) + if (torrent->trackers().isEmpty()) emit trackerlessStateChanged(torrent, true); emit trackersChanged(torrent); } @@ -4831,6 +4773,7 @@ void Session::handleAlert(const lt::alert *a) case lt::session_stats_alert::alert_type: handleSessionStatsAlert(static_cast(a)); break; + case lt::tracker_announce_alert::alert_type: case lt::tracker_error_alert::alert_type: case lt::tracker_reply_alert::alert_type: case lt::tracker_warning_alert::alert_type: @@ -5374,43 +5317,30 @@ void Session::handleTrackerAlert(const lt::tracker_alert *a) if (!torrent) return; - const QByteArray trackerURL {a->tracker_url()}; + const auto trackerURL = QString::fromUtf8(a->tracker_url()); m_updatedTrackerEntries[torrent].insert(trackerURL); if (a->type() == lt::tracker_reply_alert::alert_type) { const int numPeers = static_cast(a)->num_peers; - torrent->updatePeerCount(QString::fromUtf8(trackerURL), a->local_endpoint, numPeers); + torrent->updatePeerCount(trackerURL, a->local_endpoint, numPeers); } } void Session::processTrackerStatuses() { - QHash> updateInfos; - for (auto it = m_updatedTrackerEntries.cbegin(); it != m_updatedTrackerEntries.cend(); ++it) { - TorrentImpl *torrent = it.key(); - const QSet &updatedTrackers = it.value(); + auto torrent = static_cast(it.key()); + const QSet &updatedTrackers = it.value(); - const std::vector trackerList = torrent->nativeHandle().trackers(); - for (const lt::announce_entry &announceEntry : trackerList) - { - const auto trackerURL = QByteArray::fromRawData(announceEntry.url.c_str(), announceEntry.url.size()); - if (!updatedTrackers.contains(trackerURL)) - continue; - -#ifdef QBT_USES_LIBTORRENT2 - const TrackerEntryUpdateInfo updateInfo = getTrackerEntryUpdateInfo(announceEntry, torrent->nativeHandle().info_hashes()); -#else - const TrackerEntryUpdateInfo updateInfo = getTrackerEntryUpdateInfo(announceEntry); -#endif - if ((updateInfo.status == TrackerEntry::Working) || (updateInfo.status == TrackerEntry::NotWorking)) - updateInfos[torrent][QString::fromUtf8(trackerURL)] = updateInfo; - } + for (const QString &trackerURL : updatedTrackers) + torrent->invalidateTrackerEntry(trackerURL); } - m_updatedTrackerEntries.clear(); - if (!updateInfos.isEmpty()) - emit trackerEntriesUpdated(updateInfos); + if (!m_updatedTrackerEntries.isEmpty()) + { + emit trackerEntriesUpdated(m_updatedTrackerEntries); + m_updatedTrackerEntries.clear(); + } } diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h index f7ecb7cc2..cd5e2a3ac 100644 --- a/src/base/bittorrent/session.h +++ b/src/base/bittorrent/session.h @@ -211,12 +211,6 @@ namespace BitTorrent } disk; }; - struct TrackerEntryUpdateInfo - { - TrackerEntry::Status status = TrackerEntry::NotContacted; - bool hasMessages = false; - }; - class Session final : public QObject { Q_OBJECT @@ -518,7 +512,7 @@ namespace BitTorrent void handleTorrentChecked(TorrentImpl *const torrent); void handleTorrentFinished(TorrentImpl *const torrent); void handleTorrentTrackersAdded(TorrentImpl *const torrent, const QVector &newTrackers); - void handleTorrentTrackersRemoved(TorrentImpl *const torrent, const QVector &deletedTrackers); + void handleTorrentTrackersRemoved(TorrentImpl *const torrent, const QStringList &deletedTrackers); void handleTorrentTrackersChanged(TorrentImpl *const torrent); void handleTorrentUrlSeedsAdded(TorrentImpl *const torrent, const QVector &newUrlSeeds); void handleTorrentUrlSeedsRemoved(TorrentImpl *const torrent, const QVector &urlSeeds); @@ -563,10 +557,10 @@ namespace BitTorrent void trackerlessStateChanged(Torrent *torrent, bool trackerless); void trackersAdded(Torrent *torrent, const QVector &trackers); void trackersChanged(Torrent *torrent); - void trackersRemoved(Torrent *torrent, const QVector &trackers); + void trackersRemoved(Torrent *torrent, const QStringList &trackers); void trackerSuccess(Torrent *torrent, const QString &tracker); void trackerWarning(Torrent *torrent, const QString &tracker); - void trackerEntriesUpdated(const QHash> &updateInfos); + void trackerEntriesUpdated(const QHash> &updateInfos); private slots: void configureDeferred(); @@ -823,7 +817,7 @@ namespace BitTorrent QMap m_categories; QSet m_tags; - QHash> m_updatedTrackerEntries; + QHash> m_updatedTrackerEntries; // I/O errored torrents QSet m_recentErroredTorrents; diff --git a/src/base/bittorrent/torrent.h b/src/base/bittorrent/torrent.h index 5ea973f21..24d11499c 100644 --- a/src/base/bittorrent/torrent.h +++ b/src/base/bittorrent/torrent.h @@ -224,7 +224,6 @@ namespace BitTorrent virtual bool hasMissingFiles() const = 0; virtual bool hasError() const = 0; virtual int queuePosition() const = 0; - virtual QVector trackerURLs() const = 0; virtual QVector trackers() const = 0; virtual QVector urlSeeds() const = 0; virtual QString error() const = 0; @@ -294,8 +293,9 @@ namespace BitTorrent virtual void setPEXDisabled(bool disable) = 0; virtual void setLSDDisabled(bool disable) = 0; virtual void flushCache() const = 0; - virtual void addTrackers(const QVector &trackers) = 0; - virtual void replaceTrackers(const QVector &trackers) = 0; + virtual void addTrackers(QVector trackers) = 0; + virtual void removeTrackers(const QStringList &trackers) = 0; + virtual void replaceTrackers(QVector trackers) = 0; virtual void addUrlSeeds(const QVector &urlSeeds) = 0; virtual void removeUrlSeeds(const QVector &urlSeeds) = 0; virtual bool connectPeer(const PeerAddress &peerAddress) = 0; diff --git a/src/base/bittorrent/torrentimpl.cpp b/src/base/bittorrent/torrentimpl.cpp index 9a1573eb1..30e73ba53 100644 --- a/src/base/bittorrent/torrentimpl.cpp +++ b/src/base/bittorrent/torrentimpl.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include #include @@ -59,6 +60,7 @@ #include "base/utils/string.h" #include "common.h" #include "downloadpriority.h" +#include "extensiondata.h" #include "loadtorrentparams.h" #include "ltqbitarray.h" #include "ltqhash.h" @@ -66,7 +68,6 @@ #include "peeraddress.h" #include "peerinfo.h" #include "session.h" -#include "trackerentry.h" using namespace BitTorrent; @@ -80,14 +81,16 @@ namespace } #ifdef QBT_USES_LIBTORRENT2 - TrackerEntry fromNativeAnnounceEntry(const lt::announce_entry &nativeEntry - , const lt::info_hash_t &hashes, const QMap &trackerPeerCounts) + void updateTrackerEntry(TrackerEntry &trackerEntry, const lt::announce_entry &nativeEntry + , const lt::info_hash_t &hashes, const QMap &updateInfo) #else - TrackerEntry fromNativeAnnounceEntry(const lt::announce_entry &nativeEntry - , const QMap &trackerPeerCounts) + void updateTrackerEntry(TrackerEntry &trackerEntry, const lt::announce_entry &nativeEntry + , const QMap &updateInfo) #endif { - TrackerEntry trackerEntry {QString::fromStdString(nativeEntry.url), nativeEntry.tier}; + Q_ASSERT(trackerEntry.url == QString::fromStdString(nativeEntry.url)); + + trackerEntry.tier = nativeEntry.tier; int numUpdating = 0; int numWorking = 0; @@ -96,7 +99,6 @@ namespace QString firstErrorMessage; #ifdef QBT_USES_LIBTORRENT2 const auto numEndpoints = static_cast(nativeEntry.endpoints.size() * ((hashes.has_v1() && hashes.has_v2()) ? 2 : 1)); - trackerEntry.endpoints.reserve(static_cast(numEndpoints)); for (const lt::announce_endpoint &endpoint : nativeEntry.endpoints) { for (const auto protocolVersion : {lt::protocol_version::V1, lt::protocol_version::V2}) @@ -106,8 +108,7 @@ namespace const lt::announce_infohash &infoHash = endpoint.info_hashes[protocolVersion]; TrackerEntry::EndpointStats trackerEndpoint; - trackerEndpoint.protocolVersion = (protocolVersion == lt::protocol_version::V1) ? 1 : 2; - trackerEndpoint.numPeers = trackerPeerCounts.value(endpoint.local_endpoint, -1); + trackerEndpoint.numPeers = updateInfo.value(endpoint.local_endpoint, trackerEndpoint.numPeers); trackerEndpoint.numSeeds = infoHash.scrape_complete; trackerEndpoint.numLeeches = infoHash.scrape_incomplete; trackerEndpoint.numDownloaded = infoHash.scrape_downloaded; @@ -136,7 +137,7 @@ namespace const QString errorMessage = QString::fromLocal8Bit(infoHash.last_error.message().c_str()); trackerEndpoint.message = (!trackerMessage.isEmpty() ? trackerMessage : errorMessage); - trackerEntry.endpoints.append(trackerEndpoint); + trackerEntry.stats[endpoint.local_endpoint][(protocolVersion == lt::protocol_version::V1) ? 1 : 2] = trackerEndpoint; trackerEntry.numPeers = std::max(trackerEntry.numPeers, trackerEndpoint.numPeers); trackerEntry.numSeeds = std::max(trackerEntry.numSeeds, trackerEndpoint.numSeeds); trackerEntry.numLeeches = std::max(trackerEntry.numLeeches, trackerEndpoint.numLeeches); @@ -151,11 +152,10 @@ namespace } #else const auto numEndpoints = static_cast(nativeEntry.endpoints.size()); - trackerEntry.endpoints.reserve(static_cast(numEndpoints)); for (const lt::announce_endpoint &endpoint : nativeEntry.endpoints) { TrackerEntry::EndpointStats trackerEndpoint; - trackerEndpoint.numPeers = trackerPeerCounts.value(endpoint.local_endpoint, -1); + trackerEndpoint.numPeers = updateInfo.value(endpoint.local_endpoint, trackerEndpoint.numPeers); trackerEndpoint.numSeeds = endpoint.scrape_complete; trackerEndpoint.numLeeches = endpoint.scrape_incomplete; trackerEndpoint.numDownloaded = endpoint.scrape_downloaded; @@ -184,7 +184,7 @@ namespace const QString errorMessage = QString::fromLocal8Bit(endpoint.last_error.message().c_str()); trackerEndpoint.message = (!trackerMessage.isEmpty() ? trackerMessage : errorMessage); - trackerEntry.endpoints.append(trackerEndpoint); + trackerEntry.stats[endpoint.local_endpoint][1] = trackerEndpoint; trackerEntry.numPeers = std::max(trackerEntry.numPeers, trackerEndpoint.numPeers); trackerEntry.numSeeds = std::max(trackerEntry.numSeeds, trackerEndpoint.numSeeds); trackerEntry.numLeeches = std::max(trackerEntry.numLeeches, trackerEndpoint.numLeeches); @@ -214,8 +214,6 @@ namespace trackerEntry.message = (!firstTrackerMessage.isEmpty() ? firstTrackerMessage : firstErrorMessage); } } - - return trackerEntry; } void initializeStatus(lt::torrent_status &status, const lt::add_torrent_params ¶ms) @@ -308,6 +306,11 @@ TorrentImpl::TorrentImpl(Session *session, lt::session *nativeSession } } + const auto extensionData = static_cast(m_ltAddTorrentParams.userdata); + m_trackerEntries.reserve(static_cast(extensionData->trackers.size())); + for (const lt::announce_entry &announceEntry : extensionData->trackers) + m_trackerEntries.append({QString::fromStdString(announceEntry.url), announceEntry.tier}); + initializeStatus(m_nativeStatus, m_ltAddTorrentParams); updateState(); @@ -523,107 +526,78 @@ void TorrentImpl::setAutoManaged(const bool enable) m_nativeHandle.unset_flags(lt::torrent_flags::auto_managed); } -QVector TorrentImpl::trackerURLs() const +QVector TorrentImpl::trackers() const { - const std::vector nativeTrackers = m_nativeHandle.trackers(); - - QVector urls; - urls.reserve(static_cast(nativeTrackers.size())); + if (!m_updatedTrackerEntries.isEmpty()) + refreshTrackerEntries(); - for (const lt::announce_entry &tracker : nativeTrackers) - { - const QString trackerURL = QString::fromStdString(tracker.url); - urls.push_back(trackerURL); - } - - return urls; + return m_trackerEntries; } -QVector TorrentImpl::trackers() const +void TorrentImpl::addTrackers(QVector trackers) { - const std::vector nativeTrackers = m_nativeHandle.trackers(); + // TODO: use std::erase_if() in C++20 + trackers.erase(std::remove_if(trackers.begin(), trackers.end(), [](const TrackerEntry &entry) { return entry.url.isEmpty(); }), trackers.end()); - QVector entries; - entries.reserve(static_cast(nativeTrackers.size())); + const auto newTrackers = QSet(trackers.cbegin(), trackers.cend()) + - QSet(m_trackerEntries.cbegin(), m_trackerEntries.cend()); + if (newTrackers.isEmpty()) + return; - for (const lt::announce_entry &tracker : nativeTrackers) - { - const QString trackerURL = QString::fromStdString(tracker.url); -#ifdef QBT_USES_LIBTORRENT2 - entries << fromNativeAnnounceEntry(tracker, m_nativeHandle.info_hashes(), m_trackerPeerCounts[trackerURL]); -#else - entries << fromNativeAnnounceEntry(tracker, m_trackerPeerCounts[trackerURL]); -#endif - } + trackers = QVector(newTrackers.cbegin(), newTrackers.cend()); + for (const TrackerEntry &tracker : trackers) + m_nativeHandle.add_tracker(makeNativeAnnounceEntry(tracker.url, tracker.tier)); - return entries; + m_trackerEntries.append(trackers); + std::sort(m_trackerEntries.begin(), m_trackerEntries.end() + , [](const TrackerEntry &lhs, const TrackerEntry &rhs) { return lhs.tier < rhs.tier; }); + m_session->handleTorrentNeedSaveResumeData(this); + m_session->handleTorrentTrackersAdded(this, trackers); } -void TorrentImpl::addTrackers(const QVector &trackers) +void TorrentImpl::removeTrackers(const QStringList &trackers) { - QSet currentTrackers; - for (const lt::announce_entry &entry : m_nativeHandle.trackers()) - currentTrackers.insert({QString::fromStdString(entry.url), entry.tier}); - - QVector newTrackers; - newTrackers.reserve(trackers.size()); - - for (const TrackerEntry &tracker : trackers) + QStringList removedTrackers = trackers; + for (const QString &tracker : trackers) { - if (!currentTrackers.contains(tracker)) - { - m_nativeHandle.add_tracker(makeNativeAnnounceEntry(tracker.url, tracker.tier)); - newTrackers << tracker; - } + if (!m_trackerEntries.removeOne({tracker})) + removedTrackers.removeOne(tracker); } - if (!newTrackers.isEmpty()) + std::vector nativeTrackers; + nativeTrackers.reserve(m_trackerEntries.size()); + for (const TrackerEntry &tracker : asConst(m_trackerEntries)) + nativeTrackers.emplace_back(makeNativeAnnounceEntry(tracker.url, tracker.tier)); + + if (!removedTrackers.isEmpty()) { + m_nativeHandle.replace_trackers(nativeTrackers); m_session->handleTorrentNeedSaveResumeData(this); - m_session->handleTorrentTrackersAdded(this, newTrackers); + m_session->handleTorrentTrackersRemoved(this, removedTrackers); } } -void TorrentImpl::replaceTrackers(const QVector &trackers) +void TorrentImpl::replaceTrackers(QVector trackers) { - QVector currentTrackers = this->trackers(); - - QVector newTrackers; - newTrackers.reserve(trackers.size()); + // TODO: use std::erase_if() in C++20 + trackers.erase(std::remove_if(trackers.begin(), trackers.end(), [](const TrackerEntry &entry) { return entry.url.isEmpty(); }), trackers.end()); + std::sort(trackers.begin(), trackers.end() + , [](const TrackerEntry &lhs, const TrackerEntry &rhs) { return lhs.tier < rhs.tier; }); std::vector nativeTrackers; nativeTrackers.reserve(trackers.size()); - for (const TrackerEntry &tracker : trackers) - { nativeTrackers.emplace_back(makeNativeAnnounceEntry(tracker.url, tracker.tier)); - if (!currentTrackers.removeOne(tracker)) - newTrackers << tracker; - } - m_nativeHandle.replace_trackers(nativeTrackers); + // Clear the peer list if it's a private torrent since + // we do not want to keep connecting with peers from old tracker. + if (isPrivate()) + clearPeers(); + m_trackerEntries = trackers; m_session->handleTorrentNeedSaveResumeData(this); - - if (newTrackers.isEmpty() && currentTrackers.isEmpty()) - { - // when existing tracker reorders - m_session->handleTorrentTrackersChanged(this); - } - else - { - if (!currentTrackers.isEmpty()) - m_session->handleTorrentTrackersRemoved(this, currentTrackers); - - if (!newTrackers.isEmpty()) - m_session->handleTorrentTrackersAdded(this, newTrackers); - - // Clear the peer list if it's a private torrent since - // we do not want to keep connecting with peers from old tracker. - if (isPrivate()) - clearPeers(); - } + m_session->handleTorrentTrackersChanged(this); } QVector TorrentImpl::urlSeeds() const @@ -1511,9 +1485,43 @@ void TorrentImpl::fileSearchFinished(const Path &savePath, const PathList &fileN endReceivedMetadataHandling(savePath, fileNames); } -void TorrentImpl::updatePeerCount(const QString &trackerUrl, const lt::tcp::endpoint &endpoint, const int count) +void TorrentImpl::updatePeerCount(const QString &trackerURL, const TrackerEntry::Endpoint &endpoint, const int count) { - m_trackerPeerCounts[trackerUrl][endpoint] = count; + m_updatedTrackerEntries[trackerURL][endpoint] = count; +} + +void TorrentImpl::invalidateTrackerEntry(const QString &trackerURL) +{ + std::ignore = m_updatedTrackerEntries[trackerURL]; +} + +void TorrentImpl::refreshTrackerEntries() const +{ + const std::vector nativeTrackers = m_nativeHandle.trackers(); + Q_ASSERT(nativeTrackers.size() == m_trackerEntries.size()); + + for (TrackerEntry &trackerEntry : m_trackerEntries) + { + const auto updatedTrackerIter = m_updatedTrackerEntries.find(trackerEntry.url); + if (updatedTrackerIter == m_updatedTrackerEntries.end()) + continue; + + const auto nativeTrackerIter = std::find_if(nativeTrackers.cbegin(), nativeTrackers.cend() + , [trackerURL = trackerEntry.url.toStdString()](const lt::announce_entry &announceEntry) + { + return (announceEntry.url == trackerURL); + }); + Q_ASSERT(nativeTrackerIter != nativeTrackers.cend()); + + const lt::announce_entry &announceEntry = *nativeTrackerIter; +#ifdef QBT_USES_LIBTORRENT2 + updateTrackerEntry(trackerEntry, announceEntry, m_nativeHandle.info_hashes(), updatedTrackerIter.value()); +#else + updateTrackerEntry(trackerEntry, announceEntry, updatedTrackerIter.value()); +#endif + } + + m_updatedTrackerEntries.clear(); } std::shared_ptr TorrentImpl::nativeTorrentInfo() const diff --git a/src/base/bittorrent/torrentimpl.h b/src/base/bittorrent/torrentimpl.h index 3c5d60d95..80b297252 100644 --- a/src/base/bittorrent/torrentimpl.h +++ b/src/base/bittorrent/torrentimpl.h @@ -34,7 +34,6 @@ #include #include -#include #include #include #include @@ -55,6 +54,7 @@ #include "torrent.h" #include "torrentcontentlayout.h" #include "torrentinfo.h" +#include "trackerentry.h" namespace BitTorrent { @@ -156,7 +156,6 @@ namespace BitTorrent bool hasMissingFiles() const override; bool hasError() const override; int queuePosition() const override; - QVector trackerURLs() const override; QVector trackers() const override; QVector urlSeeds() const override; QString error() const override; @@ -221,8 +220,9 @@ namespace BitTorrent void setPEXDisabled(bool disable) override; void setLSDDisabled(bool disable) override; void flushCache() const override; - void addTrackers(const QVector &trackers) override; - void replaceTrackers(const QVector &trackers) override; + void addTrackers(QVector trackers) override; + void removeTrackers(const QStringList &trackers) override; + void replaceTrackers(QVector trackers) override; void addUrlSeeds(const QVector &urlSeeds) override; void removeUrlSeeds(const QVector &urlSeeds) override; bool connectPeer(const PeerAddress &peerAddress) override; @@ -244,13 +244,15 @@ namespace BitTorrent void saveResumeData(); void handleMoveStorageJobFinished(const Path &path, bool hasOutstandingJob); void fileSearchFinished(const Path &savePath, const PathList &fileNames); - void updatePeerCount(const QString &trackerUrl, const lt::tcp::endpoint &endpoint, int count); + void updatePeerCount(const QString &trackerURL, const TrackerEntry::Endpoint &endpoint, int count); + void invalidateTrackerEntry(const QString &trackerURL); private: using EventTrigger = std::function; std::shared_ptr nativeTorrentInfo() const; + void refreshTrackerEntries() const; void updateStatus(const lt::torrent_status &nativeStatus); void updateState(); @@ -310,7 +312,10 @@ namespace BitTorrent MaintenanceJob m_maintenanceJob = MaintenanceJob::None; - QHash> m_trackerPeerCounts; + // TODO: Use QHash once Qt5 is dropped. + using TrackerEntryUpdateInfo = QMap; + mutable QHash m_updatedTrackerEntries; + mutable QVector m_trackerEntries; FileErrorInfo m_lastFileError; // Persistent data diff --git a/src/base/bittorrent/torrentinfo.cpp b/src/base/bittorrent/torrentinfo.cpp index cd2954280..64726c0d4 100644 --- a/src/base/bittorrent/torrentinfo.cpp +++ b/src/base/bittorrent/torrentinfo.cpp @@ -275,9 +275,8 @@ QVector TorrentInfo::trackers() const QVector ret; ret.reserve(static_cast(trackers.size())); - for (const lt::announce_entry &tracker : trackers) - ret.append({QString::fromStdString(tracker.url)}); + ret.append({QString::fromStdString(tracker.url), tracker.tier}); return ret; } diff --git a/src/base/bittorrent/torrentinfo.h b/src/base/bittorrent/torrentinfo.h index 0d4127cec..71a778ad5 100644 --- a/src/base/bittorrent/torrentinfo.h +++ b/src/base/bittorrent/torrentinfo.h @@ -30,8 +30,8 @@ #include -#include #include +#include #include #include "base/3rdparty/expected.hpp" diff --git a/src/base/bittorrent/trackerentry.cpp b/src/base/bittorrent/trackerentry.cpp index 679029317..a87bfadf8 100644 --- a/src/base/bittorrent/trackerentry.cpp +++ b/src/base/bittorrent/trackerentry.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2015, 2021 Vladimir Golovnev + * Copyright (C) 2015-2022 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -28,22 +28,16 @@ #include "trackerentry.h" -#include - bool BitTorrent::operator==(const TrackerEntry &left, const TrackerEntry &right) { - return ((left.tier == right.tier) - && QUrl(left.url) == QUrl(right.url)); + return (left.url == right.url); } #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) std::size_t BitTorrent::qHash(const TrackerEntry &key, const std::size_t seed) -{ - return qHashMulti(seed, key.url, key.tier); -} #else uint BitTorrent::qHash(const TrackerEntry &key, const uint seed) +#endif { - return (::qHash(key.url, seed) ^ ::qHash(key.tier)); + return ::qHash(key.url, seed); } -#endif diff --git a/src/base/bittorrent/trackerentry.h b/src/base/bittorrent/trackerentry.h index 61649fd7b..0a6d9193a 100644 --- a/src/base/bittorrent/trackerentry.h +++ b/src/base/bittorrent/trackerentry.h @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2015, 2021 Vladimir Golovnev + * Copyright (C) 2015-2022 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -28,14 +28,19 @@ #pragma once +#include + #include +#include +#include #include -#include namespace BitTorrent { struct TrackerEntry { + using Endpoint = lt::tcp::endpoint; + enum Status { NotContacted = 1, @@ -46,8 +51,6 @@ namespace BitTorrent struct EndpointStats { - int protocolVersion = 1; - Status status = NotContacted; int numPeers = -1; int numSeeds = -1; @@ -59,7 +62,8 @@ namespace BitTorrent QString url {}; int tier = 0; - QVector endpoints {}; + // TODO: Use QHash> once Qt5 is dropped. + QMap> stats {}; // Deprecated fields Status status = NotContacted; diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index 6c93baf73..4c5aea10a 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -1495,6 +1495,7 @@ void MainWindow::showFiltersSidebar(const bool show) connect(BitTorrent::Session::instance(), &BitTorrent::Session::trackersAdded, m_transferListFiltersWidget, &TransferListFiltersWidget::addTrackers); connect(BitTorrent::Session::instance(), &BitTorrent::Session::trackersRemoved, m_transferListFiltersWidget, &TransferListFiltersWidget::removeTrackers); + connect(BitTorrent::Session::instance(), &BitTorrent::Session::trackersChanged, m_transferListFiltersWidget, &TransferListFiltersWidget::refreshTrackers); connect(BitTorrent::Session::instance(), &BitTorrent::Session::trackerlessStateChanged, m_transferListFiltersWidget, &TransferListFiltersWidget::changeTrackerless); connect(BitTorrent::Session::instance(), &BitTorrent::Session::trackerEntriesUpdated, m_transferListFiltersWidget, &TransferListFiltersWidget::trackerEntriesUpdated); } diff --git a/src/gui/properties/trackerlistwidget.cpp b/src/gui/properties/trackerlistwidget.cpp index c68a9c7bc..f2a80dcaf 100644 --- a/src/gui/properties/trackerlistwidget.cpp +++ b/src/gui/properties/trackerlistwidget.cpp @@ -473,18 +473,7 @@ void TrackerListWidget::deleteSelectedTrackers() delete item; } - // Iterate over the trackers and remove the selected ones - const QVector trackers = torrent->trackers(); - QVector remainingTrackers; - remainingTrackers.reserve(trackers.size()); - - for (const BitTorrent::TrackerEntry &entry : trackers) - { - if (!urlsToRemove.contains(entry.url)) - remainingTrackers.push_back(entry); - } - - torrent->replaceTrackers(remainingTrackers); + torrent->removeTrackers(urlsToRemove); if (!torrent->isPaused()) torrent->forceReannounce(); diff --git a/src/gui/transferlistfilterswidget.cpp b/src/gui/transferlistfilterswidget.cpp index 09dc90b3a..65f6ba1db 100644 --- a/src/gui/transferlistfilterswidget.cpp +++ b/src/gui/transferlistfilterswidget.cpp @@ -38,6 +38,7 @@ #include #include +#include "base/algorithm.h" #include "base/bittorrent/session.h" #include "base/bittorrent/torrent.h" #include "base/global.h" @@ -376,7 +377,8 @@ TrackerFiltersList::TrackerFiltersList(QWidget *parent, TransferListWidget *tran auto *warningTracker = new QListWidgetItem(this); warningTracker->setData(Qt::DisplayRole, tr("Warning (0)")); warningTracker->setData(Qt::DecorationRole, style()->standardIcon(QStyle::SP_MessageBoxWarning)); - m_trackers[NULL_HOST] = {}; + + m_trackers[NULL_HOST] = {{}, noTracker}; setCurrentRow(0, QItemSelectionModel::SelectCurrent); toggleFilter(Preferences::instance()->getTrackerFilterState()); @@ -388,6 +390,70 @@ TrackerFiltersList::~TrackerFiltersList() Utils::Fs::removeFile(iconPath); } +void TrackerFiltersList::addTrackers(const BitTorrent::Torrent *torrent, const QVector &trackers) +{ + const BitTorrent::TorrentID torrentID = torrent->id(); + for (const BitTorrent::TrackerEntry &tracker : trackers) + addItem(tracker.url, torrentID); +} + +void TrackerFiltersList::removeTrackers(const BitTorrent::Torrent *torrent, const QStringList &trackers) +{ + const BitTorrent::TorrentID torrentID = torrent->id(); + for (const QString &tracker : trackers) + removeItem(tracker, torrentID); +} + +void TrackerFiltersList::refreshTrackers(const BitTorrent::Torrent *torrent) +{ + const BitTorrent::TorrentID torrentID = torrent->id(); + + m_errors.remove(torrentID); + m_warnings.remove(torrentID); + + Algorithm::removeIf(m_trackers, [this, &torrentID](const QString &host, TrackerData &trackerData) + { + QSet &torrentIDs = trackerData.torrents; + if (!torrentIDs.remove(torrentID)) + return false; + + QListWidgetItem *trackerItem = trackerData.item; + + if (!host.isEmpty() && torrentIDs.isEmpty()) + { + if (currentItem() == trackerItem) + setCurrentRow(0, QItemSelectionModel::SelectCurrent); + delete trackerItem; + return true; + } + + trackerItem->setText(u"%1 (%2)"_qs.arg((host.isEmpty() ? tr("Trackerless") : host), QString::number(torrentIDs.size()))); + return false; + }); + + const QVector trackerEntries = torrent->trackers(); + const bool isTrackerless = trackerEntries.isEmpty(); + if (isTrackerless) + { + addItem(NULL_HOST, torrentID); + } + else + { + for (const BitTorrent::TrackerEntry &trackerEntry : trackerEntries) + addItem(trackerEntry.url, torrentID); + } + + updateGeometry(); +} + +void TrackerFiltersList::changeTrackerless(const BitTorrent::Torrent *torrent, const bool trackerless) +{ + if (trackerless) + addItem(NULL_HOST, torrent->id()); + else + removeItem(NULL_HOST, torrent->id()); +} + void TrackerFiltersList::addItem(const QString &tracker, const BitTorrent::TorrentID &id) { const QString host = getHost(tracker); @@ -400,9 +466,7 @@ void TrackerFiltersList::addItem(const QString &tracker, const BitTorrent::Torre if (trackersIt->torrents.contains(id)) return; - trackerItem = (host == NULL_HOST) - ? item(TRACKERLESS_ROW) - : trackersIt->item; + trackerItem = trackersIt->item; } else { @@ -421,15 +485,7 @@ void TrackerFiltersList::addItem(const QString &tracker, const BitTorrent::Torre QSet &torrentIDs = trackersIt->torrents; torrentIDs.insert(id); - if (host == NULL_HOST) - { - trackerItem->setText(tr("Trackerless (%1)").arg(torrentIDs.size())); - if (currentRow() == TRACKERLESS_ROW) - applyFilter(TRACKERLESS_ROW); - return; - } - - trackerItem->setText(u"%1 (%2)"_qs.arg(host, QString::number(torrentIDs.size()))); + trackerItem->setText(u"%1 (%2)"_qs.arg(((host == NULL_HOST) ? tr("Trackerless") : host), QString::number(torrentIDs.size()))); if (exists) { if (item(currentRow()) == trackerItem) @@ -496,7 +552,7 @@ void TrackerFiltersList::removeItem(const QString &trackerURL, const BitTorrent: trackerItem = m_trackers.value(host).item; - if (torrentIDs.empty()) + if (torrentIDs.isEmpty()) { if (currentItem() == trackerItem) setCurrentRow(0, QItemSelectionModel::SelectCurrent); @@ -521,14 +577,6 @@ void TrackerFiltersList::removeItem(const QString &trackerURL, const BitTorrent: applyFilter(currentRow()); } -void TrackerFiltersList::changeTrackerless(const bool trackerless, const BitTorrent::TorrentID &id) -{ - if (trackerless) - addItem(NULL_HOST, id); - else - removeItem(NULL_HOST, id); -} - void TrackerFiltersList::setDownloadTrackerFavicon(bool value) { if (value == m_downloadTrackerFavicon) return; @@ -549,49 +597,51 @@ void TrackerFiltersList::setDownloadTrackerFavicon(bool value) } } -void TrackerFiltersList::handleTrackerEntriesUpdated(const QHash> &updateInfos) +void TrackerFiltersList::handleTrackerEntriesUpdated(const QHash> &updateInfos) { for (auto torrentsIt = updateInfos.cbegin(); torrentsIt != updateInfos.cend(); ++torrentsIt) { - const BitTorrent::TorrentID id = torrentsIt.key()->id(); - const QHash &infos = torrentsIt.value(); + const BitTorrent::Torrent *torrent = torrentsIt.key(); + const QSet &trackerURLs = torrentsIt.value(); + const BitTorrent::TorrentID id = torrent->id(); auto errorHashesIt = m_errors.find(id); auto warningHashesIt = m_warnings.find(id); - for (auto trackerIt = infos.cbegin(); trackerIt != infos.cend(); ++trackerIt) + const QVector trackers = torrent->trackers(); + for (const BitTorrent::TrackerEntry &trackerEntry : trackers) { - const QString &trackerURL = trackerIt.key(); - const BitTorrent::TrackerEntryUpdateInfo &updateInfo = trackerIt.value(); + if (!trackerURLs.contains(trackerEntry.url)) + continue; - if (updateInfo.status == BitTorrent::TrackerEntry::Working) + if (trackerEntry.status == BitTorrent::TrackerEntry::Working) { if (errorHashesIt != m_errors.end()) { QSet &errored = errorHashesIt.value(); - errored.remove(trackerURL); + errored.remove(trackerEntry.url); } - if (!updateInfo.hasMessages) + if (trackerEntry.message.isEmpty()) { if (warningHashesIt != m_warnings.end()) { QSet &warned = *warningHashesIt; - warned.remove(trackerURL); + warned.remove(trackerEntry.url); } } else { if (warningHashesIt == m_warnings.end()) warningHashesIt = m_warnings.insert(id, {}); - warningHashesIt.value().insert(trackerURL); + warningHashesIt.value().insert(trackerEntry.url); } } - else if (updateInfo.status == BitTorrent::TrackerEntry::NotWorking) + else if (trackerEntry.status == BitTorrent::TrackerEntry::NotWorking) { if (errorHashesIt == m_errors.end()) errorHashesIt = m_errors.insert(id, {}); - errorHashesIt.value().insert(trackerURL); + errorHashesIt.value().insert(trackerEntry.url); } } @@ -680,13 +730,13 @@ void TrackerFiltersList::applyFilter(const int row) void TrackerFiltersList::handleNewTorrent(BitTorrent::Torrent *const torrent) { - const BitTorrent::TorrentID torrentID {torrent->id()}; - const QVector trackerURLs {torrent->trackerURLs()}; - for (const QString &trackerURL : trackerURLs) - addItem(trackerURL, torrentID); + const BitTorrent::TorrentID torrentID = torrent->id(); + const QVector trackers = torrent->trackers(); + for (const BitTorrent::TrackerEntry &tracker : trackers) + addItem(tracker.url, torrentID); // Check for trackerless torrent - if (trackerURLs.isEmpty()) + if (trackers.isEmpty()) addItem(NULL_HOST, torrentID); item(ALL_ROW)->setText(tr("All (%1)", "this is for the tracker filter").arg(++m_totalTorrents)); @@ -694,13 +744,13 @@ void TrackerFiltersList::handleNewTorrent(BitTorrent::Torrent *const torrent) void TrackerFiltersList::torrentAboutToBeDeleted(BitTorrent::Torrent *const torrent) { - const BitTorrent::TorrentID torrentID {torrent->id()}; - const QVector trackerURLs {torrent->trackerURLs()}; - for (const QString &trackerURL : trackerURLs) - removeItem(trackerURL, torrentID); + const BitTorrent::TorrentID torrentID = torrent->id(); + const QVector trackers = torrent->trackers(); + for (const BitTorrent::TrackerEntry &tracker : trackers) + removeItem(tracker.url, torrentID); // Check for trackerless torrent - if (trackerURLs.isEmpty()) + if (trackers.isEmpty()) removeItem(NULL_HOST, torrentID); item(ALL_ROW)->setText(tr("All (%1)", "this is for the tracker filter").arg(--m_totalTorrents)); @@ -838,22 +888,25 @@ void TransferListFiltersWidget::setDownloadTrackerFavicon(bool value) void TransferListFiltersWidget::addTrackers(const BitTorrent::Torrent *torrent, const QVector &trackers) { - for (const BitTorrent::TrackerEntry &tracker : trackers) - m_trackerFilters->addItem(tracker.url, torrent->id()); + m_trackerFilters->addTrackers(torrent, trackers); } -void TransferListFiltersWidget::removeTrackers(const BitTorrent::Torrent *torrent, const QVector &trackers) +void TransferListFiltersWidget::removeTrackers(const BitTorrent::Torrent *torrent, const QStringList &trackers) { - for (const BitTorrent::TrackerEntry &tracker : trackers) - m_trackerFilters->removeItem(tracker.url, torrent->id()); + m_trackerFilters->removeTrackers(torrent, trackers); +} + +void TransferListFiltersWidget::refreshTrackers(const BitTorrent::Torrent *torrent) +{ + m_trackerFilters->refreshTrackers(torrent); } void TransferListFiltersWidget::changeTrackerless(const BitTorrent::Torrent *torrent, const bool trackerless) { - m_trackerFilters->changeTrackerless(trackerless, torrent->id()); + m_trackerFilters->changeTrackerless(torrent, trackerless); } -void TransferListFiltersWidget::trackerEntriesUpdated(const QHash> &updateInfos) +void TransferListFiltersWidget::trackerEntriesUpdated(const QHash> &updateInfos) { m_trackerFilters->handleTrackerEntriesUpdated(updateInfos); } diff --git a/src/gui/transferlistfilterswidget.h b/src/gui/transferlistfilterswidget.h index d2c3b1508..9b78ab651 100644 --- a/src/gui/transferlistfilterswidget.h +++ b/src/gui/transferlistfilterswidget.h @@ -124,12 +124,12 @@ public: TrackerFiltersList(QWidget *parent, TransferListWidget *transferList, bool downloadFavicon); ~TrackerFiltersList() override; - // Redefine addItem() to make sure the list stays sorted - void addItem(const QString &tracker, const BitTorrent::TorrentID &id); - void removeItem(const QString &trackerURL, const BitTorrent::TorrentID &id); - void changeTrackerless(bool trackerless, const BitTorrent::TorrentID &id); + void addTrackers(const BitTorrent::Torrent *torrent, const QVector &trackers); + void removeTrackers(const BitTorrent::Torrent *torrent, const QStringList &trackers); + void refreshTrackers(const BitTorrent::Torrent *torrent); + void changeTrackerless(const BitTorrent::Torrent *torrent, bool trackerless); + void handleTrackerEntriesUpdated(const QHash> &updateInfos); void setDownloadTrackerFavicon(bool value); - void handleTrackerEntriesUpdated(const QHash> &updateInfos); private slots: void handleFavicoDownloadFinished(const Net::DownloadResult &result); @@ -141,6 +141,9 @@ private: void applyFilter(int row) override; void handleNewTorrent(BitTorrent::Torrent *const torrent) override; void torrentAboutToBeDeleted(BitTorrent::Torrent *const torrent) override; + + void addItem(const QString &tracker, const BitTorrent::TorrentID &id); + void removeItem(const QString &trackerURL, const BitTorrent::TorrentID &id); QString trackerFromRow(int row) const; int rowFromTracker(const QString &tracker) const; QSet getTorrentIDs(int row) const; @@ -174,9 +177,10 @@ public: public slots: void addTrackers(const BitTorrent::Torrent *torrent, const QVector &trackers); - void removeTrackers(const BitTorrent::Torrent *torrent, const QVector &trackers); + void removeTrackers(const BitTorrent::Torrent *torrent, const QStringList &trackers); + void refreshTrackers(const BitTorrent::Torrent *torrent); void changeTrackerless(const BitTorrent::Torrent *torrent, bool trackerless); - void trackerEntriesUpdated(const QHash> &updateInfos); + void trackerEntriesUpdated(const QHash> &updateInfos); private slots: void onCategoryFilterStateChanged(bool enabled); diff --git a/src/webui/api/torrentscontroller.cpp b/src/webui/api/torrentscontroller.cpp index 3b1cc284b..875933d0c 100644 --- a/src/webui/api/torrentscontroller.cpp +++ b/src/webui/api/torrentscontroller.cpp @@ -767,8 +767,8 @@ void TorrentsController::editTrackerAction() if (!torrent) throw APIError(APIErrorType::NotFound); - const QUrl origTrackerUrl(origUrl); - const QUrl newTrackerUrl(newUrl); + const QUrl origTrackerUrl {origUrl}; + const QUrl newTrackerUrl {newUrl}; if (origTrackerUrl == newTrackerUrl) return; if (!newTrackerUrl.isValid()) @@ -778,7 +778,7 @@ void TorrentsController::editTrackerAction() bool match = false; for (BitTorrent::TrackerEntry &tracker : trackers) { - const QUrl trackerUrl(tracker.url); + const QUrl trackerUrl {tracker.url}; if (trackerUrl == newTrackerUrl) throw APIError(APIErrorType::Conflict, u"New tracker URL already exists"_qs); if (trackerUrl == origTrackerUrl) @@ -806,20 +806,7 @@ void TorrentsController::removeTrackersAction() throw APIError(APIErrorType::NotFound); const QStringList urls = params()[u"urls"_qs].split(u'|'); - - const QVector trackers = torrent->trackers(); - QVector remainingTrackers; - remainingTrackers.reserve(trackers.size()); - for (const BitTorrent::TrackerEntry &entry : trackers) - { - if (!urls.contains(entry.url)) - remainingTrackers.push_back(entry); - } - - if (remainingTrackers.size() == trackers.size()) - throw APIError(APIErrorType::Conflict, u"No trackers were removed"_qs); - - torrent->replaceTrackers(remainingTrackers); + torrent->removeTrackers(urls); if (!torrent->isPaused()) torrent->forceReannounce();