mirror of
https://github.com/qbittorrent/qBittorrent
synced 2025-07-08 06:00:59 -07:00
WebUI: Improve accuracy of trackers list
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.
This commit is contained in:
parent
eb9e98a4b3
commit
4967f977c5
3 changed files with 65 additions and 37 deletions
|
@ -116,6 +116,7 @@ let setTagFilter = function() {};
|
||||||
const TRACKERS_ALL = 1;
|
const TRACKERS_ALL = 1;
|
||||||
const TRACKERS_TRACKERLESS = 2;
|
const TRACKERS_TRACKERLESS = 2;
|
||||||
|
|
||||||
|
/** @type Map<number, {host: string, trackerTorrentMap: Map<string, string[]>}> **/
|
||||||
const trackerList = new Map();
|
const trackerList = new Map();
|
||||||
|
|
||||||
let selectedTracker = LocalPreferences.get('selected_tracker', TRACKERS_ALL);
|
let selectedTracker = LocalPreferences.get('selected_tracker', TRACKERS_ALL);
|
||||||
|
@ -623,11 +624,20 @@ window.addEventListener("DOMContentLoaded", function() {
|
||||||
|
|
||||||
// Sort trackers by hostname
|
// Sort trackers by hostname
|
||||||
const sortedList = [];
|
const sortedList = [];
|
||||||
trackerList.forEach((tracker, hash) => sortedList.push({
|
trackerList.forEach(({ host, trackerTorrentMap }, hash) => {
|
||||||
trackerHost: getHost(tracker.url),
|
const uniqueTorrents = new Set();
|
||||||
trackerHash: hash,
|
for (const torrents of trackerTorrentMap.values()) {
|
||||||
trackerCount: tracker.torrents.length
|
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));
|
sortedList.sort((left, right) => window.qBittorrent.Misc.naturalSortCollator.compare(left.trackerHost, right.trackerHost));
|
||||||
for (const { trackerHost, trackerHash, trackerCount } of sortedList)
|
for (const { trackerHost, trackerHash, trackerCount } of sortedList)
|
||||||
trackerFilterList.appendChild(createLink(trackerHash, (trackerHost + ' (%1)'), trackerCount));
|
trackerFilterList.appendChild(createLink(trackerHash, (trackerHost + ' (%1)'), trackerCount));
|
||||||
|
@ -760,32 +770,17 @@ window.addEventListener("DOMContentLoaded", function() {
|
||||||
updateTags = true;
|
updateTags = true;
|
||||||
}
|
}
|
||||||
if (response['trackers']) {
|
if (response['trackers']) {
|
||||||
for (const tracker in response['trackers']) {
|
for (const [tracker, torrents] of Object.entries(response['trackers'])) {
|
||||||
const torrents = response['trackers'][tracker];
|
const host = getHost(tracker);
|
||||||
const hash = window.qBittorrent.Client.genHash(getHost(tracker));
|
const hash = window.qBittorrent.Client.genHash(host);
|
||||||
|
|
||||||
// the reason why we need the merge here is because the WebUI api returned trackers may have different url for the same tracker host.
|
let trackerListItem = trackerList.get(hash);
|
||||||
// for example, some private trackers use diff urls for each torrent from the same tracker host.
|
if (trackerListItem === undefined) {
|
||||||
// then we got the response of `trackers` from qBittorrent api will like:
|
trackerListItem = { host: host, trackerTorrentMap: new Map() };
|
||||||
// {
|
trackerList.set(hash, trackerListItem);
|
||||||
// "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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trackerList.set(hash, {
|
trackerListItem.trackerTorrentMap.set(tracker, [...torrents]);
|
||||||
url: tracker,
|
|
||||||
torrents: merged_torrents
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
updateTrackers = true;
|
updateTrackers = true;
|
||||||
}
|
}
|
||||||
|
@ -793,7 +788,10 @@ window.addEventListener("DOMContentLoaded", function() {
|
||||||
for (let i = 0; i < response['trackers_removed'].length; ++i) {
|
for (let i = 0; i < response['trackers_removed'].length; ++i) {
|
||||||
const tracker = response['trackers_removed'][i];
|
const tracker = response['trackers_removed'][i];
|
||||||
const hash = window.qBittorrent.Client.genHash(getHost(tracker));
|
const hash = window.qBittorrent.Client.genHash(getHost(tracker));
|
||||||
trackerList.delete(hash);
|
const trackerListEntry = trackerList.get(hash);
|
||||||
|
if (trackerListEntry) {
|
||||||
|
trackerListEntry.trackerTorrentMap.delete(tracker);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
updateTrackers = true;
|
updateTrackers = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1435,8 +1435,17 @@ window.qBittorrent.DynamicTable = (function() {
|
||||||
break;
|
break;
|
||||||
default: {
|
default: {
|
||||||
const tracker = trackerList.get(trackerHashInt);
|
const tracker = trackerList.get(trackerHashInt);
|
||||||
if (tracker && !tracker.torrents.includes(row['full_data'].rowId))
|
if (tracker) {
|
||||||
return false;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -874,9 +874,16 @@ const initializeWindows = function() {
|
||||||
case TRACKERS_TRACKERLESS:
|
case TRACKERS_TRACKERLESS:
|
||||||
hashes = torrentsTable.getFilteredTorrentsHashes('all', CATEGORIES_ALL, TAGS_ALL, TRACKERS_TRACKERLESS);
|
hashes = torrentsTable.getFilteredTorrentsHashes('all', CATEGORIES_ALL, TAGS_ALL, TRACKERS_TRACKERLESS);
|
||||||
break;
|
break;
|
||||||
default:
|
default: {
|
||||||
hashes = trackerList.get(trackerHashInt).torrents;
|
const uniqueTorrents = new Set();
|
||||||
|
for (const torrents of trackerList.get(trackerHashInt).trackerTorrentMap.values()) {
|
||||||
|
for (const torrent of torrents) {
|
||||||
|
uniqueTorrents.add(torrent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hashes = [...uniqueTorrents];
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hashes.length > 0) {
|
if (hashes.length > 0) {
|
||||||
|
@ -901,9 +908,16 @@ const initializeWindows = function() {
|
||||||
case TRACKERS_TRACKERLESS:
|
case TRACKERS_TRACKERLESS:
|
||||||
hashes = torrentsTable.getFilteredTorrentsHashes('all', CATEGORIES_ALL, TAGS_ALL, TRACKERS_TRACKERLESS);
|
hashes = torrentsTable.getFilteredTorrentsHashes('all', CATEGORIES_ALL, TAGS_ALL, TRACKERS_TRACKERLESS);
|
||||||
break;
|
break;
|
||||||
default:
|
default: {
|
||||||
hashes = trackerList.get(trackerHashInt).torrents;
|
const uniqueTorrents = new Set();
|
||||||
|
for (const torrents of trackerList.get(trackerHashInt).trackerTorrentMap.values()) {
|
||||||
|
for (const torrent of torrents) {
|
||||||
|
uniqueTorrents.add(torrent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hashes = [...uniqueTorrents];
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hashes.length) {
|
if (hashes.length) {
|
||||||
|
@ -928,9 +942,16 @@ const initializeWindows = function() {
|
||||||
case TRACKERS_TRACKERLESS:
|
case TRACKERS_TRACKERLESS:
|
||||||
hashes = torrentsTable.getFilteredTorrentsHashes('all', CATEGORIES_ALL, TAGS_ALL, TRACKERS_TRACKERLESS);
|
hashes = torrentsTable.getFilteredTorrentsHashes('all', CATEGORIES_ALL, TAGS_ALL, TRACKERS_TRACKERLESS);
|
||||||
break;
|
break;
|
||||||
default:
|
default: {
|
||||||
hashes = trackerList.get(trackerHashInt).torrents;
|
const uniqueTorrents = new Set();
|
||||||
|
for (const torrents of trackerList.get(trackerHashInt).trackerTorrentMap.values()) {
|
||||||
|
for (const torrent of torrents) {
|
||||||
|
uniqueTorrents.add(torrent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hashes = [...uniqueTorrents];
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hashes.length) {
|
if (hashes.length) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue