From 84a40c1665ddcb465ede910c3f9f21d0c7819c4d Mon Sep 17 00:00:00 2001 From: Thomas Piccirello Date: Sat, 2 May 2020 04:23:23 -0700 Subject: [PATCH 1/2] Include trackers in /sync/maindata API endpoint --- src/webui/api/serialize/serialize_torrent.cpp | 2 ++ src/webui/api/serialize/serialize_torrent.h | 1 + src/webui/api/synccontroller.cpp | 30 +++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/src/webui/api/serialize/serialize_torrent.cpp b/src/webui/api/serialize/serialize_torrent.cpp index 23e53c6ee..a4979008b 100644 --- a/src/webui/api/serialize/serialize_torrent.cpp +++ b/src/webui/api/serialize/serialize_torrent.cpp @@ -32,6 +32,7 @@ #include "base/bittorrent/infohash.h" #include "base/bittorrent/torrenthandle.h" +#include "base/bittorrent/trackerentry.h" #include "base/utils/fs.h" namespace @@ -110,6 +111,7 @@ QVariantMap serialize(const BitTorrent::TorrentHandle &torrent) {KEY_TORRENT_ADDED_ON, torrent.addedTime().toSecsSinceEpoch()}, {KEY_TORRENT_COMPLETION_ON, torrent.completedTime().toSecsSinceEpoch()}, {KEY_TORRENT_TRACKER, torrent.currentTracker()}, + {KEY_TORRENT_TRACKERS_COUNT, torrent.trackers().size()}, {KEY_TORRENT_DL_LIMIT, torrent.downloadLimit()}, {KEY_TORRENT_UP_LIMIT, torrent.uploadLimit()}, {KEY_TORRENT_AMOUNT_DOWNLOADED, torrent.totalDownload()}, diff --git a/src/webui/api/serialize/serialize_torrent.h b/src/webui/api/serialize/serialize_torrent.h index b02c6574c..af0a01bae 100644 --- a/src/webui/api/serialize/serialize_torrent.h +++ b/src/webui/api/serialize/serialize_torrent.h @@ -61,6 +61,7 @@ const char KEY_TORRENT_SAVE_PATH[] = "save_path"; const char KEY_TORRENT_ADDED_ON[] = "added_on"; const char KEY_TORRENT_COMPLETION_ON[] = "completion_on"; const char KEY_TORRENT_TRACKER[] = "tracker"; +const char KEY_TORRENT_TRACKERS_COUNT[] = "trackers_count"; const char KEY_TORRENT_DL_LIMIT[] = "dl_limit"; const char KEY_TORRENT_UP_LIMIT[] = "up_limit"; const char KEY_TORRENT_AMOUNT_DOWNLOADED[] = "downloaded"; diff --git a/src/webui/api/synccontroller.cpp b/src/webui/api/synccontroller.cpp index 8c5fe4621..5d1db5269 100644 --- a/src/webui/api/synccontroller.cpp +++ b/src/webui/api/synccontroller.cpp @@ -39,6 +39,7 @@ #include "base/bittorrent/peerinfo.h" #include "base/bittorrent/session.h" #include "base/bittorrent/torrenthandle.h" +#include "base/bittorrent/trackerentry.h" #include "base/global.h" #include "base/net/geoipmanager.h" #include "base/preferences.h" @@ -247,6 +248,22 @@ namespace syncData[i.key()] = map; } break; + case QVariant::StringList: + if (!prevData.contains(i.key())) { + // new list item found - append it to syncData + syncData[i.key()] = i.value(); + } + else { + QVariantList list; + QVariantList removedList; + processList(prevData[i.key()].toList(), i.value().toList(), list, removedList); + // existing list item found - remove it from prevData + prevData.remove(i.key()); + if (!list.isEmpty() || !removedList.isEmpty()) + // changed list item found - append entire list to syncData + syncData[i.key()] = i.value(); + } + break; default: Q_ASSERT(0); } @@ -354,6 +371,8 @@ SyncController::~SyncController() // - "torrents_removed": a list of hashes of removed torrents // - "categories": map of categories info // - "categories_removed": list of removed categories +// - "trackers": dictionary contains information about trackers +// - "trackers_removed": a list of removed trackers // - "server_state": map contains information about the state of the server // The keys of the 'torrents' dictionary are hashes of torrents. // Each value of the 'torrents' dictionary contains map. The map can contain following keys: @@ -414,6 +433,7 @@ void SyncController::maindataAction() QVariantMap lastAcceptedResponse = sessionManager()->session()->getData(QLatin1String("syncMainDataLastAcceptedResponse")).toMap(); QVariantHash torrents; + QHash trackers; for (const BitTorrent::TorrentHandle *torrent : asConst(session->torrents())) { const BitTorrent::InfoHash torrentHash = torrent->hash(); @@ -439,6 +459,10 @@ void SyncController::maindataAction() } } + for (const BitTorrent::TrackerEntry &tracker : asConst(torrent->trackers())) { + trackers[tracker.url()] << torrentHash; + } + torrents[torrentHash] = map; } data["torrents"] = torrents; @@ -459,6 +483,12 @@ void SyncController::maindataAction() tags << tag; data["tags"] = tags; + QVariantHash trackersHash; + for (auto i = trackers.constBegin(); i != trackers.constEnd(); ++i) { + trackersHash[i.key()] = i.value(); + } + data["trackers"] = trackersHash; + QVariantMap serverState = getTransferInfo(); serverState[KEY_TRANSFER_FREESPACEONDISK] = getFreeDiskSpace(); serverState[KEY_SYNC_MAINDATA_QUEUEING] = session->isQueueingSystemEnabled(); From ad4d8d28ec89120bc804e4d45a1727062b16fd8f Mon Sep 17 00:00:00 2001 From: Thomas Piccirello Date: Sun, 3 May 2020 03:00:58 -0700 Subject: [PATCH 2/2] Add Trackers section to Web UI sidebar Closes #7601. --- src/webui/api/synccontroller.cpp | 9 +- src/webui/www/private/index.html | 5 + src/webui/www/private/scripts/client.js | 100 +++++++++++++++- src/webui/www/private/scripts/dynamicTable.js | 27 ++++- src/webui/www/private/scripts/mocha-init.js | 108 ++++++++++++++++-- src/webui/www/private/views/filters.html | 36 +++++- 6 files changed, 262 insertions(+), 23 deletions(-) diff --git a/src/webui/api/synccontroller.cpp b/src/webui/api/synccontroller.cpp index 5d1db5269..4965f365b 100644 --- a/src/webui/api/synccontroller.cpp +++ b/src/webui/api/synccontroller.cpp @@ -243,9 +243,10 @@ namespace processMap(prevData[i.key()].toMap(), i.value().toMap(), map); // existing list item found - remove it from prevData prevData.remove(i.key()); - if (!map.isEmpty()) + if (!map.isEmpty()) { // changed list item found - append its changes to syncData syncData[i.key()] = map; + } } break; case QVariant::StringList: @@ -259,9 +260,10 @@ namespace processList(prevData[i.key()].toList(), i.value().toList(), list, removedList); // existing list item found - remove it from prevData prevData.remove(i.key()); - if (!list.isEmpty() || !removedList.isEmpty()) + if (!list.isEmpty() || !removedList.isEmpty()) { // changed list item found - append entire list to syncData syncData[i.key()] = i.value(); + } } break; default: @@ -459,9 +461,8 @@ void SyncController::maindataAction() } } - for (const BitTorrent::TrackerEntry &tracker : asConst(torrent->trackers())) { + for (const BitTorrent::TrackerEntry &tracker : asConst(torrent->trackers())) trackers[tracker.url()] << torrentHash; - } torrents[torrentHash] = map; } diff --git a/src/webui/www/private/index.html b/src/webui/www/private/index.html index e131d8495..7142480e0 100644 --- a/src/webui/www/private/index.html +++ b/src/webui/www/private/index.html @@ -186,6 +186,11 @@
  • QBT_TR(Pause torrents)QBT_TR[CONTEXT=TagFilterWidget] QBT_TR(Pause torrents)QBT_TR[CONTEXT=TagFilterWidget]
  • QBT_TR(Delete torrents)QBT_TR[CONTEXT=TagFilterWidget] QBT_TR(Delete torrents)QBT_TR[CONTEXT=TagFilterWidget]
  • +
    • QBT_TR(Add a new tracker...)QBT_TR[CONTEXT=TrackerListWidget] QBT_TR(Add a new tracker...)QBT_TR[CONTEXT=TrackerListWidget]
    • QBT_TR(Edit tracker URL...)QBT_TR[CONTEXT=TrackerListWidget] QBT_TR(Edit tracker URL...)QBT_TR[CONTEXT=TrackerListWidget]
    • diff --git a/src/webui/www/private/scripts/client.js b/src/webui/www/private/scripts/client.js index 4acf82e43..1237440da 100644 --- a/src/webui/www/private/scripts/client.js +++ b/src/webui/www/private/scripts/client.js @@ -39,6 +39,7 @@ let syncRequestInProgress = false; let clipboardEvent; +/* Categories filter */ const CATEGORIES_ALL = 1; const CATEGORIES_UNCATEGORIZED = 2; @@ -47,6 +48,7 @@ let category_list = {}; let selected_category = CATEGORIES_ALL; let setCategoryFilter = function() {}; +/* Tags filter */ const TAGS_ALL = 1; const TAGS_UNTAGGED = 2; @@ -55,6 +57,16 @@ let tagList = {}; let selectedTag = TAGS_ALL; let setTagFilter = function() {}; +/* Trackers filter */ +const TRACKERS_ALL = 1; +const TRACKERS_TRACKERLESS = 2; + +const trackerList = new Map(); + +let selectedTracker = TRACKERS_ALL; +let setTrackerFilter = function() {}; + +/* All filters */ let selected_filter = LocalPreferences.get('selected_filter', 'all'); let setFilter = function() {}; let toggleFilterDisplay = function() {}; @@ -69,6 +81,11 @@ const loadSelectedTag = function() { }; loadSelectedTag(); +const loadSelectedTracker = function() { + selectedTracker = LocalPreferences.get('selected_tracker', TRACKERS_ALL); +}; +loadSelectedTracker(); + function genHash(string) { let hash = 0; for (let i = 0; i < string.length; ++i) { @@ -174,6 +191,14 @@ window.addEvent('load', function() { updateMainData(); }; + setTrackerFilter = function(hash) { + selectedTracker = hash.toString(); + LocalPreferences.set('selected_tracker', selectedTracker); + highlightSelectedTracker(); + if (torrentsTable.tableBody !== undefined) + updateMainData(); + }; + setFilter = function(f) { // Visually Select the right filter $("all_filter").removeClass("selectedFilter"); @@ -335,7 +360,7 @@ window.addEvent('load', function() { }; const updateFilter = function(filter, filterTitle) { - $(filter + '_filter').firstChild.childNodes[1].nodeValue = filterTitle.replace('%1', torrentsTable.getFilteredTorrentsNumber(filter, CATEGORIES_ALL, TAGS_ALL)); + $(filter + '_filter').firstChild.childNodes[1].nodeValue = filterTitle.replace('%1', torrentsTable.getFilteredTorrentsNumber(filter, CATEGORIES_ALL, TAGS_ALL, TRACKERS_ALL)); }; const updateFiltersList = function() { @@ -462,6 +487,51 @@ window.addEvent('load', function() { children[i].className = (children[i].id === selectedTag) ? "selectedFilter" : ""; }; + const updateTrackerList = function() { + const trackerFilterList = $('trackerFilterList'); + if (trackerFilterList === null) + return; + + while (trackerFilterList.firstChild !== null) + trackerFilterList.removeChild(trackerFilterList.firstChild); + + const createLink = function(hash, text, count) { + const html = '' + + '' + + window.qBittorrent.Misc.escapeHtml(text.replace("%1", count)) + ''; + const el = new Element('li', { + id: hash, + html: html + }); + window.qBittorrent.Filters.trackersFilterContextMenu.addTarget(el); + return el; + }; + + const torrentsCount = torrentsTable.getRowIds().length; + trackerFilterList.appendChild(createLink(TRACKERS_ALL, 'QBT_TR(All (%1))QBT_TR[CONTEXT=TrackerFiltersList]', torrentsCount)); + let trackerlessTorrentsCount = 0; + for (const key in torrentsTable.rows) { + if (torrentsTable.rows.hasOwnProperty(key) && (torrentsTable.rows[key]['full_data'].trackers_count === 0)) + trackerlessTorrentsCount += 1; + } + trackerFilterList.appendChild(createLink(TRACKERS_TRACKERLESS, 'QBT_TR(Trackerless (%1))QBT_TR[CONTEXT=TrackerFiltersList]', trackerlessTorrentsCount)); + + for (const [hash, tracker] of trackerList) + trackerFilterList.appendChild(createLink(hash, tracker.url + ' (%1)', tracker.torrents.length)); + + highlightSelectedTracker(); + }; + + const highlightSelectedTracker = function() { + const trackerFilterList = $('trackerFilterList'); + if (!trackerFilterList) + return; + + const children = trackerFilterList.childNodes; + for (const child of children) + child.className = (child.id === selectedTracker) ? "selectedFilter" : ""; + }; + let syncMainDataTimer; const syncMainData = function() { const url = new URI('api/v2/sync/maindata'); @@ -484,6 +554,7 @@ window.addEvent('load', function() { let torrentsTableSelectedRows; let update_categories = false; let updateTags = false; + let updateTrackers = false; const full_update = (response['full_update'] === true); if (full_update) { torrentsTableSelectedRows = torrentsTable.selectedRowsIds(); @@ -538,6 +609,25 @@ window.addEvent('load', function() { } updateTags = true; } + if (response['trackers']) { + for (const tracker in response['trackers']) { + const torrents = response['trackers'][tracker]; + const hash = genHash(tracker); + trackerList.set(hash, { + url: tracker, + torrents: torrents + }); + } + updateTrackers = true; + } + if (response['trackers_removed']) { + for (let i = 0; i < response['trackers_removed'].length; ++i) { + const tracker = response['trackers_removed'][i]; + const hash = genHash(tracker); + trackerList.delete(hash); + } + updateTrackers = true; + } if (response['torrents']) { let updateTorrentList = false; for (const key in response['torrents']) { @@ -582,6 +672,8 @@ window.addEvent('load', function() { updateTagList(); window.qBittorrent.TransferList.contextMenu.updateTagsSubMenu(tagList); } + if (updateTrackers) + updateTrackerList(); if (full_update) // re-select previously selected rows @@ -656,10 +748,10 @@ window.addEvent('load', function() { break; default: { $('connectionStatus').src = 'images/skin/disconnected.svg'; - $('connectionStatus').alt = 'QBT_TR(Connection status: Disconnected)QBT_TR[CONTEXT=MainWindow]'; + $('connectionStatus').alt = 'QBT_TR(Connection status: Disconnected)QBT_TR[CONTEXT=MainWindow]'; } break; - } + } if (queueing_enabled != serverState.queueing) { queueing_enabled = serverState.queueing; @@ -695,7 +787,7 @@ window.addEvent('load', function() { if (enabled) { $('alternativeSpeedLimits').src = 'images/slow.svg'; $('alternativeSpeedLimits').alt = 'QBT_TR(Alternative speed limits: On)QBT_TR[CONTEXT=MainWindow]'; - } + } else { $('alternativeSpeedLimits').src = 'images/slow_off.svg'; $('alternativeSpeedLimits').alt = 'QBT_TR(Alternative speed limits: Off)QBT_TR[CONTEXT=MainWindow]'; diff --git a/src/webui/www/private/scripts/dynamicTable.js b/src/webui/www/private/scripts/dynamicTable.js index 3760678e3..9412796d9 100644 --- a/src/webui/www/private/scripts/dynamicTable.js +++ b/src/webui/www/private/scripts/dynamicTable.js @@ -1198,7 +1198,7 @@ window.qBittorrent.DynamicTable = (function() { }; }, - applyFilter: function(row, filterName, categoryHash, tagHash, filterTerms) { + applyFilter: function(row, filterName, categoryHash, tagHash, trackerHash, filterTerms) { const state = row['full_data'].state; const name = row['full_data'].name.toLowerCase(); let inactive = false; @@ -1291,6 +1291,21 @@ window.qBittorrent.DynamicTable = (function() { } } + const trackerHashInt = Number.parseInt(trackerHash, 10); + switch (trackerHashInt) { + case TRACKERS_ALL: + break; // do nothing + case TRACKERS_TRACKERLESS: + if (row['full_data'].trackers_count !== 0) + return false; + break; + default: + const tracker = trackerList.get(trackerHashInt) + if (tracker && !tracker.torrents.includes(row['full_data'].rowId)) + return false + break; + } + if ((filterTerms !== undefined) && (filterTerms !== null) && (filterTerms.length > 0) && !window.qBittorrent.Misc.containsAllTerms(name, filterTerms)) return false; @@ -1298,21 +1313,21 @@ window.qBittorrent.DynamicTable = (function() { return true; }, - getFilteredTorrentsNumber: function(filterName, categoryHash, tagHash) { + getFilteredTorrentsNumber: function(filterName, categoryHash, tagHash, trackerHash) { let cnt = 0; const rows = this.rows.getValues(); for (let i = 0; i < rows.length; ++i) - if (this.applyFilter(rows[i], filterName, categoryHash, tagHash, null)) ++cnt; + if (this.applyFilter(rows[i], filterName, categoryHash, tagHash, trackerHash, null)) ++cnt; return cnt; }, - getFilteredTorrentsHashes: function(filterName, categoryHash, tagHash) { + getFilteredTorrentsHashes: function(filterName, categoryHash, tagHash, trackerHash) { const rowsHashes = []; const rows = this.rows.getValues(); for (let i = 0; i < rows.length; ++i) - if (this.applyFilter(rows[i], filterName, categoryHash, tagHash, null)) + if (this.applyFilter(rows[i], filterName, categoryHash, tagHash, trackerHash, null)) rowsHashes.push(rows[i]['rowId']); return rowsHashes; @@ -1326,7 +1341,7 @@ window.qBittorrent.DynamicTable = (function() { const filterTerms = (filterText.length > 0) ? filterText.split(" ") : null; for (let i = 0; i < rows.length; ++i) { - if (this.applyFilter(rows[i], selected_filter, selected_category, selectedTag, filterTerms)) { + if (this.applyFilter(rows[i], selected_filter, selected_category, selectedTag, selectedTracker, filterTerms)) { filteredRows.push(rows[i]); filteredRows[rows[i].rowId] = rows[i]; } diff --git a/src/webui/www/private/scripts/mocha-init.js b/src/webui/www/private/scripts/mocha-init.js index a49186bdc..d59c119c5 100644 --- a/src/webui/www/private/scripts/mocha-init.js +++ b/src/webui/www/private/scripts/mocha-init.js @@ -80,6 +80,9 @@ let deleteUnusedTagsFN = function() {}; let startTorrentsByTagFN = function() {}; let pauseTorrentsByTagFN = function() {}; let deleteTorrentsByTagFN = function() {}; +let resumeTorrentsByTrackerFN = function() {}; +let pauseTorrentsByTrackerFN = function() {}; +let deleteTorrentsByTrackerFN = function() {}; let copyNameFN = function() {}; let copyMagnetLinkFN = function() {}; let copyHashFN = function() {}; @@ -609,7 +612,7 @@ const initializeWindows = function() { deleteUnusedCategoriesFN = function() { const categories = []; for (const hash in category_list) { - if (torrentsTable.getFilteredTorrentsNumber('all', hash, TAGS_ALL) === 0) + if (torrentsTable.getFilteredTorrentsNumber('all', hash, TAGS_ALL, TRACKERS_ALL) === 0) categories.push(category_list[hash].name); } new Request({ @@ -623,7 +626,7 @@ const initializeWindows = function() { }; startTorrentsByCategoryFN = function(categoryHash) { - const hashes = torrentsTable.getFilteredTorrentsHashes('all', categoryHash, TAGS_ALL); + const hashes = torrentsTable.getFilteredTorrentsHashes('all', categoryHash, TAGS_ALL, TRACKERS_ALL); if (hashes.length) { new Request({ url: 'api/v2/torrents/resume', @@ -637,7 +640,7 @@ const initializeWindows = function() { }; pauseTorrentsByCategoryFN = function(categoryHash) { - const hashes = torrentsTable.getFilteredTorrentsHashes('all', categoryHash, TAGS_ALL); + const hashes = torrentsTable.getFilteredTorrentsHashes('all', categoryHash, TAGS_ALL, TRACKERS_ALL); if (hashes.length) { new Request({ url: 'api/v2/torrents/pause', @@ -651,7 +654,7 @@ const initializeWindows = function() { }; deleteTorrentsByCategoryFN = function(categoryHash) { - const hashes = torrentsTable.getFilteredTorrentsHashes('all', categoryHash, TAGS_ALL); + const hashes = torrentsTable.getFilteredTorrentsHashes('all', categoryHash, TAGS_ALL, TRACKERS_ALL); if (hashes.length) { new MochaUI.Window({ id: 'confirmDeletionPage', @@ -750,7 +753,7 @@ const initializeWindows = function() { deleteUnusedTagsFN = function() { const tags = []; for (const hash in tagList) { - if (torrentsTable.getFilteredTorrentsNumber('all', CATEGORIES_ALL, hash) === 0) + if (torrentsTable.getFilteredTorrentsNumber('all', CATEGORIES_ALL, hash, TRACKERS_ALL) === 0) tags.push(tagList[hash].name); } new Request({ @@ -764,7 +767,7 @@ const initializeWindows = function() { }; startTorrentsByTagFN = function(tagHash) { - const hashes = torrentsTable.getFilteredTorrentsHashes('all', CATEGORIES_ALL, tagHash); + const hashes = torrentsTable.getFilteredTorrentsHashes('all', CATEGORIES_ALL, tagHash, TRACKERS_ALL); if (hashes.length) { new Request({ url: 'api/v2/torrents/resume', @@ -778,7 +781,7 @@ const initializeWindows = function() { }; pauseTorrentsByTagFN = function(tagHash) { - const hashes = torrentsTable.getFilteredTorrentsHashes('all', CATEGORIES_ALL, tagHash); + const hashes = torrentsTable.getFilteredTorrentsHashes('all', CATEGORIES_ALL, tagHash, TRACKERS_ALL); if (hashes.length) { new Request({ url: 'api/v2/torrents/pause', @@ -792,7 +795,7 @@ const initializeWindows = function() { }; deleteTorrentsByTagFN = function(tagHash) { - const hashes = torrentsTable.getFilteredTorrentsHashes('all', CATEGORIES_ALL, tagHash); + const hashes = torrentsTable.getFilteredTorrentsHashes('all', CATEGORIES_ALL, tagHash, TRACKERS_ALL); if (hashes.length) { new MochaUI.Window({ id: 'confirmDeletionPage', @@ -810,6 +813,95 @@ const initializeWindows = function() { } }; + resumeTorrentsByTrackerFN = function(trackerHash) { + const trackerHashInt = Number.parseInt(trackerHash, 10); + let hashes = []; + switch (trackerHashInt) { + case TRACKERS_ALL: + hashes = torrentsTable.getFilteredTorrentsHashes('all', CATEGORIES_ALL, TAGS_ALL, TRACKERS_ALL); + break; + case TRACKERS_TRACKERLESS: + hashes = torrentsTable.getFilteredTorrentsHashes('all', CATEGORIES_ALL, TAGS_ALL, TRACKERS_TRACKERLESS); + break; + default: + hashes = trackerList.get(trackerHashInt).torrents + break; + } + + if (hashes.length > 0) { + new Request({ + url: 'api/v2/torrents/resume', + method: 'post', + data: { + hashes: hashes.join("|") + } + }).send(); + updateMainData(); + } + }; + + pauseTorrentsByTrackerFN = function(trackerHash) { + const trackerHashInt = Number.parseInt(trackerHash, 10); + let hashes = []; + switch (trackerHashInt) { + case TRACKERS_ALL: + hashes = torrentsTable.getFilteredTorrentsHashes('all', CATEGORIES_ALL, TAGS_ALL, TRACKERS_ALL); + break; + case TRACKERS_TRACKERLESS: + hashes = torrentsTable.getFilteredTorrentsHashes('all', CATEGORIES_ALL, TAGS_ALL, TRACKERS_TRACKERLESS); + break; + default: + hashes = trackerList.get(trackerHashInt).torrents + break; + } + + if (hashes.length) { + new Request({ + url: 'api/v2/torrents/pause', + method: 'post', + data: { + hashes: hashes.join("|") + } + }).send(); + updateMainData(); + } + }; + + deleteTorrentsByTrackerFN = function(trackerHash) { + const trackerHashInt = Number.parseInt(trackerHash, 10); + let hashes = []; + switch (trackerHashInt) { + case TRACKERS_ALL: + hashes = torrentsTable.getFilteredTorrentsHashes('all', CATEGORIES_ALL, TAGS_ALL, TRACKERS_ALL); + break; + case TRACKERS_TRACKERLESS: + hashes = torrentsTable.getFilteredTorrentsHashes('all', CATEGORIES_ALL, TAGS_ALL, TRACKERS_TRACKERLESS); + break; + default: + hashes = trackerList.get(trackerHashInt).torrents + break; + } + + if (hashes.length) { + new MochaUI.Window({ + id: 'confirmDeletionPage', + title: "QBT_TR(Deletion confirmation)QBT_TR[CONTEXT=confirmDeletionDlg]", + loadMethod: 'iframe', + contentURL: 'confirmdeletion.html?hashes=' + hashes.join("|"), + scrollbars: false, + resizable: false, + maximizable: false, + padding: 10, + width: 424, + height: 140, + onCloseComplete: function() { + updateMainData(); + setTrackerFilter(TRACKERS_ALL); + } + }); + } + }; + copyNameFN = function() { const selectedRows = torrentsTable.selectedRowsIds(); const names = []; diff --git a/src/webui/www/private/views/filters.html b/src/webui/www/private/views/filters.html index 86cc858ed..0dd068b82 100644 --- a/src/webui/www/private/views/filters.html +++ b/src/webui/www/private/views/filters.html @@ -31,6 +31,13 @@
      +
      + + QBT_TR(Trackers)QBT_TR[CONTEXT=TransferListFiltersWidget] + +
        +
      +