Browse Source

Revamp tracker entries handling

PR #17017.
adaptive-webui-19844
Vladimir Golovnev 3 years ago committed by GitHub
parent
commit
7e0cd223fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      src/base/CMakeLists.txt
  2. 1
      src/base/base.pri
  3. 45
      src/base/bittorrent/extensiondata.h
  4. 11
      src/base/bittorrent/magneturi.cpp
  5. 7
      src/base/bittorrent/nativesessionextension.cpp
  6. 12
      src/base/bittorrent/nativesessionextension.h
  7. 13
      src/base/bittorrent/nativetorrentextension.cpp
  8. 8
      src/base/bittorrent/nativetorrentextension.h
  9. 130
      src/base/bittorrent/session.cpp
  10. 14
      src/base/bittorrent/session.h
  11. 6
      src/base/bittorrent/torrent.h
  12. 186
      src/base/bittorrent/torrentimpl.cpp
  13. 17
      src/base/bittorrent/torrentimpl.h
  14. 3
      src/base/bittorrent/torrentinfo.cpp
  15. 2
      src/base/bittorrent/torrentinfo.h
  16. 14
      src/base/bittorrent/trackerentry.cpp
  17. 14
      src/base/bittorrent/trackerentry.h
  18. 1
      src/gui/mainwindow.cpp
  19. 13
      src/gui/properties/trackerlistwidget.cpp
  20. 157
      src/gui/transferlistfilterswidget.cpp
  21. 18
      src/gui/transferlistfilterswidget.h
  22. 21
      src/webui/api/torrentscontroller.cpp

1
src/base/CMakeLists.txt

@ -13,6 +13,7 @@ add_library(qbt_base STATIC @@ -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

1
src/base/base.pri

@ -12,6 +12,7 @@ HEADERS += \ @@ -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 \

45
src/base/bittorrent/extensiondata.h

@ -0,0 +1,45 @@ @@ -0,0 +1,45 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2022 Vladimir Golovnev <glassez@yandex.ru>
*
* 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 <vector>
#include <libtorrent/announce_entry.hpp>
#ifdef QBT_USES_LIBTORRENT2
#include <libtorrent/client_data.hpp>
using LTClientData = lt::client_data_t;
#else
using LTClientData = void *;
#endif
struct ExtensionData
{
std::vector<lt::announce_entry> trackers;
};

11
src/base/bittorrent/magneturi.cpp

@ -100,8 +100,15 @@ MagnetUri::MagnetUri(const QString &source) @@ -100,8 +100,15 @@ MagnetUri::MagnetUri(const QString &source)
m_name = QString::fromStdString(m_addTorrentParams.name);
m_trackers.reserve(static_cast<decltype(m_trackers)::size_type>(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<decltype(m_urlSeeds)::size_type>(m_addTorrentParams.url_seeds.size()));
for (const std::string &urlSeed : m_addTorrentParams.url_seeds)

7
src/base/bittorrent/nativesessionextension.cpp

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2020 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2020-2022 Vladimir Golovnev <glassez@yandex.ru>
*
* 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 @@ @@ -30,6 +30,7 @@
#include <libtorrent/alert_types.hpp>
#include "extensiondata.h"
#include "nativetorrentextension.h"
namespace
@ -49,9 +50,9 @@ lt::feature_flags_t NativeSessionExtension::implemented_features() @@ -49,9 +50,9 @@ lt::feature_flags_t NativeSessionExtension::implemented_features()
return alert_feature;
}
std::shared_ptr<lt::torrent_plugin> NativeSessionExtension::new_torrent(const lt::torrent_handle &torrentHandle, ClientData)
std::shared_ptr<lt::torrent_plugin> NativeSessionExtension::new_torrent(const lt::torrent_handle &torrentHandle, LTClientData clientData)
{
return std::make_shared<NativeTorrentExtension>(torrentHandle);
return std::make_shared<NativeTorrentExtension>(torrentHandle, static_cast<ExtensionData *>(clientData));
}
void NativeSessionExtension::on_alert(const lt::alert *alert)

12
src/base/bittorrent/nativesessionextension.h

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2020 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2020-2022 Vladimir Golovnev <glassez@yandex.ru>
*
* 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 @@ @@ -30,15 +30,11 @@
#include <libtorrent/extensions.hpp>
#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<lt::torrent_plugin> new_torrent(const lt::torrent_handle &torrentHandle, ClientData) override;
std::shared_ptr<lt::torrent_plugin> new_torrent(const lt::torrent_handle &torrentHandle, LTClientData clientData) override;
void on_alert(const lt::alert *alert) override;
};

13
src/base/bittorrent/nativetorrentextension.cpp

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2020 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2020-2022 Vladimir Golovnev <glassez@yandex.ru>
*
* 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 @@ -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()

8
src/base/bittorrent/nativetorrentextension.h

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2020 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2020-2022 Vladimir Golovnev <glassez@yandex.ru>
*
* 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 @@ @@ -31,10 +31,13 @@
#include <libtorrent/extensions.hpp>
#include <libtorrent/torrent_handle.hpp>
#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: @@ -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;
};

130
src/base/bittorrent/session.cpp

@ -95,6 +95,7 @@ @@ -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 @@ -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<qsizetype>(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<qsizetype>(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<AddTorrentParams>();
@ -2327,17 +2267,6 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source @@ -2327,17 +2267,6 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &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<std::size_t>(m_additionalTrackerList.size()));
p.tracker_tiers.reserve(static_cast<std::size_t>(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<MagnetUri, TorrentInfo> &source @@ -2351,6 +2280,18 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
p.save_path = actualSavePath.toString().toStdString();
if (isAddTrackersEnabled() && !(hasMetadata && p.ti->priv()))
{
p.trackers.reserve(p.trackers.size() + static_cast<std::size_t>(m_additionalTrackerList.size()));
p.tracker_tiers.reserve(p.trackers.size() + static_cast<std::size_t>(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) @@ -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 @@ -4147,12 +4089,12 @@ void Session::handleTorrentTrackersAdded(TorrentImpl *const torrent, const QVect
emit trackersChanged(torrent);
}
void Session::handleTorrentTrackersRemoved(TorrentImpl *const torrent, const QVector<TrackerEntry> &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) @@ -4831,6 +4773,7 @@ void Session::handleAlert(const lt::alert *a)
case lt::session_stats_alert::alert_type:
handleSessionStatsAlert(static_cast<const lt::session_stats_alert*>(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) @@ -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<const lt::tracker_reply_alert *>(a)->num_peers;
torrent->updatePeerCount(QString::fromUtf8(trackerURL), a->local_endpoint, numPeers);
torrent->updatePeerCount(trackerURL, a->local_endpoint, numPeers);
}
}
void Session::processTrackerStatuses()
{
QHash<Torrent *, QHash<QString, TrackerEntryUpdateInfo>> updateInfos;
for (auto it = m_updatedTrackerEntries.cbegin(); it != m_updatedTrackerEntries.cend(); ++it)
{
TorrentImpl *torrent = it.key();
const QSet<QByteArray> &updatedTrackers = it.value();
auto torrent = static_cast<TorrentImpl *>(it.key());
const QSet<QString> &updatedTrackers = it.value();
const std::vector<lt::announce_entry> 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();
}
}

14
src/base/bittorrent/session.h

@ -211,12 +211,6 @@ namespace BitTorrent @@ -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 @@ -518,7 +512,7 @@ namespace BitTorrent
void handleTorrentChecked(TorrentImpl *const torrent);
void handleTorrentFinished(TorrentImpl *const torrent);
void handleTorrentTrackersAdded(TorrentImpl *const torrent, const QVector<TrackerEntry> &newTrackers);
void handleTorrentTrackersRemoved(TorrentImpl *const torrent, const QVector<TrackerEntry> &deletedTrackers);
void handleTorrentTrackersRemoved(TorrentImpl *const torrent, const QStringList &deletedTrackers);
void handleTorrentTrackersChanged(TorrentImpl *const torrent);
void handleTorrentUrlSeedsAdded(TorrentImpl *const torrent, const QVector<QUrl> &newUrlSeeds);
void handleTorrentUrlSeedsRemoved(TorrentImpl *const torrent, const QVector<QUrl> &urlSeeds);
@ -563,10 +557,10 @@ namespace BitTorrent @@ -563,10 +557,10 @@ namespace BitTorrent
void trackerlessStateChanged(Torrent *torrent, bool trackerless);
void trackersAdded(Torrent *torrent, const QVector<TrackerEntry> &trackers);
void trackersChanged(Torrent *torrent);
void trackersRemoved(Torrent *torrent, const QVector<TrackerEntry> &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<Torrent *, QHash<QString, TrackerEntryUpdateInfo>> &updateInfos);
void trackerEntriesUpdated(const QHash<Torrent *, QSet<QString>> &updateInfos);
private slots:
void configureDeferred();
@ -823,7 +817,7 @@ namespace BitTorrent @@ -823,7 +817,7 @@ namespace BitTorrent
QMap<QString, CategoryOptions> m_categories;
QSet<QString> m_tags;
QHash<TorrentImpl *, QSet<QByteArray>> m_updatedTrackerEntries;
QHash<Torrent *, QSet<QString>> m_updatedTrackerEntries;
// I/O errored torrents
QSet<TorrentID> m_recentErroredTorrents;

6
src/base/bittorrent/torrent.h

@ -224,7 +224,6 @@ namespace BitTorrent @@ -224,7 +224,6 @@ namespace BitTorrent
virtual bool hasMissingFiles() const = 0;
virtual bool hasError() const = 0;
virtual int queuePosition() const = 0;
virtual QVector<QString> trackerURLs() const = 0;
virtual QVector<TrackerEntry> trackers() const = 0;
virtual QVector<QUrl> urlSeeds() const = 0;
virtual QString error() const = 0;
@ -294,8 +293,9 @@ namespace BitTorrent @@ -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<TrackerEntry> &trackers) = 0;
virtual void replaceTrackers(const QVector<TrackerEntry> &trackers) = 0;
virtual void addTrackers(QVector<TrackerEntry> trackers) = 0;
virtual void removeTrackers(const QStringList &trackers) = 0;
virtual void replaceTrackers(QVector<TrackerEntry> trackers) = 0;
virtual void addUrlSeeds(const QVector<QUrl> &urlSeeds) = 0;
virtual void removeUrlSeeds(const QVector<QUrl> &urlSeeds) = 0;
virtual bool connectPeer(const PeerAddress &peerAddress) = 0;

186
src/base/bittorrent/torrentimpl.cpp

@ -48,6 +48,7 @@ @@ -48,6 +48,7 @@
#include <QByteArray>
#include <QDebug>
#include <QFile>
#include <QSet>
#include <QStringList>
#include <QUrl>
@ -59,6 +60,7 @@ @@ -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 @@ @@ -66,7 +68,6 @@
#include "peeraddress.h"
#include "peerinfo.h"
#include "session.h"
#include "trackerentry.h"
using namespace BitTorrent;
@ -80,14 +81,16 @@ namespace @@ -80,14 +81,16 @@ namespace
}
#ifdef QBT_USES_LIBTORRENT2
TrackerEntry fromNativeAnnounceEntry(const lt::announce_entry &nativeEntry
, const lt::info_hash_t &hashes, const QMap<lt::tcp::endpoint, int> &trackerPeerCounts)
void updateTrackerEntry(TrackerEntry &trackerEntry, const lt::announce_entry &nativeEntry
, const lt::info_hash_t &hashes, const QMap<TrackerEntry::Endpoint, int> &updateInfo)
#else
TrackerEntry fromNativeAnnounceEntry(const lt::announce_entry &nativeEntry
, const QMap<lt::tcp::endpoint, int> &trackerPeerCounts)
void updateTrackerEntry(TrackerEntry &trackerEntry, const lt::announce_entry &nativeEntry
, const QMap<TrackerEntry::Endpoint, int> &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 @@ -96,7 +99,6 @@ namespace
QString firstErrorMessage;
#ifdef QBT_USES_LIBTORRENT2
const auto numEndpoints = static_cast<qsizetype>(nativeEntry.endpoints.size() * ((hashes.has_v1() && hashes.has_v2()) ? 2 : 1));
trackerEntry.endpoints.reserve(static_cast<decltype(trackerEntry.endpoints)::size_type>(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 @@ -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 @@ -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 @@ -151,11 +152,10 @@ namespace
}
#else
const auto numEndpoints = static_cast<qsizetype>(nativeEntry.endpoints.size());
trackerEntry.endpoints.reserve(static_cast<decltype(trackerEntry.endpoints)::size_type>(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 @@ -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 @@ -214,8 +214,6 @@ namespace
trackerEntry.message = (!firstTrackerMessage.isEmpty() ? firstTrackerMessage : firstErrorMessage);
}
}
return trackerEntry;
}
void initializeStatus(lt::torrent_status &status, const lt::add_torrent_params &params)
@ -308,6 +306,11 @@ TorrentImpl::TorrentImpl(Session *session, lt::session *nativeSession @@ -308,6 +306,11 @@ TorrentImpl::TorrentImpl(Session *session, lt::session *nativeSession
}
}
const auto extensionData = static_cast<ExtensionData *>(m_ltAddTorrentParams.userdata);
m_trackerEntries.reserve(static_cast<decltype(m_trackerEntries)::size_type>(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) @@ -523,107 +526,78 @@ void TorrentImpl::setAutoManaged(const bool enable)
m_nativeHandle.unset_flags(lt::torrent_flags::auto_managed);
}
QVector<QString> TorrentImpl::trackerURLs() const
QVector<TrackerEntry> TorrentImpl::trackers() const
{
const std::vector<lt::announce_entry> nativeTrackers = m_nativeHandle.trackers();
QVector<QString> urls;
urls.reserve(static_cast<decltype(urls)::size_type>(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<TrackerEntry> TorrentImpl::trackers() const
void TorrentImpl::addTrackers(QVector<TrackerEntry> trackers)
{
const std::vector<lt::announce_entry> 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<TrackerEntry> entries;
entries.reserve(static_cast<decltype(entries)::size_type>(nativeTrackers.size()));
const auto newTrackers = QSet<TrackerEntry>(trackers.cbegin(), trackers.cend())
- QSet<TrackerEntry>(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<TrackerEntry>(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<TrackerEntry> &trackers)
void TorrentImpl::removeTrackers(const QStringList &trackers)
{
QSet<TrackerEntry> currentTrackers;
for (const lt::announce_entry &entry : m_nativeHandle.trackers())
currentTrackers.insert({QString::fromStdString(entry.url), entry.tier});
QVector<TrackerEntry> 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<lt::announce_entry> 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<TrackerEntry> &trackers)
void TorrentImpl::replaceTrackers(QVector<TrackerEntry> trackers)
{
QVector<TrackerEntry> currentTrackers = this->trackers();
QVector<TrackerEntry> 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<lt::announce_entry> 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<QUrl> TorrentImpl::urlSeeds() const
@ -1511,9 +1485,43 @@ void TorrentImpl::fileSearchFinished(const Path &savePath, const PathList &fileN @@ -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<lt::announce_entry> 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<const libtorrent::torrent_info> TorrentImpl::nativeTorrentInfo() const

17
src/base/bittorrent/torrentimpl.h

@ -34,7 +34,6 @@ @@ -34,7 +34,6 @@
#include <libtorrent/add_torrent_params.hpp>
#include <libtorrent/fwd.hpp>
#include <libtorrent/socket.hpp>
#include <libtorrent/torrent_handle.hpp>
#include <libtorrent/torrent_info.hpp>
#include <libtorrent/torrent_status.hpp>
@ -55,6 +54,7 @@ @@ -55,6 +54,7 @@
#include "torrent.h"
#include "torrentcontentlayout.h"
#include "torrentinfo.h"
#include "trackerentry.h"
namespace BitTorrent
{
@ -156,7 +156,6 @@ namespace BitTorrent @@ -156,7 +156,6 @@ namespace BitTorrent
bool hasMissingFiles() const override;
bool hasError() const override;
int queuePosition() const override;
QVector<QString> trackerURLs() const override;
QVector<TrackerEntry> trackers() const override;
QVector<QUrl> urlSeeds() const override;
QString error() const override;
@ -221,8 +220,9 @@ namespace BitTorrent @@ -221,8 +220,9 @@ namespace BitTorrent
void setPEXDisabled(bool disable) override;
void setLSDDisabled(bool disable) override;
void flushCache() const override;
void addTrackers(const QVector<TrackerEntry> &trackers) override;
void replaceTrackers(const QVector<TrackerEntry> &trackers) override;
void addTrackers(QVector<TrackerEntry> trackers) override;
void removeTrackers(const QStringList &trackers) override;
void replaceTrackers(QVector<TrackerEntry> trackers) override;
void addUrlSeeds(const QVector<QUrl> &urlSeeds) override;
void removeUrlSeeds(const QVector<QUrl> &urlSeeds) override;
bool connectPeer(const PeerAddress &peerAddress) override;
@ -244,13 +244,15 @@ namespace BitTorrent @@ -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<void ()>;
std::shared_ptr<const lt::torrent_info> nativeTorrentInfo() const;
void refreshTrackerEntries() const;
void updateStatus(const lt::torrent_status &nativeStatus);
void updateState();
@ -310,7 +312,10 @@ namespace BitTorrent @@ -310,7 +312,10 @@ namespace BitTorrent
MaintenanceJob m_maintenanceJob = MaintenanceJob::None;
QHash<QString, QMap<lt::tcp::endpoint, int>> m_trackerPeerCounts;
// TODO: Use QHash<TrackerEntry::Endpoint, int> once Qt5 is dropped.
using TrackerEntryUpdateInfo = QMap<TrackerEntry::Endpoint, int>;
mutable QHash<QString, TrackerEntryUpdateInfo> m_updatedTrackerEntries;
mutable QVector<TrackerEntry> m_trackerEntries;
FileErrorInfo m_lastFileError;
// Persistent data

3
src/base/bittorrent/torrentinfo.cpp

@ -275,9 +275,8 @@ QVector<TrackerEntry> TorrentInfo::trackers() const @@ -275,9 +275,8 @@ QVector<TrackerEntry> TorrentInfo::trackers() const
QVector<TrackerEntry> ret;
ret.reserve(static_cast<decltype(ret)::size_type>(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;
}

2
src/base/bittorrent/torrentinfo.h

@ -30,8 +30,8 @@ @@ -30,8 +30,8 @@
#include <libtorrent/torrent_info.hpp>
#include <QCoreApplication>
#include <QtContainerFwd>
#include <QCoreApplication>
#include <QVector>
#include "base/3rdparty/expected.hpp"

14
src/base/bittorrent/trackerentry.cpp

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015, 2021 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2015-2022 Vladimir Golovnev <glassez@yandex.ru>
*
* 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 @@ @@ -28,22 +28,16 @@
#include "trackerentry.h"
#include <QUrl>
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

14
src/base/bittorrent/trackerentry.h

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015, 2021 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2015-2022 Vladimir Golovnev <glassez@yandex.ru>
*
* 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 @@ @@ -28,14 +28,19 @@
#pragma once
#include <libtorrent/socket.hpp>
#include <QtGlobal>
#include <QHash>
#include <QMap>
#include <QString>
#include <QVector>
namespace BitTorrent
{
struct TrackerEntry
{
using Endpoint = lt::tcp::endpoint;
enum Status
{
NotContacted = 1,
@ -46,8 +51,6 @@ namespace BitTorrent @@ -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 @@ -59,7 +62,8 @@ namespace BitTorrent
QString url {};
int tier = 0;
QVector<EndpointStats> endpoints {};
// TODO: Use QHash<TrackerEntry::Endpoint, QHash<int, EndpointStats>> once Qt5 is dropped.
QMap<Endpoint, QHash<int, EndpointStats>> stats {};
// Deprecated fields
Status status = NotContacted;

1
src/gui/mainwindow.cpp

@ -1495,6 +1495,7 @@ void MainWindow::showFiltersSidebar(const bool show) @@ -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);
}

13
src/gui/properties/trackerlistwidget.cpp

@ -473,18 +473,7 @@ void TrackerListWidget::deleteSelectedTrackers() @@ -473,18 +473,7 @@ void TrackerListWidget::deleteSelectedTrackers()
delete item;
}
// Iterate over the trackers and remove the selected ones
const QVector<BitTorrent::TrackerEntry> trackers = torrent->trackers();
QVector<BitTorrent::TrackerEntry> 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();

157
src/gui/transferlistfilterswidget.cpp

@ -38,6 +38,7 @@ @@ -38,6 +38,7 @@
#include <QUrl>
#include <QVBoxLayout>
#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 @@ -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() @@ -388,6 +390,70 @@ TrackerFiltersList::~TrackerFiltersList()
Utils::Fs::removeFile(iconPath);
}
void TrackerFiltersList::addTrackers(const BitTorrent::Torrent *torrent, const QVector<BitTorrent::TrackerEntry> &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<BitTorrent::TorrentID> &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<BitTorrent::TrackerEntry> 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 @@ -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 @@ -421,15 +485,7 @@ void TrackerFiltersList::addItem(const QString &tracker, const BitTorrent::Torre
QSet<BitTorrent::TorrentID> &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: @@ -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: @@ -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) @@ -549,49 +597,51 @@ void TrackerFiltersList::setDownloadTrackerFavicon(bool value)
}
}
void TrackerFiltersList::handleTrackerEntriesUpdated(const QHash<BitTorrent::Torrent *, QHash<QString, BitTorrent::TrackerEntryUpdateInfo>> &updateInfos)
void TrackerFiltersList::handleTrackerEntriesUpdated(const QHash<BitTorrent::Torrent *, QSet<QString>> &updateInfos)
{
for (auto torrentsIt = updateInfos.cbegin(); torrentsIt != updateInfos.cend(); ++torrentsIt)
{
const BitTorrent::TorrentID id = torrentsIt.key()->id();
const QHash<QString, BitTorrent::TrackerEntryUpdateInfo> &infos = torrentsIt.value();
const BitTorrent::Torrent *torrent = torrentsIt.key();
const QSet<QString> &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<BitTorrent::TrackerEntry> 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<QString> &errored = errorHashesIt.value();
errored.remove(trackerURL);
errored.remove(trackerEntry.url);
}
if (!updateInfo.hasMessages)
if (trackerEntry.message.isEmpty())
{
if (warningHashesIt != m_warnings.end())
{
QSet<QString> &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) @@ -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<QString> trackerURLs {torrent->trackerURLs()};
for (const QString &trackerURL : trackerURLs)
addItem(trackerURL, torrentID);
const BitTorrent::TorrentID torrentID = torrent->id();
const QVector<BitTorrent::TrackerEntry> 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) @@ -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<QString> trackerURLs {torrent->trackerURLs()};
for (const QString &trackerURL : trackerURLs)
removeItem(trackerURL, torrentID);
const BitTorrent::TorrentID torrentID = torrent->id();
const QVector<BitTorrent::TrackerEntry> 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) @@ -838,22 +888,25 @@ void TransferListFiltersWidget::setDownloadTrackerFavicon(bool value)
void TransferListFiltersWidget::addTrackers(const BitTorrent::Torrent *torrent, const QVector<BitTorrent::TrackerEntry> &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<BitTorrent::TrackerEntry> &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<BitTorrent::Torrent *, QHash<QString, BitTorrent::TrackerEntryUpdateInfo>> &updateInfos)
void TransferListFiltersWidget::trackerEntriesUpdated(const QHash<BitTorrent::Torrent *, QSet<QString>> &updateInfos)
{
m_trackerFilters->handleTrackerEntriesUpdated(updateInfos);
}

18
src/gui/transferlistfilterswidget.h

@ -124,12 +124,12 @@ public: @@ -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<BitTorrent::TrackerEntry> &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<BitTorrent::Torrent *, QSet<QString>> &updateInfos);
void setDownloadTrackerFavicon(bool value);
void handleTrackerEntriesUpdated(const QHash<BitTorrent::Torrent *, QHash<QString, BitTorrent::TrackerEntryUpdateInfo>> &updateInfos);
private slots:
void handleFavicoDownloadFinished(const Net::DownloadResult &result);
@ -141,6 +141,9 @@ private: @@ -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<BitTorrent::TorrentID> getTorrentIDs(int row) const;
@ -174,9 +177,10 @@ public: @@ -174,9 +177,10 @@ public:
public slots:
void addTrackers(const BitTorrent::Torrent *torrent, const QVector<BitTorrent::TrackerEntry> &trackers);
void removeTrackers(const BitTorrent::Torrent *torrent, const QVector<BitTorrent::TrackerEntry> &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<BitTorrent::Torrent *, QHash<QString, BitTorrent::TrackerEntryUpdateInfo>> &updateInfos);
void trackerEntriesUpdated(const QHash<BitTorrent::Torrent *, QSet<QString>> &updateInfos);
private slots:
void onCategoryFilterStateChanged(bool enabled);

21
src/webui/api/torrentscontroller.cpp

@ -767,8 +767,8 @@ void TorrentsController::editTrackerAction() @@ -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() @@ -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() @@ -806,20 +806,7 @@ void TorrentsController::removeTrackersAction()
throw APIError(APIErrorType::NotFound);
const QStringList urls = params()[u"urls"_qs].split(u'|');
const QVector<BitTorrent::TrackerEntry> trackers = torrent->trackers();
QVector<BitTorrent::TrackerEntry> 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();

Loading…
Cancel
Save