Revamp tracker list widget

Internally redesign tracker list widget using Qt Model/View architecture.
Make tracker list sortable by any column.

PR #19633.
Closes #261.
This commit is contained in:
Vladimir Golovnev
2023-10-03 08:42:05 +03:00
committed by GitHub
parent 70b438e6d9
commit c051ee9409
30 changed files with 1786 additions and 1106 deletions

View File

@@ -87,190 +87,167 @@ namespace
return qNow.addSecs(secsSinceNow);
}
#ifdef QBT_USES_LIBTORRENT2
QString toString(const lt::tcp::endpoint &ltTCPEndpoint)
{
return QString::fromStdString((std::stringstream() << ltTCPEndpoint).str());
}
void updateTrackerEntry(TrackerEntry &trackerEntry, const lt::announce_entry &nativeEntry
, const lt::info_hash_t &hashes, const QHash<TrackerEntry::Endpoint, QMap<int, int>> &updateInfo)
#else
void updateTrackerEntry(TrackerEntry &trackerEntry, const lt::announce_entry &nativeEntry
, const QHash<TrackerEntry::Endpoint, QMap<int, int>> &updateInfo)
#endif
, const QSet<int> &btProtocols, const QHash<lt::tcp::endpoint, QMap<int, int>> &updateInfo)
{
Q_ASSERT(trackerEntry.url == QString::fromStdString(nativeEntry.url));
trackerEntry.tier = nativeEntry.tier;
// remove outdated endpoints
trackerEntry.stats.removeIf([&nativeEntry](const decltype(trackerEntry.stats)::iterator &iter)
trackerEntry.endpointEntries.removeIf([&nativeEntry](const QHash<std::pair<QString, int>, TrackerEndpointEntry>::iterator &iter)
{
return std::none_of(nativeEntry.endpoints.cbegin(), nativeEntry.endpoints.cend()
, [&endpoint = iter.key()](const auto &existingEndpoint)
, [&endpointName = std::get<0>(iter.key())](const auto &existingEndpoint)
{
return (endpoint == existingEndpoint.local_endpoint);
return (endpointName == toString(existingEndpoint.local_endpoint));
});
});
const auto numEndpoints = static_cast<qsizetype>(nativeEntry.endpoints.size()) * btProtocols.size();
int numUpdating = 0;
int numWorking = 0;
int numNotWorking = 0;
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)
for (const lt::announce_endpoint &ltAnnounceEndpoint : nativeEntry.endpoints)
{
const auto endpointName = QString::fromStdString((std::stringstream() << endpoint.local_endpoint).str());
const auto endpointName = toString(ltAnnounceEndpoint.local_endpoint);
for (const auto protocolVersion : {lt::protocol_version::V1, lt::protocol_version::V2})
for (const auto protocolVersion : btProtocols)
{
if (!hashes.has(protocolVersion))
continue;
#ifdef QBT_USES_LIBTORRENT2
Q_ASSERT((protocolVersion == 1) || (protocolVersion == 2));
const auto ltProtocolVersion = (protocolVersion == 1) ? lt::protocol_version::V1 : lt::protocol_version::V2;
const lt::announce_infohash &ltAnnounceInfo = ltAnnounceEndpoint.info_hashes[ltProtocolVersion];
#else
Q_ASSERT(protocolVersion == 1);
const lt::announce_endpoint &ltAnnounceInfo = ltAnnounceEndpoint;
#endif
const QMap<int, int> &endpointUpdateInfo = updateInfo[ltAnnounceEndpoint.local_endpoint];
TrackerEndpointEntry &trackerEndpointEntry = trackerEntry.endpointEntries[std::make_pair(endpointName, protocolVersion)];
const lt::announce_infohash &infoHash = endpoint.info_hashes[protocolVersion];
trackerEndpointEntry.name = endpointName;
trackerEndpointEntry.btVersion = protocolVersion;
trackerEndpointEntry.numPeers = endpointUpdateInfo.value(protocolVersion, trackerEndpointEntry.numPeers);
trackerEndpointEntry.numSeeds = ltAnnounceInfo.scrape_complete;
trackerEndpointEntry.numLeeches = ltAnnounceInfo.scrape_incomplete;
trackerEndpointEntry.numDownloaded = ltAnnounceInfo.scrape_downloaded;
trackerEndpointEntry.nextAnnounceTime = fromLTTimePoint32(ltAnnounceInfo.next_announce);
trackerEndpointEntry.minAnnounceTime = fromLTTimePoint32(ltAnnounceInfo.min_announce);
const int protocolVersionNum = (protocolVersion == lt::protocol_version::V1) ? 1 : 2;
const QMap<int, int> &endpointUpdateInfo = updateInfo[endpoint.local_endpoint];
TrackerEntry::EndpointStats &trackerEndpoint = trackerEntry.stats[endpoint.local_endpoint][protocolVersionNum];
trackerEndpoint.name = endpointName;
trackerEndpoint.numPeers = endpointUpdateInfo.value(protocolVersionNum, trackerEndpoint.numPeers);
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)
if (ltAnnounceInfo.updating)
{
trackerEndpoint.status = TrackerEntry::Updating;
trackerEndpointEntry.status = TrackerEntryStatus::Updating;
++numUpdating;
}
else if (infoHash.fails > 0)
else if (ltAnnounceInfo.fails > 0)
{
if (infoHash.last_error == lt::errors::tracker_failure)
if (ltAnnounceInfo.last_error == lt::errors::tracker_failure)
{
trackerEndpoint.status = TrackerEntry::TrackerError;
trackerEndpointEntry.status = TrackerEntryStatus::TrackerError;
++numTrackerError;
}
else if (infoHash.last_error == lt::errors::announce_skipped)
else if (ltAnnounceInfo.last_error == lt::errors::announce_skipped)
{
trackerEndpoint.status = TrackerEntry::Unreachable;
trackerEndpointEntry.status = TrackerEntryStatus::Unreachable;
++numUnreachable;
}
else
{
trackerEndpoint.status = TrackerEntry::NotWorking;
trackerEndpointEntry.status = TrackerEntryStatus::NotWorking;
++numNotWorking;
}
}
else if (nativeEntry.verified)
{
trackerEndpoint.status = TrackerEntry::Working;
trackerEndpointEntry.status = TrackerEntryStatus::Working;
++numWorking;
}
else
{
trackerEndpoint.status = TrackerEntry::NotContacted;
trackerEndpointEntry.status = TrackerEntryStatus::NotContacted;
}
if (!infoHash.message.empty())
if (!ltAnnounceInfo.message.empty())
{
trackerEndpoint.message = QString::fromStdString(infoHash.message);
trackerEndpointEntry.message = QString::fromStdString(ltAnnounceInfo.message);
}
else if (infoHash.last_error)
else if (ltAnnounceInfo.last_error)
{
trackerEndpoint.message = QString::fromLocal8Bit(infoHash.last_error.message());
trackerEndpointEntry.message = QString::fromLocal8Bit(ltAnnounceInfo.last_error.message());
}
else
{
trackerEndpoint.message.clear();
trackerEndpointEntry.message.clear();
}
}
}
#else
const auto numEndpoints = static_cast<qsizetype>(nativeEntry.endpoints.size());
for (const lt::announce_endpoint &endpoint : nativeEntry.endpoints)
{
const int protocolVersionNum = 1;
const QMap<int, int> &endpointUpdateInfo = updateInfo[endpoint.local_endpoint];
TrackerEntry::EndpointStats &trackerEndpoint = trackerEntry.stats[endpoint.local_endpoint][protocolVersionNum];
trackerEndpoint.name = QString::fromStdString((std::stringstream() << endpoint.local_endpoint).str());
trackerEndpoint.numPeers = endpointUpdateInfo.value(protocolVersionNum, trackerEndpoint.numPeers);
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)
{
trackerEndpoint.status = TrackerEntry::Updating;
++numUpdating;
}
else if (endpoint.fails > 0)
{
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)
{
trackerEndpoint.status = TrackerEntry::Working;
++numWorking;
}
else
{
trackerEndpoint.status = TrackerEntry::NotContacted;
}
if (!endpoint.message.empty())
{
trackerEndpoint.message = QString::fromStdString(endpoint.message);
}
else if (endpoint.last_error)
{
trackerEndpoint.message = QString::fromLocal8Bit(endpoint.last_error.message());
}
else
{
trackerEndpoint.message.clear();
}
}
#endif
if (numEndpoints > 0)
{
if (numUpdating > 0)
{
trackerEntry.status = TrackerEntry::Updating;
trackerEntry.status = TrackerEntryStatus::Updating;
}
else if (numWorking > 0)
{
trackerEntry.status = TrackerEntry::Working;
trackerEntry.status = TrackerEntryStatus::Working;
}
else if (numTrackerError > 0)
{
trackerEntry.status = TrackerEntry::TrackerError;
trackerEntry.status = TrackerEntryStatus::TrackerError;
}
else if (numUnreachable == numEndpoints)
{
trackerEntry.status = TrackerEntry::Unreachable;
trackerEntry.status = TrackerEntryStatus::Unreachable;
}
else if ((numUnreachable + numNotWorking) == numEndpoints)
{
trackerEntry.status = TrackerEntry::NotWorking;
trackerEntry.status = TrackerEntryStatus::NotWorking;
}
}
trackerEntry.numPeers = -1;
trackerEntry.numSeeds = -1;
trackerEntry.numLeeches = -1;
trackerEntry.numDownloaded = -1;
trackerEntry.nextAnnounceTime = QDateTime();
trackerEntry.minAnnounceTime = QDateTime();
trackerEntry.message.clear();
for (const TrackerEndpointEntry &endpointEntry : asConst(trackerEntry.endpointEntries))
{
trackerEntry.numPeers = std::max(trackerEntry.numPeers, endpointEntry.numPeers);
trackerEntry.numSeeds = std::max(trackerEntry.numSeeds, endpointEntry.numSeeds);
trackerEntry.numLeeches = std::max(trackerEntry.numLeeches, endpointEntry.numLeeches);
trackerEntry.numDownloaded = std::max(trackerEntry.numDownloaded, endpointEntry.numDownloaded);
if (endpointEntry.status == trackerEntry.status)
{
if (!trackerEntry.nextAnnounceTime.isValid() || (trackerEntry.nextAnnounceTime > endpointEntry.nextAnnounceTime))
{
trackerEntry.nextAnnounceTime = endpointEntry.nextAnnounceTime;
trackerEntry.minAnnounceTime = endpointEntry.minAnnounceTime;
if ((endpointEntry.status != TrackerEntryStatus::Working)
|| !endpointEntry.message.isEmpty())
{
trackerEntry.message = endpointEntry.message;
}
}
if (endpointEntry.status == TrackerEntryStatus::Working)
{
if (trackerEntry.message.isEmpty())
trackerEntry.message = endpointEntry.message;
}
}
}
}
@@ -405,6 +382,11 @@ bool TorrentImpl::isValid() const
return m_nativeHandle.is_valid();
}
Session *TorrentImpl::session() const
{
return m_session;
}
InfoHash TorrentImpl::infoHash() const
{
return m_infoHash;
@@ -1667,7 +1649,7 @@ void TorrentImpl::fileSearchFinished(const Path &savePath, const PathList &fileN
endReceivedMetadataHandling(savePath, fileNames);
}
TrackerEntry TorrentImpl::updateTrackerEntry(const lt::announce_entry &announceEntry, const QHash<TrackerEntry::Endpoint, QMap<int, int>> &updateInfo)
TrackerEntry TorrentImpl::updateTrackerEntry(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()
, [&announceEntry](const TrackerEntry &trackerEntry)
@@ -1680,10 +1662,16 @@ TrackerEntry TorrentImpl::updateTrackerEntry(const lt::announce_entry &announceE
return {};
#ifdef QBT_USES_LIBTORRENT2
::updateTrackerEntry(*it, announceEntry, nativeHandle().info_hashes(), updateInfo);
QSet<int> btProtocols;
const auto &infoHashes = nativeHandle().info_hashes();
if (infoHashes.has(lt::protocol_version::V1))
btProtocols.insert(1);
if (infoHashes.has(lt::protocol_version::V2))
btProtocols.insert(2);
#else
::updateTrackerEntry(*it, announceEntry, updateInfo);
const QSet<int> btProtocols {1};
#endif
::updateTrackerEntry(*it, announceEntry, btProtocols, updateInfo);
return *it;
}