diff --git a/WebAPI_Changelog.md b/WebAPI_Changelog.md index 3dc7109ad40e..7c9636761ff5 100644 --- a/WebAPI_Changelog.md +++ b/WebAPI_Changelog.md @@ -2,6 +2,8 @@ ## 2.16.0 +* [#24388](https://github.com/qbittorrent/qBittorrent/pull/24388) + * `torrents/trackers` endpoint now includes `num_fails` field for tracker entries, reporting the number of consecutive failed announce attempts * [#23989](https://github.com/qbittorrent/qBittorrent/pull/23989) * `sync/torrentPeers` endpoint now includes calculated `contribution` to a peer's progress * [#24346](https://github.com/qbittorrent/qBittorrent/pull/24346) diff --git a/src/base/bittorrent/torrentimpl.cpp b/src/base/bittorrent/torrentimpl.cpp index 4d2b875456b3..8e619597fe3b 100644 --- a/src/base/bittorrent/torrentimpl.cpp +++ b/src/base/bittorrent/torrentimpl.cpp @@ -147,6 +147,7 @@ namespace trackerEndpointStatus.numSeeds = ltAnnounceInfo.scrape_complete; trackerEndpointStatus.numLeeches = ltAnnounceInfo.scrape_incomplete; trackerEndpointStatus.numDownloaded = ltAnnounceInfo.scrape_downloaded; + trackerEndpointStatus.numFails = ltAnnounceInfo.fails; trackerEndpointStatus.nextAnnounceTime = ltAnnounceInfo.next_announce; trackerEndpointStatus.minAnnounceTime = ltAnnounceInfo.min_announce; @@ -249,6 +250,7 @@ namespace trackerEntryStatus.numSeeds = -1; trackerEntryStatus.numLeeches = -1; trackerEntryStatus.numDownloaded = -1; + trackerEntryStatus.numFails = -1; trackerEntryStatus.nextAnnounceTime = {}; trackerEntryStatus.minAnnounceTime = {}; trackerEntryStatus.message.clear(); @@ -259,6 +261,12 @@ namespace trackerEntryStatus.numSeeds = std::max(trackerEntryStatus.numSeeds, endpointStatus.numSeeds); trackerEntryStatus.numLeeches = std::max(trackerEntryStatus.numLeeches, endpointStatus.numLeeches); trackerEntryStatus.numDownloaded = std::max(trackerEntryStatus.numDownloaded, endpointStatus.numDownloaded); + if (endpointStatus.numFails >= 0) + { + trackerEntryStatus.numFails = (trackerEntryStatus.numFails < 0) + ? endpointStatus.numFails + : std::min(trackerEntryStatus.numFails, endpointStatus.numFails); + } if (endpointStatus.state == trackerEntryStatus.state) { diff --git a/src/base/bittorrent/trackerentrystatus.cpp b/src/base/bittorrent/trackerentrystatus.cpp index 35acd09d374a..8663d758ebc7 100644 --- a/src/base/bittorrent/trackerentrystatus.cpp +++ b/src/base/bittorrent/trackerentrystatus.cpp @@ -38,6 +38,7 @@ void BitTorrent::TrackerEntryStatus::clear() numSeeds = -1; numLeeches = -1; numDownloaded = -1; + numFails = -1; nextAnnounceTime = {}; minAnnounceTime = {}; endpoints.clear(); diff --git a/src/base/bittorrent/trackerentrystatus.h b/src/base/bittorrent/trackerentrystatus.h index 026580623ced..0d74c6a74ebf 100644 --- a/src/base/bittorrent/trackerentrystatus.h +++ b/src/base/bittorrent/trackerentrystatus.h @@ -59,6 +59,7 @@ namespace BitTorrent int numSeeds = -1; int numLeeches = -1; int numDownloaded = -1; + int numFails = -1; AnnounceTimePoint nextAnnounceTime {}; AnnounceTimePoint minAnnounceTime {}; @@ -77,6 +78,7 @@ namespace BitTorrent int numSeeds = -1; int numLeeches = -1; int numDownloaded = -1; + int numFails = -1; AnnounceTimePoint nextAnnounceTime {}; AnnounceTimePoint minAnnounceTime {}; diff --git a/src/gui/trackerlist/trackerlistmodel.cpp b/src/gui/trackerlist/trackerlistmodel.cpp index e03269430370..0500a0a91f20 100644 --- a/src/gui/trackerlist/trackerlistmodel.cpp +++ b/src/gui/trackerlist/trackerlistmodel.cpp @@ -124,6 +124,7 @@ struct TrackerListModel::Item final int numSeeds = -1; int numLeeches = -1; int numDownloaded = -1; + int numFails = -1; BitTorrent::AnnounceTimePoint nextAnnounceTime; BitTorrent::AnnounceTimePoint minAnnounceTime; @@ -190,6 +191,7 @@ void TrackerListModel::Item::fillFrom(const BitTorrent::TrackerEntryStatus &trac numSeeds = trackerEntryStatus.numSeeds; numLeeches = trackerEntryStatus.numLeeches; numDownloaded = trackerEntryStatus.numDownloaded; + numFails = trackerEntryStatus.numFails; nextAnnounceTime = trackerEntryStatus.nextAnnounceTime; minAnnounceTime = trackerEntryStatus.minAnnounceTime; secsToNextAnnounce = 0; @@ -210,6 +212,7 @@ void TrackerListModel::Item::fillFrom(const BitTorrent::TrackerEndpointStatus &e numSeeds = endpointStatus.numSeeds; numLeeches = endpointStatus.numLeeches; numDownloaded = endpointStatus.numDownloaded; + numFails = endpointStatus.numFails; nextAnnounceTime = endpointStatus.nextAnnounceTime; minAnnounceTime = endpointStatus.minAnnounceTime; secsToNextAnnounce = 0; @@ -515,6 +518,8 @@ QVariant TrackerListModel::headerData(const int section, const Qt::Orientation o return tr("Leeches"); case COL_TIMES_DOWNLOADED: return tr("Times Downloaded"); + case COL_RETRIES: + return tr("Retries"); case COL_MSG: return tr("Message"); case COL_NEXT_ANNOUNCE: @@ -533,6 +538,7 @@ QVariant TrackerListModel::headerData(const int section, const Qt::Orientation o case COL_SEEDS: case COL_LEECHES: case COL_TIMES_DOWNLOADED: + case COL_RETRIES: case COL_NEXT_ANNOUNCE: case COL_MIN_ANNOUNCE: return QVariant {Qt::AlignRight | Qt::AlignVCenter}; @@ -579,6 +585,7 @@ QVariant TrackerListModel::data(const QModelIndex &index, const int role) const case COL_SEEDS: case COL_LEECHES: case COL_TIMES_DOWNLOADED: + case COL_RETRIES: case COL_NEXT_ANNOUNCE: case COL_MIN_ANNOUNCE: return QVariant {Qt::AlignRight | Qt::AlignVCenter}; @@ -620,6 +627,8 @@ QVariant TrackerListModel::data(const QModelIndex &index, const int role) const return prettyCount(itemPtr->numLeeches); case COL_TIMES_DOWNLOADED: return prettyCount(itemPtr->numDownloaded); + case COL_RETRIES: + return prettyCount(itemPtr->numFails); case COL_MSG: return itemPtr->message; case COL_NEXT_ANNOUNCE: @@ -649,6 +658,8 @@ QVariant TrackerListModel::data(const QModelIndex &index, const int role) const return itemPtr->numLeeches; case COL_TIMES_DOWNLOADED: return itemPtr->numDownloaded; + case COL_RETRIES: + return itemPtr->numFails; case COL_MSG: return itemPtr->message; case COL_NEXT_ANNOUNCE: diff --git a/src/gui/trackerlist/trackerlistmodel.h b/src/gui/trackerlist/trackerlistmodel.h index aa557182ce98..5a5a19efd504 100644 --- a/src/gui/trackerlist/trackerlistmodel.h +++ b/src/gui/trackerlist/trackerlistmodel.h @@ -62,6 +62,7 @@ class TrackerListModel final : public QAbstractItemModel COL_SEEDS, COL_LEECHES, COL_TIMES_DOWNLOADED, + COL_RETRIES, COL_MSG, COL_NEXT_ANNOUNCE, COL_MIN_ANNOUNCE, diff --git a/src/webui/api/torrentscontroller.cpp b/src/webui/api/torrentscontroller.cpp index 55e1ecde7b12..49049f8cabca 100644 --- a/src/webui/api/torrentscontroller.cpp +++ b/src/webui/api/torrentscontroller.cpp @@ -82,6 +82,7 @@ const QString KEY_TRACKER_PEERS_COUNT = u"num_peers"_s; const QString KEY_TRACKER_SEEDS_COUNT = u"num_seeds"_s; const QString KEY_TRACKER_LEECHES_COUNT = u"num_leeches"_s; const QString KEY_TRACKER_DOWNLOADED_COUNT = u"num_downloaded"_s; +const QString KEY_TRACKER_FAIL_COUNT = u"num_fails"_s; const QString KEY_TRACKER_NEXT_ANNOUNCE = u"next_announce"_s; const QString KEY_TRACKER_MIN_ANNOUNCE = u"min_announce"_s; const QString KEY_TRACKER_ENDPOINTS = u"endpoints"_s; @@ -306,6 +307,7 @@ namespace {KEY_TRACKER_SEEDS_COUNT, endpoint.numSeeds}, {KEY_TRACKER_LEECHES_COUNT, endpoint.numLeeches}, {KEY_TRACKER_DOWNLOADED_COUNT, endpoint.numDownloaded}, + {KEY_TRACKER_FAIL_COUNT, endpoint.numFails}, {KEY_TRACKER_NEXT_ANNOUNCE, toSecondsSinceEpoch(endpoint.nextAnnounceTime)}, {KEY_TRACKER_MIN_ANNOUNCE, toSecondsSinceEpoch(endpoint.minAnnounceTime)} }; @@ -322,6 +324,7 @@ namespace {KEY_TRACKER_SEEDS_COUNT, tracker.numSeeds}, {KEY_TRACKER_LEECHES_COUNT, tracker.numLeeches}, {KEY_TRACKER_DOWNLOADED_COUNT, tracker.numDownloaded}, + {KEY_TRACKER_FAIL_COUNT, tracker.numFails}, {KEY_TRACKER_NEXT_ANNOUNCE, toSecondsSinceEpoch(tracker.nextAnnounceTime)}, {KEY_TRACKER_MIN_ANNOUNCE, toSecondsSinceEpoch(tracker.minAnnounceTime)}, {KEY_TRACKER_ENDPOINTS, endpointsList} diff --git a/src/webui/www/private/scripts/dynamicTable.js b/src/webui/www/private/scripts/dynamicTable.js index a649f7b91be4..254e0edddf6b 100644 --- a/src/webui/www/private/scripts/dynamicTable.js +++ b/src/webui/www/private/scripts/dynamicTable.js @@ -2185,6 +2185,7 @@ window.qBittorrent.DynamicTable ??= (() => { this.newColumn("seeds", "", "QBT_TR(Seeds)QBT_TR[CONTEXT=TrackerListWidget]", 75, true); this.newColumn("leeches", "", "QBT_TR(Leeches)QBT_TR[CONTEXT=TrackerListWidget]", 75, true); this.newColumn("downloaded", "", "QBT_TR(Times Downloaded)QBT_TR[CONTEXT=TrackerListWidget]", 100, true); + this.newColumn("fails", "", "QBT_TR(Retries)QBT_TR[CONTEXT=TrackerListWidget]", 50, true); this.newColumn("message", "", "QBT_TR(Message)QBT_TR[CONTEXT=TrackerListWidget]", 250, true); this.newColumn("nextAnnounce", "", "QBT_TR(Next Announce)QBT_TR[CONTEXT=TrackerListWidget]", 150, true); this.newColumn("minAnnounce", "", "QBT_TR(Min Announce)QBT_TR[CONTEXT=TrackerListWidget]", 150, true); @@ -2275,6 +2276,9 @@ window.qBittorrent.DynamicTable ??= (() => { this.columns["seeds"].compareRows = sortMixed; this.columns["leeches"].compareRows = sortMixed; this.columns["downloaded"].compareRows = sortMixed; + this.columns["fails"].compareRows = sortMixed; + this.columns["nextAnnounce"].compareRows = sortNumbers; + this.columns["minAnnounce"].compareRows = sortNumbers; this.columns["status"].updateTd = function(td, row) { let statusClass = "trackerUnknown"; diff --git a/src/webui/www/private/scripts/prop-trackers.js b/src/webui/www/private/scripts/prop-trackers.js index 6b0d4c84441b..90510cb3a5d8 100644 --- a/src/webui/www/private/scripts/prop-trackers.js +++ b/src/webui/www/private/scripts/prop-trackers.js @@ -113,6 +113,7 @@ window.qBittorrent.PropTrackers ??= (() => { seeds: (tracker.num_seeds >= 0) ? tracker.num_seeds : notApplicable, leeches: (tracker.num_leeches >= 0) ? tracker.num_leeches : notApplicable, downloaded: (tracker.num_downloaded >= 0) ? tracker.num_downloaded : notApplicable, + fails: (tracker.num_fails >= 0) ? tracker.num_fails : notApplicable, message: tracker.msg, nextAnnounce: tracker.next_announce, minAnnounce: tracker.min_announce, @@ -134,6 +135,7 @@ window.qBittorrent.PropTrackers ??= (() => { seeds: (endpoint.num_seeds >= 0) ? endpoint.num_seeds : notApplicable, leeches: (endpoint.num_leeches >= 0) ? endpoint.num_leeches : notApplicable, downloaded: (endpoint.num_downloaded >= 0) ? endpoint.num_downloaded : notApplicable, + fails: (endpoint.num_fails >= 0) ? endpoint.num_fails : notApplicable, message: endpoint.msg, nextAnnounce: endpoint.next_announce, minAnnounce: endpoint.min_announce,