From 3d8d163159f854baeda494701020356466e81651 Mon Sep 17 00:00:00 2001 From: "Stiliyan Tonev (Bark)" Date: Thu, 9 Jan 2025 11:40:32 +0200 Subject: [PATCH 1/6] Feat: Add column that displays the percent of selected data from a torrent. --- src/gui/transferlistmodel.cpp | 6 +++++- src/gui/transferlistmodel.h | 1 + src/gui/transferlistsortmodel.cpp | 2 +- src/gui/transferlistwidget.cpp | 1 + src/webui/api/serialize/serialize_torrent.cpp | 9 ++++++++- src/webui/api/serialize/serialize_torrent.h | 1 + src/webui/webapplication.h | 2 +- src/webui/www/private/scripts/dynamicTable.js | 1 + 8 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/gui/transferlistmodel.cpp b/src/gui/transferlistmodel.cpp index 7c827c1a6a84..f9f14fee2465 100644 --- a/src/gui/transferlistmodel.cpp +++ b/src/gui/transferlistmodel.cpp @@ -194,6 +194,7 @@ QVariant TransferListModel::headerData(const int section, const Qt::Orientation case TR_INFOHASH_V2: return tr("Info Hash v2", "i.e: torrent info hash v2"); case TR_REANNOUNCE: return tr("Reannounce In", "Indicates the time until next trackers reannounce"); case TR_PRIVATE: return tr("Private", "Flags private torrents"); + case TR_PERCENT_SELECTED: return tr("% Selected", "Percentage of torrent selected"); default: return {}; } } @@ -443,8 +444,9 @@ QString TransferListModel::displayValue(const BitTorrent::Torrent *torrent, cons return reannounceString(torrent->nextAnnounce()); case TR_PRIVATE: return privateString(torrent->isPrivate(), torrent->hasMetadata()); + case TR_PERCENT_SELECTED: + return QString::number((torrent->wantedSize() * 100) / torrent->totalSize()) + u'%'; } - return {}; } @@ -526,6 +528,8 @@ QVariant TransferListModel::internalValue(const BitTorrent::Torrent *torrent, co return torrent->nextAnnounce(); case TR_PRIVATE: return (torrent->hasMetadata() ? torrent->isPrivate() : QVariant()); + case TR_PERCENT_SELECTED: + return (torrent->wantedSize() * 100) / torrent->totalSize(); } return {}; diff --git a/src/gui/transferlistmodel.h b/src/gui/transferlistmodel.h index 306beee0b9a3..04f0b155df31 100644 --- a/src/gui/transferlistmodel.h +++ b/src/gui/transferlistmodel.h @@ -87,6 +87,7 @@ class TransferListModel final : public QAbstractListModel TR_INFOHASH_V2, TR_REANNOUNCE, TR_PRIVATE, + TR_PERCENT_SELECTED, NB_COLUMNS }; diff --git a/src/gui/transferlistsortmodel.cpp b/src/gui/transferlistsortmodel.cpp index 782aead74276..a097c4e1bfe8 100644 --- a/src/gui/transferlistsortmodel.cpp +++ b/src/gui/transferlistsortmodel.cpp @@ -208,6 +208,7 @@ int TransferListSortModel::compare(const QModelIndex &left, const QModelIndex &r case TransferListModel::TR_RATIO: case TransferListModel::TR_RATIO_LIMIT: case TransferListModel::TR_POPULARITY: + case TransferListModel::TR_PERCENT_SELECTED: return customCompare(leftValue.toReal(), rightValue.toReal()); case TransferListModel::TR_STATUS: @@ -241,7 +242,6 @@ int TransferListSortModel::compare(const QModelIndex &left, const QModelIndex &r const auto totalR = right.data(TransferListModel::AdditionalUnderlyingDataRole).toInt(); return threeWayCompare(totalL, totalR); } - default: Q_ASSERT_X(false, Q_FUNC_INFO, "Missing comparison case"); break; diff --git a/src/gui/transferlistwidget.cpp b/src/gui/transferlistwidget.cpp index 9435b05beada..8a173887d55e 100644 --- a/src/gui/transferlistwidget.cpp +++ b/src/gui/transferlistwidget.cpp @@ -189,6 +189,7 @@ TransferListWidget::TransferListWidget(IGUIApplication *app, QWidget *parent) setColumnHidden(TransferListModel::TR_TOTAL_SIZE, true); setColumnHidden(TransferListModel::TR_REANNOUNCE, true); setColumnHidden(TransferListModel::TR_PRIVATE, true); + setColumnHidden(TransferListModel::TR_PERCENT_SELECTED, true); } //Ensure that at least one column is visible at all times diff --git a/src/webui/api/serialize/serialize_torrent.cpp b/src/webui/api/serialize/serialize_torrent.cpp index bc75a76985dd..f8a961cc3926 100644 --- a/src/webui/api/serialize/serialize_torrent.cpp +++ b/src/webui/api/serialize/serialize_torrent.cpp @@ -107,6 +107,12 @@ QVariantMap serialize(const BitTorrent::Torrent &torrent) : (QDateTime::currentSecsSinceEpoch() - timeSinceActivity); }; + const auto getSelectedPercentage = [&torrent]() -> QString + { + auto percent = ((torrent.wantedSize() * 100)/torrent.totalSize()); + return QString::number(percent) + u'%'; + }; + return { {KEY_TORRENT_ID, torrent.id().toString()}, {KEY_TORRENT_INFOHASHV1, torrent.infoHash().v1().toString()}, @@ -166,6 +172,7 @@ QVariantMap serialize(const BitTorrent::Torrent &torrent) {KEY_TORRENT_COMMENT, torrent.comment()}, {KEY_TORRENT_PRIVATE, (torrent.hasMetadata() ? torrent.isPrivate() : QVariant())}, {KEY_TORRENT_TOTAL_SIZE, torrent.totalSize()}, - {KEY_TORRENT_HAS_METADATA, torrent.hasMetadata()} + {KEY_TORRENT_HAS_METADATA, torrent.hasMetadata()}, + {KEY_TORRENT_PERCENT_SELECTED, getSelectedPercentage()} }; } diff --git a/src/webui/api/serialize/serialize_torrent.h b/src/webui/api/serialize/serialize_torrent.h index 883efa6dd5cd..2004edf3e701 100644 --- a/src/webui/api/serialize/serialize_torrent.h +++ b/src/webui/api/serialize/serialize_torrent.h @@ -96,5 +96,6 @@ inline const QString KEY_TORRENT_REANNOUNCE = u"reannounce"_s; inline const QString KEY_TORRENT_COMMENT = u"comment"_s; inline const QString KEY_TORRENT_PRIVATE = u"private"_s; inline const QString KEY_TORRENT_HAS_METADATA = u"has_metadata"_s; +inline const QString KEY_TORRENT_PERCENT_SELECTED = u"percent_selected"_s; QVariantMap serialize(const BitTorrent::Torrent &torrent); diff --git a/src/webui/webapplication.h b/src/webui/webapplication.h index 514ed3f14d96..46ca4152936b 100644 --- a/src/webui/webapplication.h +++ b/src/webui/webapplication.h @@ -54,7 +54,7 @@ #include "base/utils/version.h" #include "api/isessionmanager.h" -inline const Utils::Version<3, 2> API_VERSION {2, 11, 3}; +inline const Utils::Version<3, 2> API_VERSION {2, 11, 4}; class QTimer; diff --git a/src/webui/www/private/scripts/dynamicTable.js b/src/webui/www/private/scripts/dynamicTable.js index 88edb34c1cd3..ea3fc7e5109e 100644 --- a/src/webui/www/private/scripts/dynamicTable.js +++ b/src/webui/www/private/scripts/dynamicTable.js @@ -1025,6 +1025,7 @@ window.qBittorrent.DynamicTable ??= (() => { this.newColumn("infohash_v2", "", "QBT_TR(Info Hash v2)QBT_TR[CONTEXT=TransferListModel]", 100, false); this.newColumn("reannounce", "", "QBT_TR(Reannounce In)QBT_TR[CONTEXT=TransferListModel]", 100, false); this.newColumn("private", "", "QBT_TR(Private)QBT_TR[CONTEXT=TransferListModel]", 100, false); + this.newColumn("percent_selected", "", "QBT_TR(Percent Selected)QBT_TR[CONTEXT=TransferListModel]", 100, false); this.columns["state_icon"].onclick = ""; this.columns["state_icon"].dataProperties[0] = "state"; From 25238ee36fc6bf81f8dcf2672fdaf275c2474c4c Mon Sep 17 00:00:00 2001 From: "Stiliyan Tonev (Bark)" Date: Thu, 9 Jan 2025 11:42:15 +0200 Subject: [PATCH 2/6] return previosly deleted empty lines --- src/gui/transferlistmodel.cpp | 1 + src/gui/transferlistsortmodel.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/gui/transferlistmodel.cpp b/src/gui/transferlistmodel.cpp index f9f14fee2465..e3a6f1b01992 100644 --- a/src/gui/transferlistmodel.cpp +++ b/src/gui/transferlistmodel.cpp @@ -447,6 +447,7 @@ QString TransferListModel::displayValue(const BitTorrent::Torrent *torrent, cons case TR_PERCENT_SELECTED: return QString::number((torrent->wantedSize() * 100) / torrent->totalSize()) + u'%'; } + return {}; } diff --git a/src/gui/transferlistsortmodel.cpp b/src/gui/transferlistsortmodel.cpp index a097c4e1bfe8..ff0ad35a18ac 100644 --- a/src/gui/transferlistsortmodel.cpp +++ b/src/gui/transferlistsortmodel.cpp @@ -242,6 +242,7 @@ int TransferListSortModel::compare(const QModelIndex &left, const QModelIndex &r const auto totalR = right.data(TransferListModel::AdditionalUnderlyingDataRole).toInt(); return threeWayCompare(totalL, totalR); } + default: Q_ASSERT_X(false, Q_FUNC_INFO, "Missing comparison case"); break; From 765963c7dd5e2a81178e17696e09cb87f48b802d Mon Sep 17 00:00:00 2001 From: "Stiliyan Tonev (Bark)" Date: Fri, 10 Jan 2025 10:43:21 +0200 Subject: [PATCH 3/6] Add metadata check and address PR comments. --- src/gui/transferlistmodel.cpp | 6 +++++- src/webui/api/serialize/serialize_torrent.cpp | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/gui/transferlistmodel.cpp b/src/gui/transferlistmodel.cpp index e3a6f1b01992..2eac6dff1b02 100644 --- a/src/gui/transferlistmodel.cpp +++ b/src/gui/transferlistmodel.cpp @@ -194,7 +194,7 @@ QVariant TransferListModel::headerData(const int section, const Qt::Orientation case TR_INFOHASH_V2: return tr("Info Hash v2", "i.e: torrent info hash v2"); case TR_REANNOUNCE: return tr("Reannounce In", "Indicates the time until next trackers reannounce"); case TR_PRIVATE: return tr("Private", "Flags private torrents"); - case TR_PERCENT_SELECTED: return tr("% Selected", "Percentage of torrent selected"); + case TR_PERCENT_SELECTED: return tr("% Selected Data", "Percentage of selected data to download."); default: return {}; } } @@ -445,6 +445,8 @@ QString TransferListModel::displayValue(const BitTorrent::Torrent *torrent, cons case TR_PRIVATE: return privateString(torrent->isPrivate(), torrent->hasMetadata()); case TR_PERCENT_SELECTED: + if (!torrent->hasMetadata()) + return tr("N/A"); return QString::number((torrent->wantedSize() * 100) / torrent->totalSize()) + u'%'; } @@ -530,6 +532,8 @@ QVariant TransferListModel::internalValue(const BitTorrent::Torrent *torrent, co case TR_PRIVATE: return (torrent->hasMetadata() ? torrent->isPrivate() : QVariant()); case TR_PERCENT_SELECTED: + if (!torrent->hasMetadata()) + return 0; return (torrent->wantedSize() * 100) / torrent->totalSize(); } diff --git a/src/webui/api/serialize/serialize_torrent.cpp b/src/webui/api/serialize/serialize_torrent.cpp index f8a961cc3926..4ef4054b0977 100644 --- a/src/webui/api/serialize/serialize_torrent.cpp +++ b/src/webui/api/serialize/serialize_torrent.cpp @@ -109,7 +109,9 @@ QVariantMap serialize(const BitTorrent::Torrent &torrent) const auto getSelectedPercentage = [&torrent]() -> QString { - auto percent = ((torrent.wantedSize() * 100)/torrent.totalSize()); + if (!torrent.hasMetadata()) + return u"N/A"_s; + auto percent = ((torrent.wantedSize() * 100) / torrent.totalSize()); return QString::number(percent) + u'%'; }; From 0234ec86fc181a9f0df895255ddc8285acf3276d Mon Sep 17 00:00:00 2001 From: "Stiliyan Tonev (Bark)" Date: Fri, 10 Jan 2025 14:34:02 +0200 Subject: [PATCH 4/6] Pass number to WebUI instead of text and use float when comparing. --- src/gui/transferlistsortmodel.cpp | 4 +++- src/webui/api/serialize/serialize_torrent.cpp | 10 +--------- src/webui/www/private/scripts/dynamicTable.js | 12 ++++++++++++ 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/gui/transferlistsortmodel.cpp b/src/gui/transferlistsortmodel.cpp index ff0ad35a18ac..45199553d488 100644 --- a/src/gui/transferlistsortmodel.cpp +++ b/src/gui/transferlistsortmodel.cpp @@ -208,7 +208,6 @@ int TransferListSortModel::compare(const QModelIndex &left, const QModelIndex &r case TransferListModel::TR_RATIO: case TransferListModel::TR_RATIO_LIMIT: case TransferListModel::TR_POPULARITY: - case TransferListModel::TR_PERCENT_SELECTED: return customCompare(leftValue.toReal(), rightValue.toReal()); case TransferListModel::TR_STATUS: @@ -243,6 +242,9 @@ int TransferListSortModel::compare(const QModelIndex &left, const QModelIndex &r return threeWayCompare(totalL, totalR); } + case TransferListModel::TR_PERCENT_SELECTED: + return customCompare(leftValue.toFloat(), rightValue.toFloat()); + default: Q_ASSERT_X(false, Q_FUNC_INFO, "Missing comparison case"); break; diff --git a/src/webui/api/serialize/serialize_torrent.cpp b/src/webui/api/serialize/serialize_torrent.cpp index 4ef4054b0977..67a6a1bf4fb3 100644 --- a/src/webui/api/serialize/serialize_torrent.cpp +++ b/src/webui/api/serialize/serialize_torrent.cpp @@ -107,14 +107,6 @@ QVariantMap serialize(const BitTorrent::Torrent &torrent) : (QDateTime::currentSecsSinceEpoch() - timeSinceActivity); }; - const auto getSelectedPercentage = [&torrent]() -> QString - { - if (!torrent.hasMetadata()) - return u"N/A"_s; - auto percent = ((torrent.wantedSize() * 100) / torrent.totalSize()); - return QString::number(percent) + u'%'; - }; - return { {KEY_TORRENT_ID, torrent.id().toString()}, {KEY_TORRENT_INFOHASHV1, torrent.infoHash().v1().toString()}, @@ -175,6 +167,6 @@ QVariantMap serialize(const BitTorrent::Torrent &torrent) {KEY_TORRENT_PRIVATE, (torrent.hasMetadata() ? torrent.isPrivate() : QVariant())}, {KEY_TORRENT_TOTAL_SIZE, torrent.totalSize()}, {KEY_TORRENT_HAS_METADATA, torrent.hasMetadata()}, - {KEY_TORRENT_PERCENT_SELECTED, getSelectedPercentage()} + {KEY_TORRENT_PERCENT_SELECTED, torrent.hasMetadata() ? (torrent.wantedSize() * 100) / torrent.totalSize() : -1}, }; } diff --git a/src/webui/www/private/scripts/dynamicTable.js b/src/webui/www/private/scripts/dynamicTable.js index ea3fc7e5109e..f8593a438f82 100644 --- a/src/webui/www/private/scripts/dynamicTable.js +++ b/src/webui/www/private/scripts/dynamicTable.js @@ -1450,6 +1450,18 @@ window.qBittorrent.DynamicTable ??= (() => { td.textContent = string; td.title = string; }; + + // percent_selected + this.columns["percent_selected"].updateTd = function(td, row) { + if (this.getRowValue(row) === -1) { + td.textContent = "QBT_TR(N/A)QBT_TR[CONTEXT=TrackerListWidget]"; + td.title = "QBT_TR(N/A)QBT_TR[CONTEXT=TrackerListWidget]"; + return; + } + const value = window.qBittorrent.Misc.toFixedPointString(this.getRowValue(row), 2) + "%"; + td.textContent = value; + td.title = value; + }; }, applyFilter: (row, filterName, categoryHash, tagHash, trackerHash, filterTerms) => { From 3618b0f74cc1f012ce48d0afcc20eaebe1f4ded2 Mon Sep 17 00:00:00 2001 From: "Stiliyan Tonev (Bark)" Date: Mon, 13 Jan 2025 16:24:58 +0200 Subject: [PATCH 5/6] Shorten column name --- src/gui/transferlistmodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/transferlistmodel.cpp b/src/gui/transferlistmodel.cpp index 2eac6dff1b02..da8f83245568 100644 --- a/src/gui/transferlistmodel.cpp +++ b/src/gui/transferlistmodel.cpp @@ -194,7 +194,7 @@ QVariant TransferListModel::headerData(const int section, const Qt::Orientation case TR_INFOHASH_V2: return tr("Info Hash v2", "i.e: torrent info hash v2"); case TR_REANNOUNCE: return tr("Reannounce In", "Indicates the time until next trackers reannounce"); case TR_PRIVATE: return tr("Private", "Flags private torrents"); - case TR_PERCENT_SELECTED: return tr("% Selected Data", "Percentage of selected data to download."); + case TR_PERCENT_SELECTED: return tr("%", "Percentage of selected data to download."); default: return {}; } } From c203d18db67f0fcd22b1c5be07398070107f5126 Mon Sep 17 00:00:00 2001 From: "Stiliyan Tonev (Bark)" Date: Sat, 18 Jan 2025 09:49:46 +0200 Subject: [PATCH 6/6] Add tooltip and change webui column name. --- src/gui/transferlistmodel.cpp | 1 + src/webui/www/private/scripts/dynamicTable.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gui/transferlistmodel.cpp b/src/gui/transferlistmodel.cpp index da8f83245568..3d00b442ebd3 100644 --- a/src/gui/transferlistmodel.cpp +++ b/src/gui/transferlistmodel.cpp @@ -203,6 +203,7 @@ QVariant TransferListModel::headerData(const int section, const Qt::Orientation switch (section) { case TR_POPULARITY: return tr("Ratio / Time Active (in months), indicates how popular the torrent is"); + case TR_PERCENT_SELECTED: return tr("Wanted / Total size, indicates percentage of selected data to download."); default: return {}; } } diff --git a/src/webui/www/private/scripts/dynamicTable.js b/src/webui/www/private/scripts/dynamicTable.js index f8593a438f82..28babbcb2e7d 100644 --- a/src/webui/www/private/scripts/dynamicTable.js +++ b/src/webui/www/private/scripts/dynamicTable.js @@ -1025,7 +1025,7 @@ window.qBittorrent.DynamicTable ??= (() => { this.newColumn("infohash_v2", "", "QBT_TR(Info Hash v2)QBT_TR[CONTEXT=TransferListModel]", 100, false); this.newColumn("reannounce", "", "QBT_TR(Reannounce In)QBT_TR[CONTEXT=TransferListModel]", 100, false); this.newColumn("private", "", "QBT_TR(Private)QBT_TR[CONTEXT=TransferListModel]", 100, false); - this.newColumn("percent_selected", "", "QBT_TR(Percent Selected)QBT_TR[CONTEXT=TransferListModel]", 100, false); + this.newColumn("percent_selected", "", "QBT_TR(%)QBT_TR[CONTEXT=TransferListModel]", 100, false); this.columns["state_icon"].onclick = ""; this.columns["state_icon"].dataProperties[0] = "state";