Skip to content

Commit

Permalink
WebUI: Improve accuracy of trackers list
Browse files Browse the repository at this point in the history
This PR fixes various accounting issues with the trackers list. Removing a torrent would not update the trackers list, nor would removing a tracker from a torrent. And removing a tracker with a shared host but unique url (e.g. example.com/1 and example.com/2) would erroneously remove the tracker's host from the list.

Closes #20053.
Closes #20054.
PR  #20601.
  • Loading branch information
Piccirello authored Mar 29, 2024
1 parent eb9e98a commit 4967f97
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 38 deletions.
58 changes: 28 additions & 30 deletions src/webui/www/private/scripts/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ let setTagFilter = function() {};
const TRACKERS_ALL = 1;
const TRACKERS_TRACKERLESS = 2;

/** @type Map<number, {host: string, trackerTorrentMap: Map<string, string[]>}> **/
const trackerList = new Map();

let selectedTracker = LocalPreferences.get('selected_tracker', TRACKERS_ALL);
Expand Down Expand Up @@ -623,11 +624,20 @@ window.addEventListener("DOMContentLoaded", function() {

// Sort trackers by hostname
const sortedList = [];
trackerList.forEach((tracker, hash) => sortedList.push({
trackerHost: getHost(tracker.url),
trackerHash: hash,
trackerCount: tracker.torrents.length
}));
trackerList.forEach(({ host, trackerTorrentMap }, hash) => {
const uniqueTorrents = new Set();
for (const torrents of trackerTorrentMap.values()) {
for (const torrent of torrents) {
uniqueTorrents.add(torrent);
}
}

sortedList.push({
trackerHost: host,
trackerHash: hash,
trackerCount: uniqueTorrents.size,
});
});
sortedList.sort((left, right) => window.qBittorrent.Misc.naturalSortCollator.compare(left.trackerHost, right.trackerHost));
for (const { trackerHost, trackerHash, trackerCount } of sortedList)
trackerFilterList.appendChild(createLink(trackerHash, (trackerHost + ' (%1)'), trackerCount));
Expand Down Expand Up @@ -760,40 +770,28 @@ window.addEventListener("DOMContentLoaded", function() {
updateTags = true;
}
if (response['trackers']) {
for (const tracker in response['trackers']) {
const torrents = response['trackers'][tracker];
const hash = window.qBittorrent.Client.genHash(getHost(tracker));

// the reason why we need the merge here is because the WebUI api returned trackers may have different url for the same tracker host.
// for example, some private trackers use diff urls for each torrent from the same tracker host.
// then we got the response of `trackers` from qBittorrent api will like:
// {
// "trackers": {
// "https://example.com/announce?passkey=identify_info1": ["hash1"],
// "https://example.com/announce?passkey=identify_info2": ["hash2"],
// "https://example.com/announce?passkey=identify_info3": ["hash3"]
// }
// }
// after getHost(), those torrents all belongs to `example.com`
let merged_torrents = torrents;
if (trackerList.has(hash)) {
merged_torrents = trackerList.get(hash).torrents.concat(torrents);
// deduplicate is needed when the webui opens in multi tabs
merged_torrents = merged_torrents.filter((item, pos) => merged_torrents.indexOf(item) === pos);
for (const [tracker, torrents] of Object.entries(response['trackers'])) {
const host = getHost(tracker);
const hash = window.qBittorrent.Client.genHash(host);

let trackerListItem = trackerList.get(hash);
if (trackerListItem === undefined) {
trackerListItem = { host: host, trackerTorrentMap: new Map() };
trackerList.set(hash, trackerListItem);
}

trackerList.set(hash, {
url: tracker,
torrents: merged_torrents
});
trackerListItem.trackerTorrentMap.set(tracker, [...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 = window.qBittorrent.Client.genHash(getHost(tracker));
trackerList.delete(hash);
const trackerListEntry = trackerList.get(hash);
if (trackerListEntry) {
trackerListEntry.trackerTorrentMap.delete(tracker);
}
}
updateTrackers = true;
}
Expand Down
13 changes: 11 additions & 2 deletions src/webui/www/private/scripts/dynamicTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -1435,8 +1435,17 @@ window.qBittorrent.DynamicTable = (function() {
break;
default: {
const tracker = trackerList.get(trackerHashInt);
if (tracker && !tracker.torrents.includes(row['full_data'].rowId))
return false;
if (tracker) {
let found = false;
for (const torrents of tracker.trackerTorrentMap.values()) {
if (torrents.includes(row['full_data'].rowId)) {
found = true;
break;
}
}
if (!found)
return false;
}
break;
}
}
Expand Down
33 changes: 27 additions & 6 deletions src/webui/www/private/scripts/mocha-init.js
Original file line number Diff line number Diff line change
Expand Up @@ -874,9 +874,16 @@ const initializeWindows = function() {
case TRACKERS_TRACKERLESS:
hashes = torrentsTable.getFilteredTorrentsHashes('all', CATEGORIES_ALL, TAGS_ALL, TRACKERS_TRACKERLESS);
break;
default:
hashes = trackerList.get(trackerHashInt).torrents;
default: {
const uniqueTorrents = new Set();
for (const torrents of trackerList.get(trackerHashInt).trackerTorrentMap.values()) {
for (const torrent of torrents) {
uniqueTorrents.add(torrent);
}
}
hashes = [...uniqueTorrents];
break;
}
}

if (hashes.length > 0) {
Expand All @@ -901,9 +908,16 @@ const initializeWindows = function() {
case TRACKERS_TRACKERLESS:
hashes = torrentsTable.getFilteredTorrentsHashes('all', CATEGORIES_ALL, TAGS_ALL, TRACKERS_TRACKERLESS);
break;
default:
hashes = trackerList.get(trackerHashInt).torrents;
default: {
const uniqueTorrents = new Set();
for (const torrents of trackerList.get(trackerHashInt).trackerTorrentMap.values()) {
for (const torrent of torrents) {
uniqueTorrents.add(torrent);
}
}
hashes = [...uniqueTorrents];
break;
}
}

if (hashes.length) {
Expand All @@ -928,9 +942,16 @@ const initializeWindows = function() {
case TRACKERS_TRACKERLESS:
hashes = torrentsTable.getFilteredTorrentsHashes('all', CATEGORIES_ALL, TAGS_ALL, TRACKERS_TRACKERLESS);
break;
default:
hashes = trackerList.get(trackerHashInt).torrents;
default: {
const uniqueTorrents = new Set();
for (const torrents of trackerList.get(trackerHashInt).trackerTorrentMap.values()) {
for (const torrent of torrents) {
uniqueTorrents.add(torrent);
}
}
hashes = [...uniqueTorrents];
break;
}
}

if (hashes.length) {
Expand Down

0 comments on commit 4967f97

Please sign in to comment.