Improve tracker entries handling

PR #19496.

* Add torrent entry status to represent tracker error
* Add torrent entry status to represent unreachable endpoint
* Display tracker entry next/min announce time
* Reset tracker entries when torrent is stopped
This commit is contained in:
Vladimir Golovnev
2023-09-07 08:58:13 +03:00
committed by GitHub
parent 2f94c92df9
commit 7cd2445a49
11 changed files with 239 additions and 69 deletions

View File

@@ -4841,6 +4841,15 @@ void SessionImpl::handleTorrentMetadataReceived(TorrentImpl *const torrent)
void SessionImpl::handleTorrentPaused(TorrentImpl *const torrent)
{
torrent->resetTrackerEntries();
const auto &trackerEntries = torrent->trackers();
QHash<QString, TrackerEntry> updatedTrackerEntries;
updatedTrackerEntries.reserve(trackerEntries.size());
for (const auto &trackerEntry : trackerEntries)
updatedTrackerEntries.emplace(trackerEntry.url, trackerEntry);
emit trackerEntriesUpdated(torrent, updatedTrackerEntries);
LogMsg(tr("Torrent paused. Torrent: \"%1\"").arg(torrent->name()));
emit torrentPaused(torrent);
}
@@ -5985,39 +5994,7 @@ void SessionImpl::processTrackerStatuses()
for (auto it = m_updatedTrackerEntries.cbegin(); it != m_updatedTrackerEntries.cend(); ++it)
{
invokeAsync([this, torrentHandle = it.key(), updatedTrackers = it.value()]() mutable
{
try
{
std::vector<lt::announce_entry> nativeTrackers = torrentHandle.trackers();
invoke([this, torrentHandle, nativeTrackers = std::move(nativeTrackers)
, updatedTrackers = std::move(updatedTrackers)]
{
TorrentImpl *torrent = m_torrents.value(torrentHandle.info_hash());
if (!torrent)
return;
QHash<QString, TrackerEntry> updatedTrackerEntries;
updatedTrackerEntries.reserve(updatedTrackers.size());
for (const lt::announce_entry &announceEntry : nativeTrackers)
{
const auto updatedTrackersIter = updatedTrackers.find(announceEntry.url);
if (updatedTrackersIter == updatedTrackers.end())
continue;
const auto &updateInfo = updatedTrackersIter.value();
TrackerEntry trackerEntry = torrent->updateTrackerEntry(announceEntry, updateInfo);
const QString url = trackerEntry.url;
updatedTrackerEntries.emplace(url, std::move(trackerEntry));
}
emit trackerEntriesUpdated(torrent, updatedTrackerEntries);
});
}
catch (const std::exception &)
{
}
});
updateTrackerEntries(it.key(), it.value());
}
m_updatedTrackerEntries.clear();
@@ -6046,3 +6023,40 @@ void SessionImpl::loadStatistics()
m_previouslyDownloaded = value[u"AlltimeDL"_s].toLongLong();
m_previouslyUploaded = value[u"AlltimeUL"_s].toLongLong();
}
void SessionImpl::updateTrackerEntries(lt::torrent_handle torrentHandle, QHash<std::string, QHash<TrackerEntry::Endpoint, QMap<int, int>>> updatedTrackers)
{
invokeAsync([this, torrentHandle = std::move(torrentHandle), updatedTrackers = std::move(updatedTrackers)]() mutable
{
try
{
std::vector<lt::announce_entry> nativeTrackers = torrentHandle.trackers();
invoke([this, torrentHandle, nativeTrackers = std::move(nativeTrackers)
, updatedTrackers = std::move(updatedTrackers)]
{
TorrentImpl *torrent = m_torrents.value(torrentHandle.info_hash());
if (!torrent || torrent->isPaused())
return;
QHash<QString, TrackerEntry> updatedTrackerEntries;
updatedTrackerEntries.reserve(updatedTrackers.size());
for (const lt::announce_entry &announceEntry : nativeTrackers)
{
const auto updatedTrackersIter = updatedTrackers.find(announceEntry.url);
if (updatedTrackersIter == updatedTrackers.end())
continue;
const auto &updateInfo = updatedTrackersIter.value();
TrackerEntry trackerEntry = torrent->updateTrackerEntry(announceEntry, updateInfo);
const QString url = trackerEntry.url;
updatedTrackerEntries.emplace(url, std::move(trackerEntry));
}
emit trackerEntriesUpdated(torrent, updatedTrackerEntries);
});
}
catch (const std::exception &)
{
}
});
}

View File

@@ -572,6 +572,8 @@ namespace BitTorrent
void saveStatistics() const;
void loadStatistics();
void updateTrackerEntries(lt::torrent_handle torrentHandle, QHash<std::string, QHash<TrackerEntry::Endpoint, QMap<int, int>>> updatedTrackers);
// BitTorrent
lt::session *m_nativeSession = nullptr;
NativeSessionExtension *m_nativeSessionExtension = nullptr;

View File

@@ -78,6 +78,15 @@ namespace
return entry;
}
QDateTime fromLTTimePoint32(const lt::time_point32 &timePoint)
{
const auto ltNow = lt::clock_type::now();
const auto qNow = QDateTime::currentDateTime();
const auto secsSinceNow = lt::duration_cast<lt::seconds>(timePoint - ltNow + lt::milliseconds(500)).count();
return qNow.addSecs(secsSinceNow);
}
#ifdef QBT_USES_LIBTORRENT2
void updateTrackerEntry(TrackerEntry &trackerEntry, const lt::announce_entry &nativeEntry
, const lt::info_hash_t &hashes, const QHash<TrackerEntry::Endpoint, QMap<int, int>> &updateInfo)
@@ -103,8 +112,8 @@ namespace
int numUpdating = 0;
int numWorking = 0;
int numNotWorking = 0;
QString firstTrackerMessage;
QString firstErrorMessage;
int numTrackerError = 0;
int numUnreachable = 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)
@@ -127,6 +136,8 @@ namespace
trackerEndpoint.numSeeds = infoHash.scrape_complete;
trackerEndpoint.numLeeches = infoHash.scrape_incomplete;
trackerEndpoint.numDownloaded = infoHash.scrape_downloaded;
trackerEndpoint.nextAnnounceTime = fromLTTimePoint32(infoHash.next_announce);
trackerEndpoint.minAnnounceTime = fromLTTimePoint32(infoHash.min_announce);
if (infoHash.updating)
{
@@ -135,8 +146,21 @@ namespace
}
else if (infoHash.fails > 0)
{
trackerEndpoint.status = TrackerEntry::NotWorking;
++numNotWorking;
if (infoHash.last_error == lt::errors::tracker_failure)
{
trackerEndpoint.status = TrackerEntry::TrackerError;
++numTrackerError;
}
else if (infoHash.last_error == lt::errors::announce_skipped)
{
trackerEndpoint.status = TrackerEntry::Unreachable;
++numUnreachable;
}
else
{
trackerEndpoint.status = TrackerEntry::NotWorking;
++numNotWorking;
}
}
else if (nativeEntry.verified)
{
@@ -151,14 +175,10 @@ namespace
if (!infoHash.message.empty())
{
trackerEndpoint.message = QString::fromStdString(infoHash.message);
if (firstTrackerMessage.isEmpty())
firstTrackerMessage = trackerEndpoint.message;
}
else if (infoHash.last_error)
{
trackerEndpoint.message = QString::fromLocal8Bit(infoHash.last_error.message());
if (firstErrorMessage.isEmpty())
firstErrorMessage = trackerEndpoint.message;
}
}
}
@@ -175,6 +195,8 @@ namespace
trackerEndpoint.numSeeds = endpoint.scrape_complete;
trackerEndpoint.numLeeches = endpoint.scrape_incomplete;
trackerEndpoint.numDownloaded = endpoint.scrape_downloaded;
trackerEndpoint.nextAnnounceTime = fromLTTimePoint32(endpoint.next_announce);
trackerEndpoint.minAnnounceTime = fromLTTimePoint32(endpoint.min_announce);
if (endpoint.updating)
{
@@ -183,8 +205,21 @@ namespace
}
else if (endpoint.fails > 0)
{
trackerEndpoint.status = TrackerEntry::NotWorking;
++numNotWorking;
if (endpoint.last_error == lt::errors::tracker_failure)
{
trackerEndpoint.status = TrackerEntry::TrackerError;
++numTrackerError;
}
else if (endpoint.last_error == lt::errors::announce_skipped)
{
trackerEndpoint.status = TrackerEntry::Unreachable;
++numUnreachable;
}
else
{
trackerEndpoint.status = TrackerEntry::NotWorking;
++numNotWorking;
}
}
else if (nativeEntry.verified)
{
@@ -199,14 +234,10 @@ namespace
if (!endpoint.message.empty())
{
trackerEndpoint.message = QString::fromStdString(endpoint.message);
if (firstTrackerMessage.isEmpty())
firstTrackerMessage = trackerEndpoint.message;
}
else if (endpoint.last_error)
{
trackerEndpoint.message = QString::fromLocal8Bit(endpoint.last_error.message());
if (firstErrorMessage.isEmpty())
firstErrorMessage = trackerEndpoint.message;
}
}
#endif
@@ -220,12 +251,18 @@ namespace
else if (numWorking > 0)
{
trackerEntry.status = TrackerEntry::Working;
trackerEntry.message = firstTrackerMessage;
}
else if (numNotWorking == numEndpoints)
else if (numTrackerError > 0)
{
trackerEntry.status = TrackerEntry::TrackerError;
}
else if (numUnreachable == numEndpoints)
{
trackerEntry.status = TrackerEntry::Unreachable;
}
else if ((numUnreachable + numNotWorking) == numEndpoints)
{
trackerEntry.status = TrackerEntry::NotWorking;
trackerEntry.message = (!firstTrackerMessage.isEmpty() ? firstTrackerMessage : firstErrorMessage);
}
}
}
@@ -1639,6 +1676,12 @@ TrackerEntry TorrentImpl::updateTrackerEntry(const lt::announce_entry &announceE
return *it;
}
void TorrentImpl::resetTrackerEntries()
{
for (auto &trackerEntry : m_trackerEntries)
trackerEntry = {trackerEntry.url, trackerEntry.tier};
}
std::shared_ptr<const libtorrent::torrent_info> TorrentImpl::nativeTorrentInfo() const
{
if (m_nativeStatus.torrent_file.expired())

View File

@@ -264,7 +264,8 @@ namespace BitTorrent
void saveResumeData(lt::resume_data_flags_t flags = {});
void handleMoveStorageJobFinished(const Path &path, MoveStorageContext context, bool hasOutstandingJob);
void fileSearchFinished(const Path &savePath, const PathList &fileNames);
TrackerEntry updateTrackerEntry(const lt::announce_entry &announceEntry, const QHash<TrackerEntry::Endpoint, QMap<int, int> > &updateInfo);
TrackerEntry updateTrackerEntry(const lt::announce_entry &announceEntry, const QHash<TrackerEntry::Endpoint, QMap<int, int>> &updateInfo);
void resetTrackerEntries();
private:
using EventTrigger = std::function<void ()>;

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015-2022 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
* modify it under the terms of the GNU General Public License
@@ -31,6 +31,7 @@
#include <libtorrent/socket.hpp>
#include <QtContainerFwd>
#include <QDateTime>
#include <QHash>
#include <QString>
#include <QStringView>
@@ -46,24 +47,30 @@ namespace BitTorrent
NotContacted = 1,
Working = 2,
Updating = 3,
NotWorking = 4
NotWorking = 4,
TrackerError = 5,
Unreachable = 6
};
struct EndpointStats
{
QString name {};
Status status = NotContacted;
QString message {};
int numPeers = -1;
int numSeeds = -1;
int numLeeches = -1;
int numDownloaded = -1;
QString message {};
QString name {};
QDateTime nextAnnounceTime;
QDateTime minAnnounceTime;
};
QString url {};
int tier = 0;
Status status = NotContacted;
QString message {};
QHash<Endpoint, QHash<int, EndpointStats>> stats {};
};