Revise Tracker related classes

And also rename them.

PR #20489.
This commit is contained in:
Chocobo1 2024-04-01 19:17:35 +08:00 committed by GitHub
parent 4967f977c5
commit 90383567b2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 529 additions and 345 deletions

View file

@ -46,6 +46,7 @@ add_library(qbt_base STATIC
bittorrent/torrentinfo.h bittorrent/torrentinfo.h
bittorrent/tracker.h bittorrent/tracker.h
bittorrent/trackerentry.h bittorrent/trackerentry.h
bittorrent/trackerentrystatus.h
concepts/explicitlyconvertibleto.h concepts/explicitlyconvertibleto.h
concepts/stringable.h concepts/stringable.h
digest32.h digest32.h
@ -151,6 +152,7 @@ add_library(qbt_base STATIC
bittorrent/torrentinfo.cpp bittorrent/torrentinfo.cpp
bittorrent/tracker.cpp bittorrent/tracker.cpp
bittorrent/trackerentry.cpp bittorrent/trackerentry.cpp
bittorrent/trackerentrystatus.cpp
exceptions.cpp exceptions.cpp
http/connection.cpp http/connection.cpp
http/httperror.cpp http/httperror.cpp

View file

@ -38,6 +38,7 @@
#include "categoryoptions.h" #include "categoryoptions.h"
#include "sharelimitaction.h" #include "sharelimitaction.h"
#include "trackerentry.h" #include "trackerentry.h"
#include "trackerentrystatus.h"
class QString; class QString;
@ -490,6 +491,6 @@ namespace BitTorrent
void trackersRemoved(Torrent *torrent, const QStringList &trackers); void trackersRemoved(Torrent *torrent, const QStringList &trackers);
void trackerSuccess(Torrent *torrent, const QString &tracker); void trackerSuccess(Torrent *torrent, const QString &tracker);
void trackerWarning(Torrent *torrent, const QString &tracker); void trackerWarning(Torrent *torrent, const QString &tracker);
void trackerEntriesUpdated(Torrent *torrent, const QHash<QString, TrackerEntry> &updatedTrackerEntries); void trackerEntryStatusesUpdated(Torrent *torrent, const QHash<QString, TrackerEntryStatus> &updatedTrackers);
}; };
} }

View file

@ -102,6 +102,7 @@
#include "torrentdescriptor.h" #include "torrentdescriptor.h"
#include "torrentimpl.h" #include "torrentimpl.h"
#include "tracker.h" #include "tracker.h"
#include "trackerentry.h"
using namespace std::chrono_literals; using namespace std::chrono_literals;
using namespace BitTorrent; using namespace BitTorrent;
@ -2214,7 +2215,7 @@ void SessionImpl::populateAdditionalTrackers()
{ {
tracker = tracker.trimmed(); tracker = tracker.trimmed();
if (!tracker.isEmpty()) if (!tracker.isEmpty())
m_additionalTrackerList.append({tracker.toString()}); m_additionalTrackerList.append({.url = tracker.toString(), .tier = 0});
} }
} }
@ -4898,14 +4899,15 @@ void SessionImpl::handleTorrentMetadataReceived(TorrentImpl *const torrent)
void SessionImpl::handleTorrentStopped(TorrentImpl *const torrent) void SessionImpl::handleTorrentStopped(TorrentImpl *const torrent)
{ {
torrent->resetTrackerEntries(); torrent->resetTrackerEntryStatuses();
const auto &trackerEntries = torrent->trackers(); const QVector<TrackerEntryStatus> trackers = torrent->trackers();
QHash<QString, TrackerEntry> updatedTrackerEntries; QHash<QString, TrackerEntryStatus> updatedTrackers;
updatedTrackerEntries.reserve(trackerEntries.size()); updatedTrackers.reserve(trackers.size());
for (const auto &trackerEntry : trackerEntries)
updatedTrackerEntries.emplace(trackerEntry.url, trackerEntry); for (const TrackerEntryStatus &status : trackers)
emit trackerEntriesUpdated(torrent, updatedTrackerEntries); updatedTrackers.emplace(status.url, status);
emit trackerEntryStatusesUpdated(torrent, updatedTrackers);
LogMsg(tr("Torrent stopped. Torrent: \"%1\"").arg(torrent->name())); LogMsg(tr("Torrent stopped. Torrent: \"%1\"").arg(torrent->name()));
emit torrentStopped(torrent); emit torrentStopped(torrent);
@ -6041,7 +6043,7 @@ void SessionImpl::handleTrackerAlert(const lt::tracker_alert *a)
if (!torrent) if (!torrent)
return; return;
QMap<int, int> &updateInfo = m_updatedTrackerEntries[torrent->nativeHandle()][std::string(a->tracker_url())][a->local_endpoint]; QMap<int, int> &updateInfo = m_updatedTrackerStatuses[torrent->nativeHandle()][std::string(a->tracker_url())][a->local_endpoint];
if (a->type() == lt::tracker_reply_alert::alert_type) if (a->type() == lt::tracker_reply_alert::alert_type)
{ {
@ -6105,15 +6107,13 @@ void SessionImpl::handleTorrentConflictAlert(const lt::torrent_conflict_alert *a
void SessionImpl::processTrackerStatuses() void SessionImpl::processTrackerStatuses()
{ {
if (m_updatedTrackerEntries.isEmpty()) if (m_updatedTrackerStatuses.isEmpty())
return; return;
for (auto it = m_updatedTrackerEntries.cbegin(); it != m_updatedTrackerEntries.cend(); ++it) for (auto it = m_updatedTrackerStatuses.cbegin(); it != m_updatedTrackerStatuses.cend(); ++it)
{ updateTrackerEntryStatuses(it.key(), it.value());
updateTrackerEntries(it.key(), it.value());
}
m_updatedTrackerEntries.clear(); m_updatedTrackerStatuses.clear();
} }
void SessionImpl::saveStatistics() const void SessionImpl::saveStatistics() const
@ -6140,7 +6140,7 @@ void SessionImpl::loadStatistics()
m_previouslyUploaded = value[u"AlltimeUL"_s].toLongLong(); m_previouslyUploaded = value[u"AlltimeUL"_s].toLongLong();
} }
void SessionImpl::updateTrackerEntries(lt::torrent_handle torrentHandle, QHash<std::string, QHash<lt::tcp::endpoint, QMap<int, int>>> updatedTrackers) void SessionImpl::updateTrackerEntryStatuses(lt::torrent_handle torrentHandle, QHash<std::string, QHash<lt::tcp::endpoint, QMap<int, int>>> updatedTrackers)
{ {
invokeAsync([this, torrentHandle = std::move(torrentHandle), updatedTrackers = std::move(updatedTrackers)]() mutable invokeAsync([this, torrentHandle = std::move(torrentHandle), updatedTrackers = std::move(updatedTrackers)]() mutable
{ {
@ -6154,8 +6154,8 @@ void SessionImpl::updateTrackerEntries(lt::torrent_handle torrentHandle, QHash<s
if (!torrent || torrent->isStopped()) if (!torrent || torrent->isStopped())
return; return;
QHash<QString, TrackerEntry> updatedTrackerEntries; QHash<QString, TrackerEntryStatus> trackers;
updatedTrackerEntries.reserve(updatedTrackers.size()); trackers.reserve(updatedTrackers.size());
for (const lt::announce_entry &announceEntry : nativeTrackers) for (const lt::announce_entry &announceEntry : nativeTrackers)
{ {
const auto updatedTrackersIter = updatedTrackers.find(announceEntry.url); const auto updatedTrackersIter = updatedTrackers.find(announceEntry.url);
@ -6163,12 +6163,12 @@ void SessionImpl::updateTrackerEntries(lt::torrent_handle torrentHandle, QHash<s
continue; continue;
const auto &updateInfo = updatedTrackersIter.value(); const auto &updateInfo = updatedTrackersIter.value();
TrackerEntry trackerEntry = torrent->updateTrackerEntry(announceEntry, updateInfo); TrackerEntryStatus status = torrent->updateTrackerEntryStatus(announceEntry, updateInfo);
const QString url = trackerEntry.url; const QString url = status.url;
updatedTrackerEntries.emplace(url, std::move(trackerEntry)); trackers.emplace(url, std::move(status));
} }
emit trackerEntriesUpdated(torrent, updatedTrackerEntries); emit trackerEntryStatusesUpdated(torrent, trackers);
}); });
} }
catch (const std::exception &) catch (const std::exception &)

View file

@ -54,7 +54,7 @@
#include "session.h" #include "session.h"
#include "sessionstatus.h" #include "sessionstatus.h"
#include "torrentinfo.h" #include "torrentinfo.h"
#include "trackerentry.h" #include "trackerentrystatus.h"
class QString; class QString;
class QThread; class QThread;
@ -69,16 +69,18 @@ class NativeSessionExtension;
namespace BitTorrent namespace BitTorrent
{ {
enum class MoveStorageMode;
enum class MoveStorageContext;
class InfoHash; class InfoHash;
class ResumeDataStorage; class ResumeDataStorage;
class Torrent; class Torrent;
class TorrentDescriptor; class TorrentDescriptor;
class TorrentImpl; class TorrentImpl;
class Tracker; class Tracker;
struct LoadTorrentParams;
enum class MoveStorageMode; struct LoadTorrentParams;
enum class MoveStorageContext; struct TrackerEntry;
struct SessionMetricIndices struct SessionMetricIndices
{ {
@ -587,7 +589,7 @@ namespace BitTorrent
void saveStatistics() const; void saveStatistics() const;
void loadStatistics(); void loadStatistics();
void updateTrackerEntries(lt::torrent_handle torrentHandle, QHash<std::string, QHash<lt::tcp::endpoint, QMap<int, int>>> updatedTrackers); void updateTrackerEntryStatuses(lt::torrent_handle torrentHandle, QHash<std::string, QHash<lt::tcp::endpoint, QMap<int, int>>> updatedTrackers);
// BitTorrent // BitTorrent
lt::session *m_nativeSession = nullptr; lt::session *m_nativeSession = nullptr;
@ -766,7 +768,7 @@ namespace BitTorrent
// This field holds amounts of peers reported by trackers in their responses to announces // This field holds amounts of peers reported by trackers in their responses to announces
// (torrent.tracker_name.tracker_local_endpoint.protocol_version.num_peers) // (torrent.tracker_name.tracker_local_endpoint.protocol_version.num_peers)
QHash<lt::torrent_handle, QHash<std::string, QHash<lt::tcp::endpoint, QMap<int, int>>>> m_updatedTrackerEntries; QHash<lt::torrent_handle, QHash<std::string, QHash<lt::tcp::endpoint, QMap<int, int>>>> m_updatedTrackerStatuses;
// I/O errored torrents // I/O errored torrents
QSet<TorrentID> m_recentErroredTorrents; QSet<TorrentID> m_recentErroredTorrents;

View file

@ -48,14 +48,17 @@ class QUrl;
namespace BitTorrent namespace BitTorrent
{ {
enum class DownloadPriority; enum class DownloadPriority;
class InfoHash; class InfoHash;
class PeerInfo; class PeerInfo;
class Session; class Session;
class TorrentID; class TorrentID;
class TorrentInfo; class TorrentInfo;
struct PeerAddress; struct PeerAddress;
struct SSLParameters; struct SSLParameters;
struct TrackerEntry; struct TrackerEntry;
struct TrackerEntryStatus;
// Using `Q_ENUM_NS()` without a wrapper namespace in our case is not advised // Using `Q_ENUM_NS()` without a wrapper namespace in our case is not advised
// since `Q_NAMESPACE` cannot be used when the same namespace resides at different files. // since `Q_NAMESPACE` cannot be used when the same namespace resides at different files.
@ -245,7 +248,7 @@ namespace BitTorrent
virtual bool hasMissingFiles() const = 0; virtual bool hasMissingFiles() const = 0;
virtual bool hasError() const = 0; virtual bool hasError() const = 0;
virtual int queuePosition() const = 0; virtual int queuePosition() const = 0;
virtual QVector<TrackerEntry> trackers() const = 0; virtual QVector<TrackerEntryStatus> trackers() const = 0;
virtual QVector<QUrl> urlSeeds() const = 0; virtual QVector<QUrl> urlSeeds() const = 0;
virtual QString error() const = 0; virtual QString error() const = 0;
virtual qlonglong totalDownload() const = 0; virtual qlonglong totalDownload() const = 0;

View file

@ -71,6 +71,7 @@
#include "peeraddress.h" #include "peeraddress.h"
#include "peerinfo.h" #include "peerinfo.h"
#include "sessionimpl.h" #include "sessionimpl.h"
#include "trackerentry.h"
#if defined(Q_OS_MACOS) || defined(Q_OS_WIN) #if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
#include "base/utils/os.h" #include "base/utils/os.h"
@ -101,15 +102,15 @@ namespace
return QString::fromStdString((std::stringstream() << ltTCPEndpoint).str()); return QString::fromStdString((std::stringstream() << ltTCPEndpoint).str());
} }
void updateTrackerEntry(TrackerEntry &trackerEntry, const lt::announce_entry &nativeEntry void updateTrackerEntryStatus(TrackerEntryStatus &trackerEntryStatus, const lt::announce_entry &nativeEntry
, const QSet<int> &btProtocols, const QHash<lt::tcp::endpoint, QMap<int, int>> &updateInfo) , const QSet<int> &btProtocols, const QHash<lt::tcp::endpoint, QMap<int, int>> &updateInfo)
{ {
Q_ASSERT(trackerEntry.url == QString::fromStdString(nativeEntry.url)); Q_ASSERT(trackerEntryStatus.url == QString::fromStdString(nativeEntry.url));
trackerEntry.tier = nativeEntry.tier; trackerEntryStatus.tier = nativeEntry.tier;
// remove outdated endpoints // remove outdated endpoints
trackerEntry.endpointEntries.removeIf([&nativeEntry](const QHash<std::pair<QString, int>, TrackerEndpointEntry>::iterator &iter) trackerEntryStatus.endpoints.removeIf([&nativeEntry](const QHash<std::pair<QString, int>, TrackerEndpointStatus>::iterator &iter)
{ {
return std::none_of(nativeEntry.endpoints.cbegin(), nativeEntry.endpoints.cend() return std::none_of(nativeEntry.endpoints.cbegin(), nativeEntry.endpoints.cend()
, [&endpointName = std::get<0>(iter.key())](const auto &existingEndpoint) , [&endpointName = std::get<0>(iter.key())](const auto &existingEndpoint)
@ -141,61 +142,61 @@ namespace
const lt::announce_endpoint &ltAnnounceInfo = ltAnnounceEndpoint; const lt::announce_endpoint &ltAnnounceInfo = ltAnnounceEndpoint;
#endif #endif
const QMap<int, int> &endpointUpdateInfo = updateInfo[ltAnnounceEndpoint.local_endpoint]; const QMap<int, int> &endpointUpdateInfo = updateInfo[ltAnnounceEndpoint.local_endpoint];
TrackerEndpointEntry &trackerEndpointEntry = trackerEntry.endpointEntries[std::make_pair(endpointName, protocolVersion)]; TrackerEndpointStatus &trackerEndpointStatus = trackerEntryStatus.endpoints[std::make_pair(endpointName, protocolVersion)];
trackerEndpointEntry.name = endpointName; trackerEndpointStatus.name = endpointName;
trackerEndpointEntry.btVersion = protocolVersion; trackerEndpointStatus.btVersion = protocolVersion;
trackerEndpointEntry.numPeers = endpointUpdateInfo.value(protocolVersion, trackerEndpointEntry.numPeers); trackerEndpointStatus.numPeers = endpointUpdateInfo.value(protocolVersion, trackerEndpointStatus.numPeers);
trackerEndpointEntry.numSeeds = ltAnnounceInfo.scrape_complete; trackerEndpointStatus.numSeeds = ltAnnounceInfo.scrape_complete;
trackerEndpointEntry.numLeeches = ltAnnounceInfo.scrape_incomplete; trackerEndpointStatus.numLeeches = ltAnnounceInfo.scrape_incomplete;
trackerEndpointEntry.numDownloaded = ltAnnounceInfo.scrape_downloaded; trackerEndpointStatus.numDownloaded = ltAnnounceInfo.scrape_downloaded;
trackerEndpointEntry.nextAnnounceTime = fromLTTimePoint32(ltAnnounceInfo.next_announce); trackerEndpointStatus.nextAnnounceTime = fromLTTimePoint32(ltAnnounceInfo.next_announce);
trackerEndpointEntry.minAnnounceTime = fromLTTimePoint32(ltAnnounceInfo.min_announce); trackerEndpointStatus.minAnnounceTime = fromLTTimePoint32(ltAnnounceInfo.min_announce);
if (ltAnnounceInfo.updating) if (ltAnnounceInfo.updating)
{ {
trackerEndpointEntry.status = TrackerEntryStatus::Updating; trackerEndpointStatus.state = TrackerEndpointState::Updating;
++numUpdating; ++numUpdating;
} }
else if (ltAnnounceInfo.fails > 0) else if (ltAnnounceInfo.fails > 0)
{ {
if (ltAnnounceInfo.last_error == lt::errors::tracker_failure) if (ltAnnounceInfo.last_error == lt::errors::tracker_failure)
{ {
trackerEndpointEntry.status = TrackerEntryStatus::TrackerError; trackerEndpointStatus.state = TrackerEndpointState::TrackerError;
++numTrackerError; ++numTrackerError;
} }
else if (ltAnnounceInfo.last_error == lt::errors::announce_skipped) else if (ltAnnounceInfo.last_error == lt::errors::announce_skipped)
{ {
trackerEndpointEntry.status = TrackerEntryStatus::Unreachable; trackerEndpointStatus.state = TrackerEndpointState::Unreachable;
++numUnreachable; ++numUnreachable;
} }
else else
{ {
trackerEndpointEntry.status = TrackerEntryStatus::NotWorking; trackerEndpointStatus.state = TrackerEndpointState::NotWorking;
++numNotWorking; ++numNotWorking;
} }
} }
else if (nativeEntry.verified) else if (nativeEntry.verified)
{ {
trackerEndpointEntry.status = TrackerEntryStatus::Working; trackerEndpointStatus.state = TrackerEndpointState::Working;
++numWorking; ++numWorking;
} }
else else
{ {
trackerEndpointEntry.status = TrackerEntryStatus::NotContacted; trackerEndpointStatus.state = TrackerEndpointState::NotContacted;
} }
if (!ltAnnounceInfo.message.empty()) if (!ltAnnounceInfo.message.empty())
{ {
trackerEndpointEntry.message = QString::fromStdString(ltAnnounceInfo.message); trackerEndpointStatus.message = QString::fromStdString(ltAnnounceInfo.message);
} }
else if (ltAnnounceInfo.last_error) else if (ltAnnounceInfo.last_error)
{ {
trackerEndpointEntry.message = QString::fromLocal8Bit(ltAnnounceInfo.last_error.message()); trackerEndpointStatus.message = QString::fromLocal8Bit(ltAnnounceInfo.last_error.message());
} }
else else
{ {
trackerEndpointEntry.message.clear(); trackerEndpointStatus.message.clear();
} }
} }
} }
@ -204,58 +205,58 @@ namespace
{ {
if (numUpdating > 0) if (numUpdating > 0)
{ {
trackerEntry.status = TrackerEntryStatus::Updating; trackerEntryStatus.state = TrackerEndpointState::Updating;
} }
else if (numWorking > 0) else if (numWorking > 0)
{ {
trackerEntry.status = TrackerEntryStatus::Working; trackerEntryStatus.state = TrackerEndpointState::Working;
} }
else if (numTrackerError > 0) else if (numTrackerError > 0)
{ {
trackerEntry.status = TrackerEntryStatus::TrackerError; trackerEntryStatus.state = TrackerEndpointState::TrackerError;
} }
else if (numUnreachable == numEndpoints) else if (numUnreachable == numEndpoints)
{ {
trackerEntry.status = TrackerEntryStatus::Unreachable; trackerEntryStatus.state = TrackerEndpointState::Unreachable;
} }
else if ((numUnreachable + numNotWorking) == numEndpoints) else if ((numUnreachable + numNotWorking) == numEndpoints)
{ {
trackerEntry.status = TrackerEntryStatus::NotWorking; trackerEntryStatus.state = TrackerEndpointState::NotWorking;
} }
} }
trackerEntry.numPeers = -1; trackerEntryStatus.numPeers = -1;
trackerEntry.numSeeds = -1; trackerEntryStatus.numSeeds = -1;
trackerEntry.numLeeches = -1; trackerEntryStatus.numLeeches = -1;
trackerEntry.numDownloaded = -1; trackerEntryStatus.numDownloaded = -1;
trackerEntry.nextAnnounceTime = QDateTime(); trackerEntryStatus.nextAnnounceTime = QDateTime();
trackerEntry.minAnnounceTime = QDateTime(); trackerEntryStatus.minAnnounceTime = QDateTime();
trackerEntry.message.clear(); trackerEntryStatus.message.clear();
for (const TrackerEndpointEntry &endpointEntry : asConst(trackerEntry.endpointEntries)) for (const TrackerEndpointStatus &endpointStatus : asConst(trackerEntryStatus.endpoints))
{ {
trackerEntry.numPeers = std::max(trackerEntry.numPeers, endpointEntry.numPeers); trackerEntryStatus.numPeers = std::max(trackerEntryStatus.numPeers, endpointStatus.numPeers);
trackerEntry.numSeeds = std::max(trackerEntry.numSeeds, endpointEntry.numSeeds); trackerEntryStatus.numSeeds = std::max(trackerEntryStatus.numSeeds, endpointStatus.numSeeds);
trackerEntry.numLeeches = std::max(trackerEntry.numLeeches, endpointEntry.numLeeches); trackerEntryStatus.numLeeches = std::max(trackerEntryStatus.numLeeches, endpointStatus.numLeeches);
trackerEntry.numDownloaded = std::max(trackerEntry.numDownloaded, endpointEntry.numDownloaded); trackerEntryStatus.numDownloaded = std::max(trackerEntryStatus.numDownloaded, endpointStatus.numDownloaded);
if (endpointEntry.status == trackerEntry.status) if (endpointStatus.state == trackerEntryStatus.state)
{ {
if (!trackerEntry.nextAnnounceTime.isValid() || (trackerEntry.nextAnnounceTime > endpointEntry.nextAnnounceTime)) if (!trackerEntryStatus.nextAnnounceTime.isValid() || (trackerEntryStatus.nextAnnounceTime > endpointStatus.nextAnnounceTime))
{ {
trackerEntry.nextAnnounceTime = endpointEntry.nextAnnounceTime; trackerEntryStatus.nextAnnounceTime = endpointStatus.nextAnnounceTime;
trackerEntry.minAnnounceTime = endpointEntry.minAnnounceTime; trackerEntryStatus.minAnnounceTime = endpointStatus.minAnnounceTime;
if ((endpointEntry.status != TrackerEntryStatus::Working) if ((endpointStatus.state != TrackerEndpointState::Working)
|| !endpointEntry.message.isEmpty()) || !endpointStatus.message.isEmpty())
{ {
trackerEntry.message = endpointEntry.message; trackerEntryStatus.message = endpointStatus.message;
} }
} }
if (endpointEntry.status == TrackerEntryStatus::Working) if (endpointStatus.state == TrackerEndpointState::Working)
{ {
if (trackerEntry.message.isEmpty()) if (trackerEntryStatus.message.isEmpty())
trackerEntry.message = endpointEntry.message; trackerEntryStatus.message = endpointStatus.message;
} }
} }
} }
@ -347,9 +348,9 @@ TorrentImpl::TorrentImpl(SessionImpl *session, lt::session *nativeSession
setStopCondition(params.stopCondition); setStopCondition(params.stopCondition);
const auto *extensionData = static_cast<ExtensionData *>(m_ltAddTorrentParams.userdata); const auto *extensionData = static_cast<ExtensionData *>(m_ltAddTorrentParams.userdata);
m_trackerEntries.reserve(static_cast<decltype(m_trackerEntries)::size_type>(extensionData->trackers.size())); m_trackerEntryStatuses.reserve(static_cast<decltype(m_trackerEntryStatuses)::size_type>(extensionData->trackers.size()));
for (const lt::announce_entry &announceEntry : extensionData->trackers) for (const lt::announce_entry &announceEntry : extensionData->trackers)
m_trackerEntries.append({QString::fromStdString(announceEntry.url), announceEntry.tier}); m_trackerEntryStatuses.append({QString::fromStdString(announceEntry.url), announceEntry.tier});
m_urlSeeds.reserve(static_cast<decltype(m_urlSeeds)::size_type>(extensionData->urlSeeds.size())); m_urlSeeds.reserve(static_cast<decltype(m_urlSeeds)::size_type>(extensionData->urlSeeds.size()));
for (const std::string &urlSeed : extensionData->urlSeeds) for (const std::string &urlSeed : extensionData->urlSeeds)
m_urlSeeds.append(QString::fromStdString(urlSeed)); m_urlSeeds.append(QString::fromStdString(urlSeed));
@ -601,27 +602,32 @@ Path TorrentImpl::makeUserPath(const Path &path) const
return userPath; return userPath;
} }
QVector<TrackerEntry> TorrentImpl::trackers() const QVector<TrackerEntryStatus> TorrentImpl::trackers() const
{ {
return m_trackerEntries; return m_trackerEntryStatuses;
} }
void TorrentImpl::addTrackers(QVector<TrackerEntry> trackers) void TorrentImpl::addTrackers(QVector<TrackerEntry> trackers)
{ {
trackers.removeIf([](const TrackerEntry &entry) { return entry.url.isEmpty(); }); trackers.removeIf([](const TrackerEntry &trackerEntry) { return trackerEntry.url.isEmpty(); });
const auto newTrackers = QSet<TrackerEntry>(trackers.cbegin(), trackers.cend()) QSet<TrackerEntry> currentTrackerSet;
- QSet<TrackerEntry>(m_trackerEntries.cbegin(), m_trackerEntries.cend()); currentTrackerSet.reserve(m_trackerEntryStatuses.size());
if (newTrackers.isEmpty()) for (const TrackerEntryStatus &status : asConst(m_trackerEntryStatuses))
currentTrackerSet.insert({.url = status.url, .tier = status.tier});
const auto newTrackerSet = QSet<TrackerEntry>(trackers.cbegin(), trackers.cend()) - currentTrackerSet;
if (newTrackerSet.isEmpty())
return; return;
trackers = QVector<TrackerEntry>(newTrackers.cbegin(), newTrackers.cend()); trackers = QVector<TrackerEntry>(newTrackerSet.cbegin(), newTrackerSet.cend());
for (const TrackerEntry &tracker : trackers) for (const TrackerEntry &tracker : asConst(trackers))
{
m_nativeHandle.add_tracker(makeNativeAnnounceEntry(tracker.url, tracker.tier)); m_nativeHandle.add_tracker(makeNativeAnnounceEntry(tracker.url, tracker.tier));
m_trackerEntryStatuses.append({tracker.url, tracker.tier});
m_trackerEntries.append(trackers); }
std::sort(m_trackerEntries.begin(), m_trackerEntries.end() std::sort(m_trackerEntryStatuses.begin(), m_trackerEntryStatuses.end()
, [](const TrackerEntry &lhs, const TrackerEntry &rhs) { return lhs.tier < rhs.tier; }); , [](const TrackerEntryStatus &left, const TrackerEntryStatus &right) { return left.tier < right.tier; });
deferredRequestResumeData(); deferredRequestResumeData();
m_session->handleTorrentTrackersAdded(this, trackers); m_session->handleTorrentTrackersAdded(this, trackers);
@ -632,13 +638,13 @@ void TorrentImpl::removeTrackers(const QStringList &trackers)
QStringList removedTrackers = trackers; QStringList removedTrackers = trackers;
for (const QString &tracker : trackers) for (const QString &tracker : trackers)
{ {
if (!m_trackerEntries.removeOne({tracker})) if (!m_trackerEntryStatuses.removeOne({tracker}))
removedTrackers.removeOne(tracker); removedTrackers.removeOne(tracker);
} }
std::vector<lt::announce_entry> nativeTrackers; std::vector<lt::announce_entry> nativeTrackers;
nativeTrackers.reserve(m_trackerEntries.size()); nativeTrackers.reserve(m_trackerEntryStatuses.size());
for (const TrackerEntry &tracker : asConst(m_trackerEntries)) for (const TrackerEntryStatus &tracker : asConst(m_trackerEntryStatuses))
nativeTrackers.emplace_back(makeNativeAnnounceEntry(tracker.url, tracker.tier)); nativeTrackers.emplace_back(makeNativeAnnounceEntry(tracker.url, tracker.tier));
if (!removedTrackers.isEmpty()) if (!removedTrackers.isEmpty())
@ -652,20 +658,25 @@ void TorrentImpl::removeTrackers(const QStringList &trackers)
void TorrentImpl::replaceTrackers(QVector<TrackerEntry> trackers) void TorrentImpl::replaceTrackers(QVector<TrackerEntry> trackers)
{ {
trackers.removeIf([](const TrackerEntry &entry) { return entry.url.isEmpty(); }); trackers.removeIf([](const TrackerEntry &trackerEntry) { return trackerEntry.url.isEmpty(); });
// Filter out duplicate trackers // Filter out duplicate trackers
const auto uniqueTrackers = QSet<TrackerEntry>(trackers.cbegin(), trackers.cend()); const auto uniqueTrackers = QSet<TrackerEntry>(trackers.cbegin(), trackers.cend());
trackers = QVector<TrackerEntry>(uniqueTrackers.cbegin(), uniqueTrackers.cend()); trackers = QVector<TrackerEntry>(uniqueTrackers.cbegin(), uniqueTrackers.cend());
std::sort(trackers.begin(), trackers.end() std::sort(trackers.begin(), trackers.end()
, [](const TrackerEntry &lhs, const TrackerEntry &rhs) { return lhs.tier < rhs.tier; }); , [](const TrackerEntry &left, const TrackerEntry &right) { return left.tier < right.tier; });
std::vector<lt::announce_entry> nativeTrackers; std::vector<lt::announce_entry> nativeTrackers;
nativeTrackers.reserve(trackers.size()); nativeTrackers.reserve(trackers.size());
m_trackerEntryStatuses.clear();
for (const TrackerEntry &tracker : trackers) for (const TrackerEntry &tracker : trackers)
{
nativeTrackers.emplace_back(makeNativeAnnounceEntry(tracker.url, tracker.tier)); nativeTrackers.emplace_back(makeNativeAnnounceEntry(tracker.url, tracker.tier));
m_trackerEntryStatuses.append({tracker.url, tracker.tier});
}
m_nativeHandle.replace_trackers(nativeTrackers); m_nativeHandle.replace_trackers(nativeTrackers);
m_trackerEntries = trackers;
// Clear the peer list if it's a private torrent since // Clear the peer list if it's a private torrent since
// we do not want to keep connecting with peers from old tracker. // we do not want to keep connecting with peers from old tracker.
@ -1679,16 +1690,16 @@ void TorrentImpl::fileSearchFinished(const Path &savePath, const PathList &fileN
endReceivedMetadataHandling(savePath, fileNames); endReceivedMetadataHandling(savePath, fileNames);
} }
TrackerEntry TorrentImpl::updateTrackerEntry(const lt::announce_entry &announceEntry, const QHash<lt::tcp::endpoint, QMap<int, int>> &updateInfo) TrackerEntryStatus TorrentImpl::updateTrackerEntryStatus(const lt::announce_entry &announceEntry, const QHash<lt::tcp::endpoint, QMap<int, int>> &updateInfo)
{ {
const auto it = std::find_if(m_trackerEntries.begin(), m_trackerEntries.end() const auto it = std::find_if(m_trackerEntryStatuses.begin(), m_trackerEntryStatuses.end()
, [&announceEntry](const TrackerEntry &trackerEntry) , [&announceEntry](const TrackerEntryStatus &trackerEntryStatus)
{ {
return (trackerEntry.url == QString::fromStdString(announceEntry.url)); return (trackerEntryStatus.url == QString::fromStdString(announceEntry.url));
}); });
Q_ASSERT(it != m_trackerEntries.end()); Q_ASSERT(it != m_trackerEntryStatuses.end());
if (it == m_trackerEntries.end()) [[unlikely]] if (it == m_trackerEntryStatuses.end()) [[unlikely]]
return {}; return {};
#ifdef QBT_USES_LIBTORRENT2 #ifdef QBT_USES_LIBTORRENT2
@ -1701,14 +1712,21 @@ TrackerEntry TorrentImpl::updateTrackerEntry(const lt::announce_entry &announceE
#else #else
const QSet<int> btProtocols {1}; const QSet<int> btProtocols {1};
#endif #endif
::updateTrackerEntry(*it, announceEntry, btProtocols, updateInfo); ::updateTrackerEntryStatus(*it, announceEntry, btProtocols, updateInfo);
return *it; return *it;
} }
void TorrentImpl::resetTrackerEntries() void TorrentImpl::resetTrackerEntryStatuses()
{ {
for (auto &trackerEntry : m_trackerEntries) for (TrackerEntryStatus &status : m_trackerEntryStatuses)
trackerEntry = {trackerEntry.url, trackerEntry.tier}; {
const QString tempUrl = status.url;
const int tempTier = status.tier;
status.clear();
status.url = tempUrl;
status.tier = tempTier;
}
} }
std::shared_ptr<const libtorrent::torrent_info> TorrentImpl::nativeTorrentInfo() const std::shared_ptr<const libtorrent::torrent_info> TorrentImpl::nativeTorrentInfo() const
@ -2738,7 +2756,7 @@ QString TorrentImpl::createMagnetURI() const
ret += u"&dn=" + QString::fromLatin1(QUrl::toPercentEncoding(displayName)); ret += u"&dn=" + QString::fromLatin1(QUrl::toPercentEncoding(displayName));
} }
for (const TrackerEntry &tracker : asConst(trackers())) for (const TrackerEntryStatus &tracker : asConst(trackers()))
{ {
ret += u"&tr=" + QString::fromLatin1(QUrl::toPercentEncoding(tracker.url)); ret += u"&tr=" + QString::fromLatin1(QUrl::toPercentEncoding(tracker.url));
} }
@ -2766,8 +2784,8 @@ nonstd::expected<lt::entry, QString> TorrentImpl::exportTorrent() const
#endif #endif
lt::create_torrent creator {*torrentInfo}; lt::create_torrent creator {*torrentInfo};
for (const TrackerEntry &entry : asConst(trackers())) for (const TrackerEntryStatus &status : asConst(trackers()))
creator.add_tracker(entry.url.toStdString(), entry.tier); creator.add_tracker(status.url.toStdString(), status.tier);
return creator.generate(); return creator.generate();
} }

View file

@ -55,7 +55,7 @@
#include "torrent.h" #include "torrent.h"
#include "torrentcontentlayout.h" #include "torrentcontentlayout.h"
#include "torrentinfo.h" #include "torrentinfo.h"
#include "trackerentry.h" #include "trackerentrystatus.h"
namespace BitTorrent namespace BitTorrent
{ {
@ -175,7 +175,7 @@ namespace BitTorrent
bool hasMissingFiles() const override; bool hasMissingFiles() const override;
bool hasError() const override; bool hasError() const override;
int queuePosition() const override; int queuePosition() const override;
QVector<TrackerEntry> trackers() const override; QVector<TrackerEntryStatus> trackers() const override;
QVector<QUrl> urlSeeds() const override; QVector<QUrl> urlSeeds() const override;
QString error() const override; QString error() const override;
qlonglong totalDownload() const override; qlonglong totalDownload() const override;
@ -275,8 +275,8 @@ namespace BitTorrent
void deferredRequestResumeData(); void deferredRequestResumeData();
void handleMoveStorageJobFinished(const Path &path, MoveStorageContext context, bool hasOutstandingJob); void handleMoveStorageJobFinished(const Path &path, MoveStorageContext context, bool hasOutstandingJob);
void fileSearchFinished(const Path &savePath, const PathList &fileNames); void fileSearchFinished(const Path &savePath, const PathList &fileNames);
TrackerEntry updateTrackerEntry(const lt::announce_entry &announceEntry, const QHash<lt::tcp::endpoint, QMap<int, int>> &updateInfo); TrackerEntryStatus updateTrackerEntryStatus(const lt::announce_entry &announceEntry, const QHash<lt::tcp::endpoint, QMap<int, int>> &updateInfo);
void resetTrackerEntries(); void resetTrackerEntryStatuses();
private: private:
using EventTrigger = std::function<void ()>; using EventTrigger = std::function<void ()>;
@ -349,7 +349,7 @@ namespace BitTorrent
MaintenanceJob m_maintenanceJob = MaintenanceJob::None; MaintenanceJob m_maintenanceJob = MaintenanceJob::None;
QVector<TrackerEntry> m_trackerEntries; QVector<TrackerEntryStatus> m_trackerEntryStatuses;
QVector<QUrl> m_urlSeeds; QVector<QUrl> m_urlSeeds;
FileErrorInfo m_lastFileError; FileErrorInfo m_lastFileError;

View file

@ -279,7 +279,7 @@ QVector<TrackerEntry> TorrentInfo::trackers() const
QVector<TrackerEntry> ret; QVector<TrackerEntry> ret;
ret.reserve(static_cast<decltype(ret)::size_type>(trackers.size())); ret.reserve(static_cast<decltype(ret)::size_type>(trackers.size()));
for (const lt::announce_entry &tracker : trackers) for (const lt::announce_entry &tracker : trackers)
ret.append({QString::fromStdString(tracker.url), tracker.tier}); ret.append({.url = QString::fromStdString(tracker.url), .tier = tracker.tier});
return ret; return ret;
} }

View file

@ -1,5 +1,6 @@
/* /*
* Bittorrent Client using Qt and libtorrent. * Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2024 Mike Tzou (Chocobo1)
* Copyright (C) 2015-2023 Vladimir Golovnev <glassez@yandex.ru> * Copyright (C) 2015-2023 Vladimir Golovnev <glassez@yandex.ru>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -28,7 +29,9 @@
#include "trackerentry.h" #include "trackerentry.h"
#include <QHash>
#include <QList> #include <QList>
#include <QStringView>
QList<BitTorrent::TrackerEntry> BitTorrent::parseTrackerEntries(const QStringView str) QList<BitTorrent::TrackerEntry> BitTorrent::parseTrackerEntries(const QStringView str)
{ {

View file

@ -1,5 +1,6 @@
/* /*
* Bittorrent Client using Qt and libtorrent. * Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2024 Mike Tzou (Chocobo1)
* Copyright (C) 2015-2023 Vladimir Golovnev <glassez@yandex.ru> * Copyright (C) 2015-2023 Vladimir Golovnev <glassez@yandex.ru>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -29,56 +30,16 @@
#pragma once #pragma once
#include <QtContainerFwd> #include <QtContainerFwd>
#include <QDateTime>
#include <QHash>
#include <QString> #include <QString>
#include <QStringView>
class QStringView;
namespace BitTorrent namespace BitTorrent
{ {
enum class TrackerEntryStatus
{
NotContacted = 1,
Working = 2,
Updating = 3,
NotWorking = 4,
TrackerError = 5,
Unreachable = 6
};
struct TrackerEndpointEntry
{
QString name {};
int btVersion = 1;
TrackerEntryStatus status = TrackerEntryStatus::NotContacted;
QString message {};
int numPeers = -1;
int numSeeds = -1;
int numLeeches = -1;
int numDownloaded = -1;
QDateTime nextAnnounceTime {};
QDateTime minAnnounceTime {};
};
struct TrackerEntry struct TrackerEntry
{ {
QString url {}; QString url {};
int tier = 0; int tier = 0;
TrackerEntryStatus status = TrackerEntryStatus::NotContacted;
QString message {};
int numPeers = -1;
int numSeeds = -1;
int numLeeches = -1;
int numDownloaded = -1;
QDateTime nextAnnounceTime {};
QDateTime minAnnounceTime {};
QHash<std::pair<QString, int>, TrackerEndpointEntry> endpointEntries {};
}; };
QList<TrackerEntry> parseTrackerEntries(QStringView str); QList<TrackerEntry> parseTrackerEntries(QStringView str);

View file

@ -0,0 +1,54 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015-2023 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.
*/
#include "trackerentrystatus.h"
void BitTorrent::TrackerEntryStatus::clear()
{
url.clear();
tier = 0;
state = TrackerEndpointState::NotContacted;
message.clear();
numPeers = -1;
numSeeds = -1;
numLeeches = -1;
numDownloaded = -1;
nextAnnounceTime = {};
minAnnounceTime = {};
endpoints.clear();
}
bool BitTorrent::operator==(const TrackerEntryStatus &left, const TrackerEntryStatus &right)
{
return (left.url == right.url);
}
std::size_t BitTorrent::qHash(const TrackerEntryStatus &key, const std::size_t seed)
{
return ::qHash(key.url, seed);
}

View file

@ -0,0 +1,89 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015-2023 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 <QDateTime>
#include <QHash>
#include <QString>
class QStringView;
namespace BitTorrent
{
enum class TrackerEndpointState
{
NotContacted = 1,
Working = 2,
Updating = 3,
NotWorking = 4,
TrackerError = 5,
Unreachable = 6
};
struct TrackerEndpointStatus
{
QString name {};
int btVersion = 1;
TrackerEndpointState state = TrackerEndpointState::NotContacted;
QString message {};
int numPeers = -1;
int numSeeds = -1;
int numLeeches = -1;
int numDownloaded = -1;
QDateTime nextAnnounceTime {};
QDateTime minAnnounceTime {};
};
struct TrackerEntryStatus
{
QString url {};
int tier = 0;
TrackerEndpointState state = TrackerEndpointState::NotContacted;
QString message {};
int numPeers = -1;
int numSeeds = -1;
int numLeeches = -1;
int numDownloaded = -1;
QDateTime nextAnnounceTime {};
QDateTime minAnnounceTime {};
QHash<std::pair<QString, int>, TrackerEndpointStatus> endpoints {};
void clear();
};
bool operator==(const TrackerEntryStatus &left, const TrackerEntryStatus &right);
std::size_t qHash(const TrackerEntryStatus &key, std::size_t seed = 0);
}

View file

@ -1386,7 +1386,7 @@ void MainWindow::showFiltersSidebar(const bool show)
connect(BitTorrent::Session::instance(), &BitTorrent::Session::trackersAdded, m_transferListFiltersWidget, &TransferListFiltersWidget::addTrackers); 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::trackersRemoved, m_transferListFiltersWidget, &TransferListFiltersWidget::removeTrackers);
connect(BitTorrent::Session::instance(), &BitTorrent::Session::trackersChanged, m_transferListFiltersWidget, &TransferListFiltersWidget::refreshTrackers); connect(BitTorrent::Session::instance(), &BitTorrent::Session::trackersChanged, m_transferListFiltersWidget, &TransferListFiltersWidget::refreshTrackers);
connect(BitTorrent::Session::instance(), &BitTorrent::Session::trackerEntriesUpdated, m_transferListFiltersWidget, &TransferListFiltersWidget::trackerEntriesUpdated); connect(BitTorrent::Session::instance(), &BitTorrent::Session::trackerEntryStatusesUpdated, m_transferListFiltersWidget, &TransferListFiltersWidget::trackerEntryStatusesUpdated);
m_splitter->insertWidget(0, m_transferListFiltersWidget); m_splitter->insertWidget(0, m_transferListFiltersWidget);
m_splitter->setCollapsible(0, true); m_splitter->setCollapsible(0, true);

View file

@ -48,6 +48,7 @@
#include "base/bittorrent/peerinfo.h" #include "base/bittorrent/peerinfo.h"
#include "base/bittorrent/session.h" #include "base/bittorrent/session.h"
#include "base/bittorrent/torrent.h" #include "base/bittorrent/torrent.h"
#include "base/bittorrent/trackerentry.h"
#include "base/global.h" #include "base/global.h"
#include "base/utils/misc.h" #include "base/utils/misc.h"
@ -68,24 +69,24 @@ namespace
return (val > -1) ? QString::number(val) : TrackerListModel::tr("N/A"); return (val > -1) ? QString::number(val) : TrackerListModel::tr("N/A");
} }
QString toString(const BitTorrent::TrackerEntryStatus status) QString toString(const BitTorrent::TrackerEndpointState state)
{ {
switch (status) switch (state)
{ {
case BitTorrent::TrackerEntryStatus::Working: case BitTorrent::TrackerEndpointState::Working:
return TrackerListModel::tr(STR_WORKING); return TrackerListModel::tr(STR_WORKING);
case BitTorrent::TrackerEntryStatus::Updating: case BitTorrent::TrackerEndpointState::Updating:
return TrackerListModel::tr("Updating..."); return TrackerListModel::tr("Updating...");
case BitTorrent::TrackerEntryStatus::NotWorking: case BitTorrent::TrackerEndpointState::NotWorking:
return TrackerListModel::tr("Not working"); return TrackerListModel::tr("Not working");
case BitTorrent::TrackerEntryStatus::TrackerError: case BitTorrent::TrackerEndpointState::TrackerError:
return TrackerListModel::tr("Tracker error"); return TrackerListModel::tr("Tracker error");
case BitTorrent::TrackerEntryStatus::Unreachable: case BitTorrent::TrackerEndpointState::Unreachable:
return TrackerListModel::tr("Unreachable"); return TrackerListModel::tr("Unreachable");
case BitTorrent::TrackerEntryStatus::NotContacted: case BitTorrent::TrackerEndpointState::NotContacted:
return TrackerListModel::tr("Not contacted yet"); return TrackerListModel::tr("Not contacted yet");
} }
return TrackerListModel::tr("Invalid status!"); return TrackerListModel::tr("Invalid state!");
} }
QString statusDHT(const BitTorrent::Torrent *torrent) QString statusDHT(const BitTorrent::Torrent *torrent)
@ -132,7 +133,7 @@ struct TrackerListModel::Item final
QString name {}; QString name {};
int tier = -1; int tier = -1;
int btVersion = -1; int btVersion = -1;
BitTorrent::TrackerEntryStatus status = BitTorrent::TrackerEntryStatus::NotContacted; BitTorrent::TrackerEndpointState status = BitTorrent::TrackerEndpointState::NotContacted;
QString message {}; QString message {};
int numPeers = -1; int numPeers = -1;
@ -159,11 +160,11 @@ struct TrackerListModel::Item final
>> childItems {}; >> childItems {};
Item(QStringView name, QStringView message); Item(QStringView name, QStringView message);
explicit Item(const BitTorrent::TrackerEntry &trackerEntry); explicit Item(const BitTorrent::TrackerEntryStatus &trackerEntryStatus);
Item(const std::shared_ptr<Item> &parentItem, const BitTorrent::TrackerEndpointEntry &endpointEntry); Item(const std::shared_ptr<Item> &parentItem, const BitTorrent::TrackerEndpointStatus &endpointStatus);
void fillFrom(const BitTorrent::TrackerEntry &trackerEntry); void fillFrom(const BitTorrent::TrackerEntryStatus &trackerEntryStatus);
void fillFrom(const BitTorrent::TrackerEndpointEntry &endpointEntry); void fillFrom(const BitTorrent::TrackerEndpointStatus &endpointStatus);
}; };
class TrackerListModel::Items final : public multi_index_container< class TrackerListModel::Items final : public multi_index_container<
@ -180,53 +181,53 @@ TrackerListModel::Item::Item(const QStringView name, const QStringView message)
{ {
} }
TrackerListModel::Item::Item(const BitTorrent::TrackerEntry &trackerEntry) TrackerListModel::Item::Item(const BitTorrent::TrackerEntryStatus &trackerEntryStatus)
: name {trackerEntry.url} : name {trackerEntryStatus.url}
{ {
fillFrom(trackerEntry); fillFrom(trackerEntryStatus);
} }
TrackerListModel::Item::Item(const std::shared_ptr<Item> &parentItem, const BitTorrent::TrackerEndpointEntry &endpointEntry) TrackerListModel::Item::Item(const std::shared_ptr<Item> &parentItem, const BitTorrent::TrackerEndpointStatus &endpointStatus)
: name {endpointEntry.name} : name {endpointStatus.name}
, btVersion {endpointEntry.btVersion} , btVersion {endpointStatus.btVersion}
, parentItem {parentItem} , parentItem {parentItem}
{ {
fillFrom(endpointEntry); fillFrom(endpointStatus);
} }
void TrackerListModel::Item::fillFrom(const BitTorrent::TrackerEntry &trackerEntry) void TrackerListModel::Item::fillFrom(const BitTorrent::TrackerEntryStatus &trackerEntryStatus)
{ {
Q_ASSERT(parentItem.expired()); Q_ASSERT(parentItem.expired());
Q_ASSERT(trackerEntry.url == name); Q_ASSERT(trackerEntryStatus.url == name);
tier = trackerEntry.tier; tier = trackerEntryStatus.tier;
status = trackerEntry.status; status = trackerEntryStatus.state;
message = trackerEntry.message; message = trackerEntryStatus.message;
numPeers = trackerEntry.numPeers; numPeers = trackerEntryStatus.numPeers;
numSeeds = trackerEntry.numSeeds; numSeeds = trackerEntryStatus.numSeeds;
numLeeches = trackerEntry.numLeeches; numLeeches = trackerEntryStatus.numLeeches;
numDownloaded = trackerEntry.numDownloaded; numDownloaded = trackerEntryStatus.numDownloaded;
nextAnnounceTime = trackerEntry.nextAnnounceTime; nextAnnounceTime = trackerEntryStatus.nextAnnounceTime;
minAnnounceTime = trackerEntry.minAnnounceTime; minAnnounceTime = trackerEntryStatus.minAnnounceTime;
secsToNextAnnounce = 0; secsToNextAnnounce = 0;
secsToMinAnnounce = 0; secsToMinAnnounce = 0;
announceTimestamp = QDateTime(); announceTimestamp = QDateTime();
} }
void TrackerListModel::Item::fillFrom(const BitTorrent::TrackerEndpointEntry &endpointEntry) void TrackerListModel::Item::fillFrom(const BitTorrent::TrackerEndpointStatus &endpointStatus)
{ {
Q_ASSERT(!parentItem.expired()); Q_ASSERT(!parentItem.expired());
Q_ASSERT(endpointEntry.name == name); Q_ASSERT(endpointStatus.name == name);
Q_ASSERT(endpointEntry.btVersion == btVersion); Q_ASSERT(endpointStatus.btVersion == btVersion);
status = endpointEntry.status; status = endpointStatus.state;
message = endpointEntry.message; message = endpointStatus.message;
numPeers = endpointEntry.numPeers; numPeers = endpointStatus.numPeers;
numSeeds = endpointEntry.numSeeds; numSeeds = endpointStatus.numSeeds;
numLeeches = endpointEntry.numLeeches; numLeeches = endpointStatus.numLeeches;
numDownloaded = endpointEntry.numDownloaded; numDownloaded = endpointStatus.numDownloaded;
nextAnnounceTime = endpointEntry.nextAnnounceTime; nextAnnounceTime = endpointStatus.nextAnnounceTime;
minAnnounceTime = endpointEntry.minAnnounceTime; minAnnounceTime = endpointStatus.minAnnounceTime;
secsToNextAnnounce = 0; secsToNextAnnounce = 0;
secsToMinAnnounce = 0; secsToMinAnnounce = 0;
announceTimestamp = QDateTime(); announceTimestamp = QDateTime();
@ -261,8 +262,8 @@ TrackerListModel::TrackerListModel(BitTorrent::Session *btSession, QObject *pare
if (torrent == m_torrent) if (torrent == m_torrent)
onTrackersChanged(); onTrackersChanged();
}); });
connect(m_btSession, &BitTorrent::Session::trackerEntriesUpdated, this connect(m_btSession, &BitTorrent::Session::trackerEntryStatusesUpdated, this
, [this](BitTorrent::Torrent *torrent, const QHash<QString, BitTorrent::TrackerEntry> &updatedTrackers) , [this](BitTorrent::Torrent *torrent, const QHash<QString, BitTorrent::TrackerEntryStatus> &updatedTrackers)
{ {
if (torrent == m_torrent) if (torrent == m_torrent)
onTrackersUpdated(updatedTrackers); onTrackersUpdated(updatedTrackers);
@ -296,8 +297,8 @@ void TrackerListModel::populate()
{ {
Q_ASSERT(m_torrent); Q_ASSERT(m_torrent);
const QList<BitTorrent::TrackerEntry> trackerEntries = m_torrent->trackers(); const QList<BitTorrent::TrackerEntryStatus> trackers = m_torrent->trackers();
m_items->reserve(trackerEntries.size() + STICKY_ROW_COUNT); m_items->reserve(trackers.size() + STICKY_ROW_COUNT);
const QString &privateTorrentMessage = m_torrent->isPrivate() ? tr(STR_PRIVATE_MSG) : u""_s; const QString &privateTorrentMessage = m_torrent->isPrivate() ? tr(STR_PRIVATE_MSG) : u""_s;
m_items->emplace_back(std::make_shared<Item>(u"** [DHT] **", privateTorrentMessage)); m_items->emplace_back(std::make_shared<Item>(u"** [DHT] **", privateTorrentMessage));
@ -365,46 +366,44 @@ void TrackerListModel::populate()
emit dataChanged(index(ROW_DHT, COL_SEEDS), index(ROW_LSD, COL_LEECHES)); emit dataChanged(index(ROW_DHT, COL_SEEDS), index(ROW_LSD, COL_LEECHES));
}); });
for (const BitTorrent::TrackerEntry &trackerEntry : trackerEntries) for (const BitTorrent::TrackerEntryStatus &status : trackers)
addTrackerItem(trackerEntry); addTrackerItem(status);
m_announceTimestamp = QDateTime::currentDateTime(); m_announceTimestamp = QDateTime::currentDateTime();
m_announceRefreshTimer->start(ANNOUNCE_TIME_REFRESH_INTERVAL); m_announceRefreshTimer->start(ANNOUNCE_TIME_REFRESH_INTERVAL);
} }
std::shared_ptr<TrackerListModel::Item> TrackerListModel::createTrackerItem(const BitTorrent::TrackerEntry &trackerEntry) std::shared_ptr<TrackerListModel::Item> TrackerListModel::createTrackerItem(const BitTorrent::TrackerEntryStatus &trackerEntryStatus)
{ {
auto item = std::make_shared<Item>(trackerEntry); const auto item = std::make_shared<Item>(trackerEntryStatus);
for (const auto &[id, endpointEntry] : trackerEntry.endpointEntries.asKeyValueRange()) for (const auto &[id, endpointStatus] : trackerEntryStatus.endpoints.asKeyValueRange())
{ item->childItems.emplace_back(std::make_shared<Item>(item, endpointStatus));
item->childItems.emplace_back(std::make_shared<Item>(item, endpointEntry));
}
return item; return item;
} }
void TrackerListModel::addTrackerItem(const BitTorrent::TrackerEntry &trackerEntry) void TrackerListModel::addTrackerItem(const BitTorrent::TrackerEntryStatus &trackerEntryStatus)
{ {
[[maybe_unused]] const auto &[iter, res] = m_items->emplace_back(createTrackerItem(trackerEntry)); [[maybe_unused]] const auto &[iter, res] = m_items->emplace_back(createTrackerItem(trackerEntryStatus));
Q_ASSERT(res); Q_ASSERT(res);
} }
void TrackerListModel::updateTrackerItem(const std::shared_ptr<Item> &item, const BitTorrent::TrackerEntry &trackerEntry) void TrackerListModel::updateTrackerItem(const std::shared_ptr<Item> &item, const BitTorrent::TrackerEntryStatus &trackerEntryStatus)
{ {
QSet<std::pair<QString, int>> endpointItemIDs; QSet<std::pair<QString, int>> endpointItemIDs;
QList<std::shared_ptr<Item>> newEndpointItems; QList<std::shared_ptr<Item>> newEndpointItems;
for (const auto &[id, endpointEntry] : trackerEntry.endpointEntries.asKeyValueRange()) for (const auto &[id, endpointStatus] : trackerEntryStatus.endpoints.asKeyValueRange())
{ {
endpointItemIDs.insert(id); endpointItemIDs.insert(id);
auto &itemsByID = item->childItems.get<ByID>(); auto &itemsByID = item->childItems.get<ByID>();
if (const auto &iter = itemsByID.find(std::make_tuple(id.first, id.second)); iter != itemsByID.end()) if (const auto &iter = itemsByID.find(std::make_tuple(id.first, id.second)); iter != itemsByID.end())
{ {
(*iter)->fillFrom(endpointEntry); (*iter)->fillFrom(endpointStatus);
} }
else else
{ {
newEndpointItems.emplace_back(std::make_shared<Item>(item, endpointEntry)); newEndpointItems.emplace_back(std::make_shared<Item>(item, endpointStatus));
} }
} }
@ -429,7 +428,7 @@ void TrackerListModel::updateTrackerItem(const std::shared_ptr<Item> &item, cons
} }
} }
const auto numRows = rowCount(trackerIndex); const int numRows = rowCount(trackerIndex);
emit dataChanged(index(0, 0, trackerIndex), index((numRows - 1), (columnCount(trackerIndex) - 1), trackerIndex)); emit dataChanged(index(0, 0, trackerIndex), index((numRows - 1), (columnCount(trackerIndex) - 1), trackerIndex));
if (!newEndpointItems.isEmpty()) if (!newEndpointItems.isEmpty())
@ -440,7 +439,7 @@ void TrackerListModel::updateTrackerItem(const std::shared_ptr<Item> &item, cons
endInsertRows(); endInsertRows();
} }
item->fillFrom(trackerEntry); item->fillFrom(trackerEntryStatus);
emit dataChanged(trackerIndex, index(trackerRow, (columnCount() - 1))); emit dataChanged(trackerIndex, index(trackerRow, (columnCount() - 1)));
} }
@ -697,10 +696,10 @@ QModelIndex TrackerListModel::parent(const QModelIndex &index) const
void TrackerListModel::onTrackersAdded(const QList<BitTorrent::TrackerEntry> &newTrackers) void TrackerListModel::onTrackersAdded(const QList<BitTorrent::TrackerEntry> &newTrackers)
{ {
const auto row = rowCount(); const int row = rowCount();
beginInsertRows({}, row, (row + newTrackers.size() - 1)); beginInsertRows({}, row, (row + newTrackers.size() - 1));
for (const BitTorrent::TrackerEntry &trackerEntry : newTrackers) for (const BitTorrent::TrackerEntry &entry : newTrackers)
addTrackerItem(trackerEntry); addTrackerItem({entry.url, entry.tier});
endInsertRows(); endInsertRows();
} }
@ -727,18 +726,18 @@ void TrackerListModel::onTrackersChanged()
trackerItemIDs.insert(m_items->at(i)->name); trackerItemIDs.insert(m_items->at(i)->name);
QList<std::shared_ptr<Item>> newTrackerItems; QList<std::shared_ptr<Item>> newTrackerItems;
for (const BitTorrent::TrackerEntry &trackerEntry : m_torrent->trackers()) for (const BitTorrent::TrackerEntryStatus &trackerEntryStatus : m_torrent->trackers())
{ {
trackerItemIDs.insert(trackerEntry.url); trackerItemIDs.insert(trackerEntryStatus.url);
auto &itemsByName = m_items->get<ByName>(); auto &itemsByName = m_items->get<ByName>();
if (const auto &iter = itemsByName.find(trackerEntry.url); iter != itemsByName.end()) if (const auto &iter = itemsByName.find(trackerEntryStatus.url); iter != itemsByName.end())
{ {
updateTrackerItem(*iter, trackerEntry); updateTrackerItem(*iter, trackerEntryStatus);
} }
else else
{ {
newTrackerItems.emplace_back(createTrackerItem(trackerEntry)); newTrackerItems.emplace_back(createTrackerItem(trackerEntryStatus));
} }
} }
@ -760,7 +759,7 @@ void TrackerListModel::onTrackersChanged()
if (!newTrackerItems.isEmpty()) if (!newTrackerItems.isEmpty())
{ {
const auto numRows = rowCount(); const int numRows = rowCount();
beginInsertRows({}, numRows, (numRows + newTrackerItems.size() - 1)); beginInsertRows({}, numRows, (numRows + newTrackerItems.size() - 1));
for (const auto &newTrackerItem : asConst(newTrackerItems)) for (const auto &newTrackerItem : asConst(newTrackerItems))
m_items->get<0>().push_back(newTrackerItem); m_items->get<0>().push_back(newTrackerItem);
@ -768,14 +767,14 @@ void TrackerListModel::onTrackersChanged()
} }
} }
void TrackerListModel::onTrackersUpdated(const QHash<QString, BitTorrent::TrackerEntry> &updatedTrackers) void TrackerListModel::onTrackersUpdated(const QHash<QString, BitTorrent::TrackerEntryStatus> &updatedTrackers)
{ {
for (const auto &[url, entry] : updatedTrackers.asKeyValueRange()) for (const auto &[url, tracker] : updatedTrackers.asKeyValueRange())
{ {
auto &itemsByName = m_items->get<ByName>(); auto &itemsByName = m_items->get<ByName>();
if (const auto &iter = itemsByName.find(entry.url); iter != itemsByName.end()) [[likely]] if (const auto &iter = itemsByName.find(tracker.url); iter != itemsByName.end()) [[likely]]
{ {
updateTrackerItem(*iter, entry); updateTrackerItem(*iter, tracker);
} }
} }
} }

View file

@ -35,7 +35,7 @@
#include <QAbstractItemModel> #include <QAbstractItemModel>
#include <QDateTime> #include <QDateTime>
#include "base/bittorrent/trackerentry.h" #include "base/bittorrent/trackerentrystatus.h"
class QTimer; class QTimer;
@ -43,6 +43,7 @@ namespace BitTorrent
{ {
class Session; class Session;
class Torrent; class Torrent;
struct TrackerEntry;
} }
class TrackerListModel final : public QAbstractItemModel class TrackerListModel final : public QAbstractItemModel
@ -99,14 +100,14 @@ private:
struct Item; struct Item;
void populate(); void populate();
std::shared_ptr<Item> createTrackerItem(const BitTorrent::TrackerEntry &trackerEntry); std::shared_ptr<Item> createTrackerItem(const BitTorrent::TrackerEntryStatus &trackerEntryStatus);
void addTrackerItem(const BitTorrent::TrackerEntry &trackerEntry); void addTrackerItem(const BitTorrent::TrackerEntryStatus &trackerEntryStatus);
void updateTrackerItem(const std::shared_ptr<Item> &item, const BitTorrent::TrackerEntry &trackerEntry); void updateTrackerItem(const std::shared_ptr<Item> &item, const BitTorrent::TrackerEntryStatus &trackerEntryStatus);
void refreshAnnounceTimes(); void refreshAnnounceTimes();
void onTrackersAdded(const QList<BitTorrent::TrackerEntry> &newTrackers); void onTrackersAdded(const QList<BitTorrent::TrackerEntry> &newTrackers);
void onTrackersRemoved(const QStringList &deletedTrackers); void onTrackersRemoved(const QStringList &deletedTrackers);
void onTrackersChanged(); void onTrackersChanged();
void onTrackersUpdated(const QHash<QString, BitTorrent::TrackerEntry> &updatedTrackers); void onTrackersUpdated(const QHash<QString, BitTorrent::TrackerEntryStatus> &updatedTrackers);
BitTorrent::Session *m_btSession = nullptr; BitTorrent::Session *m_btSession = nullptr;
BitTorrent::Torrent *m_torrent = nullptr; BitTorrent::Torrent *m_torrent = nullptr;

View file

@ -47,7 +47,7 @@
#include "base/bittorrent/session.h" #include "base/bittorrent/session.h"
#include "base/bittorrent/torrent.h" #include "base/bittorrent/torrent.h"
#include "base/bittorrent/trackerentry.h" #include "base/bittorrent/trackerentrystatus.h"
#include "base/global.h" #include "base/global.h"
#include "base/preferences.h" #include "base/preferences.h"
#include "gui/autoexpandabledialog.h" #include "gui/autoexpandabledialog.h"
@ -142,52 +142,68 @@ QModelIndexList TrackerListWidget::getSelectedTrackerRows() const
void TrackerListWidget::decreaseSelectedTrackerTiers() void TrackerListWidget::decreaseSelectedTrackerTiers()
{ {
const auto &trackerIndexes = getSelectedTrackerRows(); const QModelIndexList trackerIndexes = getSelectedTrackerRows();
if (trackerIndexes.isEmpty()) if (trackerIndexes.isEmpty())
return; return;
QSet<QString> trackerURLs; QSet<QString> trackerURLs;
trackerURLs.reserve(trackerIndexes.size());
for (const QModelIndex &index : trackerIndexes) for (const QModelIndex &index : trackerIndexes)
{
trackerURLs.insert(index.siblingAtColumn(TrackerListModel::COL_URL).data().toString()); trackerURLs.insert(index.siblingAtColumn(TrackerListModel::COL_URL).data().toString());
const QList<BitTorrent::TrackerEntryStatus> trackers = m_model->torrent()->trackers();
QList<BitTorrent::TrackerEntry> adjustedTrackers;
adjustedTrackers.reserve(trackers.size());
for (const BitTorrent::TrackerEntryStatus &status : trackers)
{
BitTorrent::TrackerEntry entry
{
.url = status.url,
.tier = status.tier
};
if (trackerURLs.contains(entry.url))
{
if (entry.tier > 0)
--entry.tier;
}
adjustedTrackers.append(entry);
} }
QList<BitTorrent::TrackerEntry> trackers = m_model->torrent()->trackers(); m_model->torrent()->replaceTrackers(adjustedTrackers);
for (BitTorrent::TrackerEntry &trackerEntry : trackers)
{
if (trackerURLs.contains(trackerEntry.url))
{
if (trackerEntry.tier > 0)
--trackerEntry.tier;
}
}
m_model->torrent()->replaceTrackers(trackers);
} }
void TrackerListWidget::increaseSelectedTrackerTiers() void TrackerListWidget::increaseSelectedTrackerTiers()
{ {
const auto &trackerIndexes = getSelectedTrackerRows(); const QModelIndexList trackerIndexes = getSelectedTrackerRows();
if (trackerIndexes.isEmpty()) if (trackerIndexes.isEmpty())
return; return;
QSet<QString> trackerURLs; QSet<QString> trackerURLs;
trackerURLs.reserve(trackerIndexes.size());
for (const QModelIndex &index : trackerIndexes) for (const QModelIndex &index : trackerIndexes)
{
trackerURLs.insert(index.siblingAtColumn(TrackerListModel::COL_URL).data().toString()); trackerURLs.insert(index.siblingAtColumn(TrackerListModel::COL_URL).data().toString());
const QList<BitTorrent::TrackerEntryStatus> trackers = m_model->torrent()->trackers();
QList<BitTorrent::TrackerEntry> adjustedTrackers;
adjustedTrackers.reserve(trackers.size());
for (const BitTorrent::TrackerEntryStatus &status : trackers)
{
BitTorrent::TrackerEntry entry
{
.url = status.url,
.tier = status.tier
};
if (trackerURLs.contains(entry.url))
{
if (entry.tier < std::numeric_limits<decltype(entry.tier)>::max())
++entry.tier;
}
adjustedTrackers.append(entry);
} }
QList<BitTorrent::TrackerEntry> trackers = m_model->torrent()->trackers(); m_model->torrent()->replaceTrackers(adjustedTrackers);
for (BitTorrent::TrackerEntry &trackerEntry : trackers)
{
if (trackerURLs.contains(trackerEntry.url))
{
if (trackerEntry.tier < std::numeric_limits<decltype(trackerEntry.tier)>::max())
++trackerEntry.tier;
}
}
m_model->torrent()->replaceTrackers(trackers);
} }
void TrackerListWidget::openAddTrackersDialog() void TrackerListWidget::openAddTrackersDialog()
@ -205,7 +221,7 @@ void TrackerListWidget::copyTrackerUrl()
if (!torrent()) if (!torrent())
return; return;
const auto &selectedTrackerIndexes = getSelectedTrackerRows(); const QModelIndexList selectedTrackerIndexes = getSelectedTrackerRows();
if (selectedTrackerIndexes.isEmpty()) if (selectedTrackerIndexes.isEmpty())
return; return;
@ -226,7 +242,7 @@ void TrackerListWidget::deleteSelectedTrackers()
if (!torrent()) if (!torrent())
return; return;
const auto &selectedTrackerIndexes = getSelectedTrackerRows(); const QModelIndexList selectedTrackerIndexes = getSelectedTrackerRows();
if (selectedTrackerIndexes.isEmpty()) if (selectedTrackerIndexes.isEmpty())
return; return;
@ -245,7 +261,7 @@ void TrackerListWidget::editSelectedTracker()
if (!torrent()) if (!torrent())
return; return;
const auto &selectedTrackerIndexes = getSelectedTrackerRows(); const QModelIndexList selectedTrackerIndexes = getSelectedTrackerRows();
if (selectedTrackerIndexes.isEmpty()) if (selectedTrackerIndexes.isEmpty())
return; return;
@ -268,24 +284,36 @@ void TrackerListWidget::editSelectedTracker()
if (newTrackerURL == trackerURL) if (newTrackerURL == trackerURL)
return; return;
QList<BitTorrent::TrackerEntry> trackers = torrent()->trackers(); const QList<BitTorrent::TrackerEntryStatus> trackers = torrent()->trackers();
QList<BitTorrent::TrackerEntry> entries;
entries.reserve(trackers.size());
bool match = false; bool match = false;
for (BitTorrent::TrackerEntry &entry : trackers) for (const BitTorrent::TrackerEntryStatus &status : trackers)
{ {
if (newTrackerURL == QUrl(entry.url)) const QUrl url {status.url};
if (newTrackerURL == url)
{ {
QMessageBox::warning(this, tr("Tracker editing failed"), tr("The tracker URL already exists.")); QMessageBox::warning(this, tr("Tracker editing failed"), tr("The tracker URL already exists."));
return; return;
} }
if (!match && (trackerURL == QUrl(entry.url))) BitTorrent::TrackerEntry entry
{
.url = status.url,
.tier = status.tier
};
if (!match && (trackerURL == url))
{ {
match = true; match = true;
entry.url = newTrackerURL.toString(); entry.url = newTrackerURL.toString();
} }
entries.append(entry);
} }
torrent()->replaceTrackers(trackers); torrent()->replaceTrackers(entries);
} }
void TrackerListWidget::reannounceSelected() void TrackerListWidget::reannounceSelected()
@ -315,15 +343,13 @@ void TrackerListWidget::reannounceSelected()
trackerURLs.insert(index.siblingAtColumn(TrackerListModel::COL_URL).data().toString()); trackerURLs.insert(index.siblingAtColumn(TrackerListModel::COL_URL).data().toString());
} }
const QList<BitTorrent::TrackerEntry> &trackers = m_model->torrent()->trackers(); const QList<BitTorrent::TrackerEntryStatus> &trackers = m_model->torrent()->trackers();
for (qsizetype i = 0; i < trackers.size(); ++i) for (qsizetype i = 0; i < trackers.size(); ++i)
{ {
const BitTorrent::TrackerEntry &trackerEntry = trackers.at(i); const BitTorrent::TrackerEntryStatus &status = trackers.at(i);
if (trackerURLs.contains(trackerEntry.url)) if (trackerURLs.contains(status.url))
{
torrent()->forceReannounce(i); torrent()->forceReannounce(i);
} }
}
} }
void TrackerListWidget::showTrackerListMenu() void TrackerListWidget::showTrackerListMenu()

View file

@ -38,6 +38,8 @@
#include "base/algorithm.h" #include "base/algorithm.h"
#include "base/bittorrent/session.h" #include "base/bittorrent/session.h"
#include "base/bittorrent/trackerentry.h"
#include "base/bittorrent/trackerentrystatus.h"
#include "base/global.h" #include "base/global.h"
#include "base/net/downloadmanager.h" #include "base/net/downloadmanager.h"
#include "base/preferences.h" #include "base/preferences.h"
@ -202,16 +204,15 @@ void TrackersFilterWidget::refreshTrackers(const BitTorrent::Torrent *torrent)
return false; return false;
}); });
const QVector<BitTorrent::TrackerEntry> trackerEntries = torrent->trackers(); const QVector<BitTorrent::TrackerEntryStatus> trackers = torrent->trackers();
const bool isTrackerless = trackerEntries.isEmpty(); if (trackers.isEmpty())
if (isTrackerless)
{ {
addItems(NULL_HOST, {torrentID}); addItems(NULL_HOST, {torrentID});
} }
else else
{ {
for (const BitTorrent::TrackerEntry &trackerEntry : trackerEntries) for (const BitTorrent::TrackerEntryStatus &status : trackers)
addItems(trackerEntry.url, {torrentID}); addItems(status.url, {torrentID});
} }
item(OTHERERROR_ROW)->setText(formatItemText(OTHERERROR_ROW, m_errors.size())); item(OTHERERROR_ROW)->setText(formatItemText(OTHERERROR_ROW, m_errors.size()));
@ -380,8 +381,8 @@ void TrackersFilterWidget::setDownloadTrackerFavicon(bool value)
} }
} }
void TrackersFilterWidget::handleTrackerEntriesUpdated(const BitTorrent::Torrent *torrent void TrackersFilterWidget::handleTrackerStatusesUpdated(const BitTorrent::Torrent *torrent
, const QHash<QString, BitTorrent::TrackerEntry> &updatedTrackerEntries) , const QHash<QString, BitTorrent::TrackerEntryStatus> &updatedTrackers)
{ {
const BitTorrent::TorrentID id = torrent->id(); const BitTorrent::TorrentID id = torrent->id();
@ -389,51 +390,51 @@ void TrackersFilterWidget::handleTrackerEntriesUpdated(const BitTorrent::Torrent
auto trackerErrorHashesIt = m_trackerErrors.find(id); auto trackerErrorHashesIt = m_trackerErrors.find(id);
auto warningHashesIt = m_warnings.find(id); auto warningHashesIt = m_warnings.find(id);
for (const BitTorrent::TrackerEntry &trackerEntry : updatedTrackerEntries) for (const BitTorrent::TrackerEntryStatus &trackerEntryStatus : updatedTrackers)
{ {
if (trackerEntry.status == BitTorrent::TrackerEntryStatus::Working) if (trackerEntryStatus.state == BitTorrent::TrackerEndpointState::Working)
{ {
if (errorHashesIt != m_errors.end()) if (errorHashesIt != m_errors.end())
{ {
errorHashesIt->remove(trackerEntry.url); errorHashesIt->remove(trackerEntryStatus.url);
} }
if (trackerErrorHashesIt != m_trackerErrors.end()) if (trackerErrorHashesIt != m_trackerErrors.end())
{ {
trackerErrorHashesIt->remove(trackerEntry.url); trackerErrorHashesIt->remove(trackerEntryStatus.url);
} }
const bool hasNoWarningMessages = std::all_of(trackerEntry.endpointEntries.cbegin(), trackerEntry.endpointEntries.cend() const bool hasNoWarningMessages = std::all_of(trackerEntryStatus.endpoints.cbegin(), trackerEntryStatus.endpoints.cend()
, [](const BitTorrent::TrackerEndpointEntry &endpointEntry) , [](const BitTorrent::TrackerEndpointStatus &endpointEntry)
{ {
return endpointEntry.message.isEmpty() || (endpointEntry.status != BitTorrent::TrackerEntryStatus::Working); return endpointEntry.message.isEmpty() || (endpointEntry.state != BitTorrent::TrackerEndpointState::Working);
}); });
if (hasNoWarningMessages) if (hasNoWarningMessages)
{ {
if (warningHashesIt != m_warnings.end()) if (warningHashesIt != m_warnings.end())
{ {
warningHashesIt->remove(trackerEntry.url); warningHashesIt->remove(trackerEntryStatus.url);
} }
} }
else else
{ {
if (warningHashesIt == m_warnings.end()) if (warningHashesIt == m_warnings.end())
warningHashesIt = m_warnings.insert(id, {}); warningHashesIt = m_warnings.insert(id, {});
warningHashesIt->insert(trackerEntry.url); warningHashesIt->insert(trackerEntryStatus.url);
} }
} }
else if ((trackerEntry.status == BitTorrent::TrackerEntryStatus::NotWorking) else if ((trackerEntryStatus.state == BitTorrent::TrackerEndpointState::NotWorking)
|| (trackerEntry.status == BitTorrent::TrackerEntryStatus::Unreachable)) || (trackerEntryStatus.state == BitTorrent::TrackerEndpointState::Unreachable))
{ {
if (errorHashesIt == m_errors.end()) if (errorHashesIt == m_errors.end())
errorHashesIt = m_errors.insert(id, {}); errorHashesIt = m_errors.insert(id, {});
errorHashesIt->insert(trackerEntry.url); errorHashesIt->insert(trackerEntryStatus.url);
} }
else if (trackerEntry.status == BitTorrent::TrackerEntryStatus::TrackerError) else if (trackerEntryStatus.state == BitTorrent::TrackerEndpointState::TrackerError)
{ {
if (trackerErrorHashesIt == m_trackerErrors.end()) if (trackerErrorHashesIt == m_trackerErrors.end())
trackerErrorHashesIt = m_trackerErrors.insert(id, {}); trackerErrorHashesIt = m_trackerErrors.insert(id, {});
trackerErrorHashesIt->insert(trackerEntry.url); trackerErrorHashesIt->insert(trackerEntryStatus.url);
} }
} }
@ -481,10 +482,10 @@ void TrackersFilterWidget::removeTracker(const QString &tracker)
continue; continue;
QStringList trackersToRemove; QStringList trackersToRemove;
for (const BitTorrent::TrackerEntry &trackerEntry : asConst(torrent->trackers())) for (const BitTorrent::TrackerEntryStatus &trackerEntryStatus : asConst(torrent->trackers()))
{ {
if ((trackerEntry.url == tracker) || (QUrl(trackerEntry.url).host() == tracker)) if ((trackerEntryStatus.url == tracker) || (QUrl(trackerEntryStatus.url).host() == tracker))
trackersToRemove.append(trackerEntry.url); trackersToRemove.append(trackerEntryStatus.url);
} }
torrent->removeTrackers({trackersToRemove}); torrent->removeTrackers({trackersToRemove});
@ -592,8 +593,8 @@ void TrackersFilterWidget::handleTorrentsLoaded(const QVector<BitTorrent::Torren
for (const BitTorrent::Torrent *torrent : torrents) for (const BitTorrent::Torrent *torrent : torrents)
{ {
const BitTorrent::TorrentID torrentID = torrent->id(); const BitTorrent::TorrentID torrentID = torrent->id();
const QVector<BitTorrent::TrackerEntry> trackers = torrent->trackers(); const QVector<BitTorrent::TrackerEntryStatus> trackers = torrent->trackers();
for (const BitTorrent::TrackerEntry &tracker : trackers) for (const BitTorrent::TrackerEntryStatus &tracker : trackers)
torrentsPerTracker[tracker.url].append(torrentID); torrentsPerTracker[tracker.url].append(torrentID);
// Check for trackerless torrent // Check for trackerless torrent
@ -613,8 +614,8 @@ void TrackersFilterWidget::handleTorrentsLoaded(const QVector<BitTorrent::Torren
void TrackersFilterWidget::torrentAboutToBeDeleted(BitTorrent::Torrent *const torrent) void TrackersFilterWidget::torrentAboutToBeDeleted(BitTorrent::Torrent *const torrent)
{ {
const BitTorrent::TorrentID torrentID = torrent->id(); const BitTorrent::TorrentID torrentID = torrent->id();
const QVector<BitTorrent::TrackerEntry> trackers = torrent->trackers(); const QVector<BitTorrent::TrackerEntryStatus> trackers = torrent->trackers();
for (const BitTorrent::TrackerEntry &tracker : trackers) for (const BitTorrent::TrackerEntryStatus &tracker : trackers)
removeItem(tracker.url, torrentID); removeItem(tracker.url, torrentID);
// Check for trackerless torrent // Check for trackerless torrent

View file

@ -32,12 +32,17 @@
#include <QtContainerFwd> #include <QtContainerFwd>
#include <QHash> #include <QHash>
#include "base/bittorrent/trackerentry.h"
#include "base/path.h" #include "base/path.h"
#include "basefilterwidget.h" #include "basefilterwidget.h"
class TransferListWidget; class TransferListWidget;
namespace BitTorrent
{
struct TrackerEntry;
struct TrackerEntryStatus;
}
namespace Net namespace Net
{ {
struct DownloadResult; struct DownloadResult;
@ -55,8 +60,8 @@ public:
void addTrackers(const BitTorrent::Torrent *torrent, const QVector<BitTorrent::TrackerEntry> &trackers); void addTrackers(const BitTorrent::Torrent *torrent, const QVector<BitTorrent::TrackerEntry> &trackers);
void removeTrackers(const BitTorrent::Torrent *torrent, const QStringList &trackers); void removeTrackers(const BitTorrent::Torrent *torrent, const QStringList &trackers);
void refreshTrackers(const BitTorrent::Torrent *torrent); void refreshTrackers(const BitTorrent::Torrent *torrent);
void handleTrackerEntriesUpdated(const BitTorrent::Torrent *torrent void handleTrackerStatusesUpdated(const BitTorrent::Torrent *torrent
, const QHash<QString, BitTorrent::TrackerEntry> &updatedTrackerEntries); , const QHash<QString, BitTorrent::TrackerEntryStatus> &updatedTrackers);
void setDownloadTrackerFavicon(bool value); void setDownloadTrackerFavicon(bool value);
private slots: private slots:

View file

@ -42,6 +42,7 @@
#include "base/algorithm.h" #include "base/algorithm.h"
#include "base/bittorrent/session.h" #include "base/bittorrent/session.h"
#include "base/bittorrent/torrent.h" #include "base/bittorrent/torrent.h"
#include "base/bittorrent/trackerentrystatus.h"
#include "base/global.h" #include "base/global.h"
#include "base/logger.h" #include "base/logger.h"
#include "base/net/downloadmanager.h" #include "base/net/downloadmanager.h"
@ -191,10 +192,10 @@ void TransferListFiltersWidget::refreshTrackers(const BitTorrent::Torrent *torre
m_trackersFilterWidget->refreshTrackers(torrent); m_trackersFilterWidget->refreshTrackers(torrent);
} }
void TransferListFiltersWidget::trackerEntriesUpdated(const BitTorrent::Torrent *torrent void TransferListFiltersWidget::trackerEntryStatusesUpdated(const BitTorrent::Torrent *torrent
, const QHash<QString, BitTorrent::TrackerEntry> &updatedTrackerEntries) , const QHash<QString, BitTorrent::TrackerEntryStatus> &updatedTrackers)
{ {
m_trackersFilterWidget->handleTrackerEntriesUpdated(torrent, updatedTrackerEntries); m_trackersFilterWidget->handleTrackerStatusesUpdated(torrent, updatedTrackers);
} }
void TransferListFiltersWidget::onCategoryFilterStateChanged(bool enabled) void TransferListFiltersWidget::onCategoryFilterStateChanged(bool enabled)

View file

@ -30,10 +30,8 @@
#pragma once #pragma once
#include <QtContainerFwd> #include <QtContainerFwd>
#include <QHash>
#include <QWidget> #include <QWidget>
#include "base/bittorrent/torrent.h"
#include "base/bittorrent/trackerentry.h" #include "base/bittorrent/trackerentry.h"
class CategoryFilterWidget; class CategoryFilterWidget;
@ -42,6 +40,12 @@ class TagFilterWidget;
class TrackersFilterWidget; class TrackersFilterWidget;
class TransferListWidget; class TransferListWidget;
namespace BitTorrent
{
class Torrent;
struct TrackerEntryStatus;
}
class TransferListFiltersWidget final : public QWidget class TransferListFiltersWidget final : public QWidget
{ {
Q_OBJECT Q_OBJECT
@ -55,8 +59,8 @@ public slots:
void addTrackers(const BitTorrent::Torrent *torrent, const QVector<BitTorrent::TrackerEntry> &trackers); void addTrackers(const BitTorrent::Torrent *torrent, const QVector<BitTorrent::TrackerEntry> &trackers);
void removeTrackers(const BitTorrent::Torrent *torrent, const QStringList &trackers); void removeTrackers(const BitTorrent::Torrent *torrent, const QStringList &trackers);
void refreshTrackers(const BitTorrent::Torrent *torrent); void refreshTrackers(const BitTorrent::Torrent *torrent);
void trackerEntriesUpdated(const BitTorrent::Torrent *torrent void trackerEntryStatusesUpdated(const BitTorrent::Torrent *torrent
, const QHash<QString, BitTorrent::TrackerEntry> &updatedTrackerEntries); , const QHash<QString, BitTorrent::TrackerEntryStatus> &updatedTrackers);
private slots: private slots:
void onCategoryFilterStateChanged(bool enabled); void onCategoryFilterStateChanged(bool enabled);

View file

@ -45,7 +45,7 @@
#include "base/bittorrent/session.h" #include "base/bittorrent/session.h"
#include "base/bittorrent/torrent.h" #include "base/bittorrent/torrent.h"
#include "base/bittorrent/trackerentry.h" #include "base/bittorrent/trackerentrystatus.h"
#include "base/global.h" #include "base/global.h"
#include "base/logger.h" #include "base/logger.h"
#include "base/path.h" #include "base/path.h"
@ -783,14 +783,14 @@ void TransferListWidget::editTorrentTrackers()
if (!torrents.empty()) if (!torrents.empty())
{ {
commonTrackers = torrents[0]->trackers(); for (const BitTorrent::TrackerEntryStatus &status : asConst(torrents[0]->trackers()))
commonTrackers.append({.url = status.url, .tier = status.tier});
for (const BitTorrent::Torrent *torrent : torrents) for (const BitTorrent::Torrent *torrent : torrents)
{ {
QSet<BitTorrent::TrackerEntry> trackerSet; QSet<BitTorrent::TrackerEntry> trackerSet;
for (const BitTorrent::TrackerEntryStatus &status : asConst(torrent->trackers()))
for (const BitTorrent::TrackerEntry &entry : asConst(torrent->trackers())) trackerSet.insert({.url = status.url, .tier = status.tier});
trackerSet.insert(entry);
commonTrackers.erase(std::remove_if(commonTrackers.begin(), commonTrackers.end() commonTrackers.erase(std::remove_if(commonTrackers.begin(), commonTrackers.end()
, [&trackerSet](const BitTorrent::TrackerEntry &entry) { return !trackerSet.contains(entry); }) , [&trackerSet](const BitTorrent::TrackerEntry &entry) { return !trackerSet.contains(entry); })

View file

@ -33,7 +33,7 @@
#include "base/bittorrent/infohash.h" #include "base/bittorrent/infohash.h"
#include "base/bittorrent/torrent.h" #include "base/bittorrent/torrent.h"
#include "base/bittorrent/trackerentry.h" #include "base/bittorrent/trackerentrystatus.h"
#include "base/path.h" #include "base/path.h"
#include "base/tagset.h" #include "base/tagset.h"
#include "base/utils/datetime.h" #include "base/utils/datetime.h"

View file

@ -43,7 +43,7 @@
#include "base/bittorrent/sessionstatus.h" #include "base/bittorrent/sessionstatus.h"
#include "base/bittorrent/torrent.h" #include "base/bittorrent/torrent.h"
#include "base/bittorrent/torrentinfo.h" #include "base/bittorrent/torrentinfo.h"
#include "base/bittorrent/trackerentry.h" #include "base/bittorrent/trackerentrystatus.h"
#include "base/global.h" #include "base/global.h"
#include "base/net/geoipmanager.h" #include "base/net/geoipmanager.h"
#include "base/preferences.h" #include "base/preferences.h"
@ -523,8 +523,8 @@ void SyncController::makeMaindataSnapshot()
QVariantMap serializedTorrent = serialize(*torrent); QVariantMap serializedTorrent = serialize(*torrent);
serializedTorrent.remove(KEY_TORRENT_ID); serializedTorrent.remove(KEY_TORRENT_ID);
for (const BitTorrent::TrackerEntry &tracker : asConst(torrent->trackers())) for (const BitTorrent::TrackerEntryStatus &status : asConst(torrent->trackers()))
m_knownTrackers[tracker.url].insert(torrentID); m_knownTrackers[status.url].insert(torrentID);
m_maindataSnapshot.torrents[torrentID.toString()] = serializedTorrent; m_maindataSnapshot.torrents[torrentID.toString()] = serializedTorrent;
} }
@ -834,11 +834,11 @@ void SyncController::onTorrentAdded(BitTorrent::Torrent *torrent)
m_removedTorrents.remove(torrentID); m_removedTorrents.remove(torrentID);
m_updatedTorrents.insert(torrentID); m_updatedTorrents.insert(torrentID);
for (const BitTorrent::TrackerEntry &trackerEntry : asConst(torrent->trackers())) for (const BitTorrent::TrackerEntryStatus &status : asConst(torrent->trackers()))
{ {
m_knownTrackers[trackerEntry.url].insert(torrentID); m_knownTrackers[status.url].insert(torrentID);
m_updatedTrackers.insert(trackerEntry.url); m_updatedTrackers.insert(status.url);
m_removedTrackers.remove(trackerEntry.url); m_removedTrackers.remove(status.url);
} }
} }
@ -849,9 +849,9 @@ void SyncController::onTorrentAboutToBeRemoved(BitTorrent::Torrent *torrent)
m_updatedTorrents.remove(torrentID); m_updatedTorrents.remove(torrentID);
m_removedTorrents.insert(torrentID); m_removedTorrents.insert(torrentID);
for (const BitTorrent::TrackerEntry &trackerEntry : asConst(torrent->trackers())) for (const BitTorrent::TrackerEntryStatus &status : asConst(torrent->trackers()))
{ {
auto iter = m_knownTrackers.find(trackerEntry.url); auto iter = m_knownTrackers.find(status.url);
Q_ASSERT(iter != m_knownTrackers.end()); Q_ASSERT(iter != m_knownTrackers.end());
if (iter == m_knownTrackers.end()) [[unlikely]] if (iter == m_knownTrackers.end()) [[unlikely]]
continue; continue;
@ -861,12 +861,12 @@ void SyncController::onTorrentAboutToBeRemoved(BitTorrent::Torrent *torrent)
if (torrentIDs.isEmpty()) if (torrentIDs.isEmpty())
{ {
m_knownTrackers.erase(iter); m_knownTrackers.erase(iter);
m_updatedTrackers.remove(trackerEntry.url); m_updatedTrackers.remove(status.url);
m_removedTrackers.insert(trackerEntry.url); m_removedTrackers.insert(status.url);
} }
else else
{ {
m_updatedTrackers.insert(trackerEntry.url); m_updatedTrackers.insert(status.url);
} }
} }
} }
@ -922,11 +922,12 @@ void SyncController::onTorrentTrackersChanged(BitTorrent::Torrent *torrent)
{ {
using namespace BitTorrent; using namespace BitTorrent;
const QVector<TrackerEntry> currentTrackerEntries = torrent->trackers(); const QVector<TrackerEntryStatus> trackers = torrent->trackers();
QSet<QString> currentTrackers; QSet<QString> currentTrackers;
currentTrackers.reserve(currentTrackerEntries.size()); currentTrackers.reserve(trackers.size());
for (const TrackerEntry &currentTrackerEntry : currentTrackerEntries) for (const TrackerEntryStatus &status : trackers)
currentTrackers.insert(currentTrackerEntry.url); currentTrackers.insert(status.url);
const TorrentID torrentID = torrent->id(); const TorrentID torrentID = torrent->id();
Algorithm::removeIf(m_knownTrackers Algorithm::removeIf(m_knownTrackers

View file

@ -49,6 +49,7 @@
#include "base/bittorrent/torrent.h" #include "base/bittorrent/torrent.h"
#include "base/bittorrent/torrentdescriptor.h" #include "base/bittorrent/torrentdescriptor.h"
#include "base/bittorrent/trackerentry.h" #include "base/bittorrent/trackerentry.h"
#include "base/bittorrent/trackerentrystatus.h"
#include "base/interfaces/iapplication.h" #include "base/interfaces/iapplication.h"
#include "base/global.h" #include "base/global.h"
#include "base/logger.h" #include "base/logger.h"
@ -194,7 +195,7 @@ namespace
} }
} }
const int working = static_cast<int>(BitTorrent::TrackerEntryStatus::Working); const int working = static_cast<int>(BitTorrent::TrackerEndpointState::Working);
const int disabled = 0; const int disabled = 0;
const QString privateMsg {QCoreApplication::translate("TrackerListWidget", "This torrent is private")}; const QString privateMsg {QCoreApplication::translate("TrackerListWidget", "This torrent is private")};
@ -500,16 +501,16 @@ void TorrentsController::trackersAction()
QJsonArray trackerList = getStickyTrackers(torrent); QJsonArray trackerList = getStickyTrackers(torrent);
for (const BitTorrent::TrackerEntry &tracker : asConst(torrent->trackers())) for (const BitTorrent::TrackerEntryStatus &tracker : asConst(torrent->trackers()))
{ {
const bool isNotWorking = (tracker.status == BitTorrent::TrackerEntryStatus::NotWorking) const bool isNotWorking = (tracker.state == BitTorrent::TrackerEndpointState::NotWorking)
|| (tracker.status == BitTorrent::TrackerEntryStatus::TrackerError) || (tracker.state == BitTorrent::TrackerEndpointState::TrackerError)
|| (tracker.status == BitTorrent::TrackerEntryStatus::Unreachable); || (tracker.state == BitTorrent::TrackerEndpointState::Unreachable);
trackerList << QJsonObject trackerList << QJsonObject
{ {
{KEY_TRACKER_URL, tracker.url}, {KEY_TRACKER_URL, tracker.url},
{KEY_TRACKER_TIER, tracker.tier}, {KEY_TRACKER_TIER, tracker.tier},
{KEY_TRACKER_STATUS, static_cast<int>((isNotWorking ? BitTorrent::TrackerEntryStatus::NotWorking : tracker.status))}, {KEY_TRACKER_STATUS, static_cast<int>((isNotWorking ? BitTorrent::TrackerEndpointState::NotWorking : tracker.state))},
{KEY_TRACKER_MSG, tracker.message}, {KEY_TRACKER_MSG, tracker.message},
{KEY_TRACKER_PEERS_COUNT, tracker.numPeers}, {KEY_TRACKER_PEERS_COUNT, tracker.numPeers},
{KEY_TRACKER_SEEDS_COUNT, tracker.numSeeds}, {KEY_TRACKER_SEEDS_COUNT, tracker.numSeeds},
@ -800,7 +801,7 @@ void TorrentsController::addTrackersAction()
if (!torrent) if (!torrent)
throw APIError(APIErrorType::NotFound); throw APIError(APIErrorType::NotFound);
const QVector<BitTorrent::TrackerEntry> entries = BitTorrent::parseTrackerEntries(params()[u"urls"_s]); const QList<BitTorrent::TrackerEntry> entries = BitTorrent::parseTrackerEntries(params()[u"urls"_s]);
torrent->addTrackers(entries); torrent->addTrackers(entries);
} }
@ -823,23 +824,35 @@ void TorrentsController::editTrackerAction()
if (!newTrackerUrl.isValid()) if (!newTrackerUrl.isValid())
throw APIError(APIErrorType::BadParams, u"New tracker URL is invalid"_s); throw APIError(APIErrorType::BadParams, u"New tracker URL is invalid"_s);
QVector<BitTorrent::TrackerEntry> trackers = torrent->trackers(); const QList<BitTorrent::TrackerEntryStatus> currentTrackers = torrent->trackers();
QList<BitTorrent::TrackerEntry> entries;
entries.reserve(currentTrackers.size());
bool match = false; bool match = false;
for (BitTorrent::TrackerEntry &tracker : trackers) for (const BitTorrent::TrackerEntryStatus &tracker : currentTrackers)
{ {
const QUrl trackerUrl {tracker.url}; const QUrl trackerUrl {tracker.url};
if (trackerUrl == newTrackerUrl) if (trackerUrl == newTrackerUrl)
throw APIError(APIErrorType::Conflict, u"New tracker URL already exists"_s); throw APIError(APIErrorType::Conflict, u"New tracker URL already exists"_s);
BitTorrent::TrackerEntry entry
{
.url = tracker.url,
.tier = tracker.tier
};
if (trackerUrl == origTrackerUrl) if (trackerUrl == origTrackerUrl)
{ {
match = true; match = true;
tracker.url = newTrackerUrl.toString(); entry.url = newTrackerUrl.toString();
} }
entries.append(entry);
} }
if (!match) if (!match)
throw APIError(APIErrorType::Conflict, u"Tracker not found"_s); throw APIError(APIErrorType::Conflict, u"Tracker not found"_s);
torrent->replaceTrackers(trackers); torrent->replaceTrackers(entries);
if (!torrent->isStopped()) if (!torrent->isStopped())
torrent->forceReannounce(); torrent->forceReannounce();