From 4d490c84e7b08c87b10b9ba082409832f50084b6 Mon Sep 17 00:00:00 2001 From: ManiMatter <124743318+ManiMatter@users.noreply.github.com> Date: Sun, 7 Jul 2024 18:09:57 +0200 Subject: [PATCH] Add ability to display torrent "privateness" in UI PR #20951. --------- Co-authored-by: Chocobo1 Co-authored-by: Vladimir Golovnev Co-authored-by: thalieht --- src/gui/properties/propertieswidget.cpp | 8 + src/gui/properties/propertieswidget.ui | 162 +++++++++++------- src/gui/transferlistmodel.cpp | 14 ++ src/gui/transferlistmodel.h | 1 + src/gui/transferlistsortmodel.cpp | 37 ++-- src/gui/transferlistwidget.cpp | 1 + src/webui/api/serialize/serialize_torrent.cpp | 5 +- src/webui/api/serialize/serialize_torrent.h | 1 + src/webui/api/torrentscontroller.cpp | 9 +- src/webui/webapplication.h | 2 +- src/webui/www/private/scripts/dynamicTable.js | 14 ++ src/webui/www/private/scripts/prop-general.js | 10 ++ src/webui/www/private/views/properties.html | 4 + 13 files changed, 183 insertions(+), 85 deletions(-) diff --git a/src/gui/properties/propertieswidget.cpp b/src/gui/properties/propertieswidget.cpp index b35623b96..26d737732 100644 --- a/src/gui/properties/propertieswidget.cpp +++ b/src/gui/properties/propertieswidget.cpp @@ -210,6 +210,7 @@ void PropertiesWidget::clear() m_ui->labelSavePathVal->clear(); m_ui->labelCreatedOnVal->clear(); m_ui->labelTotalPiecesVal->clear(); + m_ui->labelPrivateVal->clear(); m_ui->labelInfohash1Val->clear(); m_ui->labelInfohash2Val->clear(); m_ui->labelCommentVal->clear(); @@ -335,7 +336,14 @@ void PropertiesWidget::loadTorrentInfos(BitTorrent::Torrent *const torrent) m_ui->labelCommentVal->setText(Utils::Misc::parseHtmlLinks(m_torrent->comment().toHtmlEscaped())); m_ui->labelCreatedByVal->setText(m_torrent->creator()); + + m_ui->labelPrivateVal->setText(m_torrent->isPrivate() ? tr("Yes") : tr("No")); } + else + { + m_ui->labelPrivateVal->setText(tr("N/A")); + } + // Load dynamic data loadDynamicData(); } diff --git a/src/gui/properties/propertieswidget.ui b/src/gui/properties/propertieswidget.ui index 17ffa6b4f..71b3864f9 100644 --- a/src/gui/properties/propertieswidget.ui +++ b/src/gui/properties/propertieswidget.ui @@ -823,6 +823,38 @@ + + + + 0 + 0 + + + + Private: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + Qt::PlainText + + + Qt::TextSelectableByMouse + + + + @@ -838,71 +870,7 @@ - - - - - 0 - 0 - - - - Info Hash v2: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - 0 - 0 - - - - Qt::PlainText - - - Qt::TextSelectableByMouse - - - - - - - - 0 - 0 - - - - Save Path: - - - Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing - - - - - - - - 0 - 0 - - - - Comment: - - - Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing - - - - @@ -918,7 +886,55 @@ + + + + + 0 + 0 + + + + Info Hash v2: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 0 + 0 + + + + Qt::PlainText + + + Qt::TextSelectableByMouse + + + + + + + + 0 + 0 + + + + Save Path: + + + Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing + + + + @@ -937,7 +953,23 @@ - + + + + + 0 + 0 + + + + Comment: + + + Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing + + + + diff --git a/src/gui/transferlistmodel.cpp b/src/gui/transferlistmodel.cpp index cb35a51c0..63f1b3ebb 100644 --- a/src/gui/transferlistmodel.cpp +++ b/src/gui/transferlistmodel.cpp @@ -193,6 +193,7 @@ QVariant TransferListModel::headerData(const int section, const Qt::Orientation case TR_INFOHASH_V1: return tr("Info Hash v1", "i.e: torrent info hash v1"); 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"); default: return {}; } } @@ -357,6 +358,15 @@ QString TransferListModel::displayValue(const BitTorrent::Torrent *torrent, cons return Utils::Misc::userFriendlyDuration(time); }; + const auto privateString = [hideValues](const bool isPrivate, const bool hasMetadata) -> QString + { + if (hideValues && !isPrivate) + return {}; + if (hasMetadata) + return isPrivate ? tr("Yes") : tr("No"); + return tr("N/A"); + }; + switch (column) { case TR_NAME: @@ -431,6 +441,8 @@ QString TransferListModel::displayValue(const BitTorrent::Torrent *torrent, cons return hashString(torrent->infoHash().v2()); case TR_REANNOUNCE: return reannounceString(torrent->nextAnnounce()); + case TR_PRIVATE: + return privateString(torrent->isPrivate(), torrent->hasMetadata()); } return {}; @@ -512,6 +524,8 @@ QVariant TransferListModel::internalValue(const BitTorrent::Torrent *torrent, co return QVariant::fromValue(torrent->infoHash().v2()); case TR_REANNOUNCE: return torrent->nextAnnounce(); + case TR_PRIVATE: + return (torrent->hasMetadata() ? torrent->isPrivate() : QVariant()); } return {}; diff --git a/src/gui/transferlistmodel.h b/src/gui/transferlistmodel.h index 061db4683..5942f77da 100644 --- a/src/gui/transferlistmodel.h +++ b/src/gui/transferlistmodel.h @@ -86,6 +86,7 @@ public: TR_INFOHASH_V1, TR_INFOHASH_V2, TR_REANNOUNCE, + TR_PRIVATE, NB_COLUMNS }; diff --git a/src/gui/transferlistsortmodel.cpp b/src/gui/transferlistsortmodel.cpp index 2f9ce6ff0..90b73044f 100644 --- a/src/gui/transferlistsortmodel.cpp +++ b/src/gui/transferlistsortmodel.cpp @@ -28,6 +28,7 @@ #include "transferlistsortmodel.h" +#include #include #include @@ -46,16 +47,6 @@ namespace return (left < right) ? -1 : 1; } - int customCompare(const QDateTime &left, const QDateTime &right) - { - const bool isLeftValid = left.isValid(); - const bool isRightValid = right.isValid(); - - if (isLeftValid == isRightValid) - return threeWayCompare(left, right); - return isLeftValid ? -1 : 1; - } - int customCompare(const TagSet &left, const TagSet &right, const Utils::Compare::NaturalCompare &compare) { for (auto leftIter = left.cbegin(), rightIter = right.cbegin(); @@ -70,12 +61,27 @@ namespace } template - int customCompare(const T left, const T right) - { - static_assert(std::is_arithmetic_v); + concept Validateable = requires (T t) { {t.isValid()} -> std::same_as; }; - const bool isLeftValid = (left >= 0); - const bool isRightValid = (right >= 0); + template + bool isValid(const T &value) + { + return value.isValid(); + } + + // consider negative values as invalid + template + requires std::is_arithmetic_v + bool isValid(const T value) + { + return (value >= 0); + } + + template + int customCompare(const T &left, const T &right) + { + const bool isLeftValid = isValid(left); + const bool isRightValid = isValid(right); if (isLeftValid && isRightValid) return threeWayCompare(left, right); @@ -209,6 +215,7 @@ int TransferListSortModel::compare(const QModelIndex &left, const QModelIndex &r case TransferListModel::TR_DLLIMIT: case TransferListModel::TR_DLSPEED: + case TransferListModel::TR_PRIVATE: case TransferListModel::TR_QUEUE_POSITION: case TransferListModel::TR_UPLIMIT: case TransferListModel::TR_UPSPEED: diff --git a/src/gui/transferlistwidget.cpp b/src/gui/transferlistwidget.cpp index 893b10b51..7b857e1a3 100644 --- a/src/gui/transferlistwidget.cpp +++ b/src/gui/transferlistwidget.cpp @@ -184,6 +184,7 @@ TransferListWidget::TransferListWidget(QWidget *parent, MainWindow *mainWindow) setColumnHidden(TransferListModel::TR_LAST_ACTIVITY, true); setColumnHidden(TransferListModel::TR_TOTAL_SIZE, true); setColumnHidden(TransferListModel::TR_REANNOUNCE, true); + setColumnHidden(TransferListModel::TR_PRIVATE, 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 88ab5af52..d6076fda9 100644 --- a/src/webui/api/serialize/serialize_torrent.cpp +++ b/src/webui/api/serialize/serialize_torrent.cpp @@ -163,7 +163,8 @@ QVariantMap serialize(const BitTorrent::Torrent &torrent) {KEY_TORRENT_AVAILABILITY, torrent.distributedCopies()}, {KEY_TORRENT_REANNOUNCE, torrent.nextAnnounce()}, {KEY_TORRENT_COMMENT, torrent.comment()}, - {KEY_TORRENT_PRIVATE, torrent.isPrivate()}, - {KEY_TORRENT_TOTAL_SIZE, torrent.totalSize()} + {KEY_TORRENT_PRIVATE, (torrent.hasMetadata() ? torrent.isPrivate() : QVariant())}, + {KEY_TORRENT_TOTAL_SIZE, torrent.totalSize()}, + {KEY_TORRENT_HAS_METADATA, torrent.hasMetadata()} }; } diff --git a/src/webui/api/serialize/serialize_torrent.h b/src/webui/api/serialize/serialize_torrent.h index ae5088e36..c9812ac07 100644 --- a/src/webui/api/serialize/serialize_torrent.h +++ b/src/webui/api/serialize/serialize_torrent.h @@ -94,5 +94,6 @@ inline const QString KEY_TORRENT_AVAILABILITY = u"availability"_s; 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; QVariantMap serialize(const BitTorrent::Torrent &torrent); diff --git a/src/webui/api/torrentscontroller.cpp b/src/webui/api/torrentscontroller.cpp index 8a36fb81d..74272921b 100644 --- a/src/webui/api/torrentscontroller.cpp +++ b/src/webui/api/torrentscontroller.cpp @@ -116,6 +116,8 @@ const QString KEY_PROP_PRIVATE = u"private"_s; const QString KEY_PROP_SSL_CERTIFICATE = u"ssl_certificate"_s; const QString KEY_PROP_SSL_PRIVATEKEY = u"ssl_private_key"_s; const QString KEY_PROP_SSL_DHPARAMS = u"ssl_dh_params"_s; +const QString KEY_PROP_HAS_METADATA = u"has_metadata"_s; + // File keys const QString KEY_FILE_INDEX = u"index"_s; @@ -438,6 +440,8 @@ void TorrentsController::propertiesAction() const int uploadLimit = torrent->uploadLimit(); const qreal ratio = torrent->realRatio(); const qreal popularity = torrent->popularity(); + const bool hasMetadata = torrent->hasMetadata(); + const bool isPrivate = torrent->isPrivate(); const QJsonObject ret { @@ -474,14 +478,15 @@ void TorrentsController::propertiesAction() {KEY_PROP_PIECES_HAVE, torrent->piecesHave()}, {KEY_PROP_CREATED_BY, torrent->creator()}, {KEY_PROP_IS_PRIVATE, torrent->isPrivate()}, // used for maintaining backward compatibility - {KEY_PROP_PRIVATE, torrent->isPrivate()}, + {KEY_PROP_PRIVATE, (hasMetadata ? isPrivate : QJsonValue())}, {KEY_PROP_ADDITION_DATE, Utils::DateTime::toSecsSinceEpoch(torrent->addedTime())}, {KEY_PROP_LAST_SEEN, Utils::DateTime::toSecsSinceEpoch(torrent->lastSeenComplete())}, {KEY_PROP_COMPLETION_DATE, Utils::DateTime::toSecsSinceEpoch(torrent->completedTime())}, {KEY_PROP_CREATION_DATE, Utils::DateTime::toSecsSinceEpoch(torrent->creationDate())}, {KEY_PROP_SAVE_PATH, torrent->savePath().toString()}, {KEY_PROP_DOWNLOAD_PATH, torrent->downloadPath().toString()}, - {KEY_PROP_COMMENT, torrent->comment()} + {KEY_PROP_COMMENT, torrent->comment()}, + {KEY_PROP_HAS_METADATA, torrent->hasMetadata()} }; setResult(ret); diff --git a/src/webui/webapplication.h b/src/webui/webapplication.h index 193701314..16b1f2950 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, 1}; +inline const Utils::Version<3, 2> API_VERSION {2, 11, 2}; class QTimer; diff --git a/src/webui/www/private/scripts/dynamicTable.js b/src/webui/www/private/scripts/dynamicTable.js index 1fa668920..43f9dda12 100644 --- a/src/webui/www/private/scripts/dynamicTable.js +++ b/src/webui/www/private/scripts/dynamicTable.js @@ -948,6 +948,7 @@ window.qBittorrent.DynamicTable = (function() { this.newColumn("last_activity", "", "QBT_TR(Last Activity)QBT_TR[CONTEXT=TransferListModel]", 100, false); this.newColumn("availability", "", "QBT_TR(Availability)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.columns["state_icon"].onclick = ""; this.columns["state_icon"].dataProperties[0] = "state"; @@ -1331,6 +1332,19 @@ window.qBittorrent.DynamicTable = (function() { td.set("text", time); td.set("title", time); }; + + // private + this.columns["private"].updateTd = function(td, row) { + const hasMetadata = row["full_data"].has_metadata; + const isPrivate = this.getRowValue(row); + const string = hasMetadata + ? (isPrivate + ? "QBT_TR(Yes)QBT_TR[CONTEXT=PropertiesWidget]" + : "QBT_TR(No)QBT_TR[CONTEXT=PropertiesWidget]") + : "QBT_TR(N/A)QBT_TR[CONTEXT=PropertiesWidget]"; + td.set("text", string); + td.set("title", string); + }; }, applyFilter: function(row, filterName, categoryHash, tagHash, trackerHash, filterTerms) { diff --git a/src/webui/www/private/scripts/prop-general.js b/src/webui/www/private/scripts/prop-general.js index 96a48f3ba..402199501 100644 --- a/src/webui/www/private/scripts/prop-general.js +++ b/src/webui/www/private/scripts/prop-general.js @@ -70,6 +70,7 @@ window.qBittorrent.PropGeneral = (function() { $("torrent_hash_v2").set("html", ""); $("save_path").set("html", ""); $("comment").set("html", ""); + $("private").set("html", ""); piecesBar.clear(); }; @@ -210,6 +211,15 @@ window.qBittorrent.PropGeneral = (function() { $("save_path").set("html", data.save_path); $("comment").set("html", window.qBittorrent.Misc.parseHtmlLinks(window.qBittorrent.Misc.escapeHtml(data.comment))); + + if (data.has_metadata) { + $("private").set("text", (data.private + ? "QBT_TR(Yes)QBT_TR[CONTEXT=PropertiesWidget]" + : "QBT_TR(No)QBT_TR[CONTEXT=PropertiesWidget]")); + } + else { + $("private").set("text", "QBT_TR(N/A)QBT_TR[CONTEXT=PropertiesWidget]"); + } } else { clearData(); diff --git a/src/webui/www/private/views/properties.html b/src/webui/www/private/views/properties.html index 350f569ad..b42c8d19f 100644 --- a/src/webui/www/private/views/properties.html +++ b/src/webui/www/private/views/properties.html @@ -74,6 +74,10 @@ QBT_TR(Created On:)QBT_TR[CONTEXT=PropertiesWidget] + + QBT_TR(Private:)QBT_TR[CONTEXT=PropertiesWidget] + + QBT_TR(Info Hash v1:)QBT_TR[CONTEXT=PropertiesWidget]