From 9c0475ebfa606d5abf941c33722194febcea3537 Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Sun, 29 Dec 2024 15:44:28 +0800 Subject: [PATCH] WebUI: migrate to fetch API This is the final part of it. PR #22072. --- src/webui/www/private/rename_files.html | 10 +- src/webui/www/private/scripts/client.js | 6 +- src/webui/www/private/scripts/mocha-init.js | 429 +++++++++--------- src/webui/www/private/scripts/prop-files.js | 7 +- src/webui/www/private/scripts/prop-general.js | 12 +- .../www/private/scripts/prop-trackers.js | 54 ++- .../www/private/scripts/prop-webseeds.js | 53 ++- src/webui/www/private/scripts/rename-files.js | 24 +- src/webui/www/private/scripts/search.js | 142 +++--- src/webui/www/private/views/cookies.html | 62 +-- .../private/views/installsearchplugin.html | 20 +- src/webui/www/private/views/preferences.html | 72 +-- src/webui/www/private/views/rss.html | 101 +++-- .../www/private/views/rssDownloader.html | 150 +++--- .../www/private/views/searchplugins.html | 35 +- 15 files changed, 626 insertions(+), 551 deletions(-) diff --git a/src/webui/www/private/rename_files.html b/src/webui/www/private/rename_files.html index 6c39af5c4395..1815719727b6 100644 --- a/src/webui/www/private/rename_files.html +++ b/src/webui/www/private/rename_files.html @@ -267,8 +267,8 @@ } setupTable(selectedRows); }; - fileRenamer.onRenameError = (err, row) => { - if (err.xhr.status === 409) + fileRenamer.onRenameError = (response, row) => { + if (response.status === 409) $("rename_error").textContent = `QBT_TR(Rename failed: file or folder already exists)QBT_TR[CONTEXT=PropertiesWidget] \`${row.renamed}\``; }; $("renameOptions").addEventListener("change", (e) => { @@ -379,7 +379,11 @@ }; const setupTable = (selectedRows) => { - fetch(new URI("api/v2/torrents/files").setData("hash", data.hash), { + const url = new URL("api/v2/torrents/files", window.location); + url.search = new URLSearchParams({ + hash: data.hash + }); + fetch(url, { method: "GET", cache: "no-store" }) diff --git a/src/webui/www/private/scripts/client.js b/src/webui/www/private/scripts/client.js index 62c66d4a18a9..7d58ac4c8752 100644 --- a/src/webui/www/private/scripts/client.js +++ b/src/webui/www/private/scripts/client.js @@ -749,7 +749,11 @@ window.addEventListener("DOMContentLoaded", () => { let syncRequestInProgress = false; const syncMainData = () => { syncRequestInProgress = true; - fetch(new URI("api/v2/sync/maindata").setData("rid", syncMainDataLastResponseId), { + const url = new URL("api/v2/sync/maindata", window.location); + url.search = new URLSearchParams({ + rid: syncMainDataLastResponseId + }); + fetch(url, { method: "GET", cache: "no-store" }) diff --git a/src/webui/www/private/scripts/mocha-init.js b/src/webui/www/private/scripts/mocha-init.js index 91967f84b818..03368ecd464e 100644 --- a/src/webui/www/private/scripts/mocha-init.js +++ b/src/webui/www/private/scripts/mocha-init.js @@ -360,13 +360,12 @@ const initializeWindows = () => { toggleSequentialDownloadFN = () => { const hashes = torrentsTable.selectedRowsIds(); if (hashes.length) { - new Request({ - url: "api/v2/torrents/toggleSequentialDownload", - method: "post", - data: { + fetch("api/v2/torrents/toggleSequentialDownload", { + method: "POST", + body: new URLSearchParams({ hashes: hashes.join("|") - } - }).send(); + }) + }); updateMainData(); } }; @@ -374,13 +373,12 @@ const initializeWindows = () => { toggleFirstLastPiecePrioFN = () => { const hashes = torrentsTable.selectedRowsIds(); if (hashes.length) { - new Request({ - url: "api/v2/torrents/toggleFirstLastPiecePrio", - method: "post", - data: { + fetch("api/v2/torrents/toggleFirstLastPiecePrio", { + method: "POST", + body: new URLSearchParams({ hashes: hashes.join("|") - } - }).send(); + }) + }); updateMainData(); } }; @@ -388,14 +386,13 @@ const initializeWindows = () => { setSuperSeedingFN = (val) => { const hashes = torrentsTable.selectedRowsIds(); if (hashes.length) { - new Request({ - url: "api/v2/torrents/setSuperSeeding", - method: "post", - data: { - value: val, - hashes: hashes.join("|") - } - }).send(); + fetch("api/v2/torrents/setSuperSeeding", { + method: "POST", + body: new URLSearchParams({ + hashes: hashes.join("|"), + value: val + }) + }); updateMainData(); } }; @@ -403,14 +400,13 @@ const initializeWindows = () => { setForceStartFN = () => { const hashes = torrentsTable.selectedRowsIds(); if (hashes.length) { - new Request({ - url: "api/v2/torrents/setForceStart", - method: "post", - data: { - value: "true", - hashes: hashes.join("|") - } - }).send(); + fetch("api/v2/torrents/setForceStart", { + method: "POST", + body: new URLSearchParams({ + hashes: hashes.join("|"), + value: "true" + }) + }); updateMainData(); } }; @@ -494,22 +490,23 @@ const initializeWindows = () => { }); } else { - new Request({ - url: "api/v2/torrents/delete", - method: "post", - data: { - hashes: hashes.join("|"), - deleteFiles: forceDeleteFiles - }, - onSuccess: () => { + fetch("api/v2/torrents/delete", { + method: "POST", + body: new URLSearchParams({ + hashes: hashes.join("|"), + deleteFiles: forceDeleteFiles + }) + }) + .then((response) => { + if (!response.ok) { + alert("QBT_TR(Unable to delete torrents.)QBT_TR[CONTEXT=HttpServer]"); + return; + } + torrentsTable.deselectAll(); updateMainData(); updatePropertiesPanel(); - }, - onFailure: () => { - alert("QBT_TR(Unable to delete torrents.)QBT_TR[CONTEXT=HttpServer]"); - } - }).send(); + }); } } }; @@ -523,13 +520,12 @@ const initializeWindows = () => { stopFN = () => { const hashes = torrentsTable.selectedRowsIds(); if (hashes.length) { - new Request({ - url: "api/v2/torrents/stop", - method: "post", - data: { + fetch("api/v2/torrents/stop", { + method: "POST", + body: new URLSearchParams({ hashes: hashes.join("|") - } - }).send(); + }) + }); updateMainData(); } }; @@ -537,13 +533,12 @@ const initializeWindows = () => { startFN = () => { const hashes = torrentsTable.selectedRowsIds(); if (hashes.length) { - new Request({ - url: "api/v2/torrents/start", - method: "post", - data: { + fetch("api/v2/torrents/start", { + method: "POST", + body: new URLSearchParams({ hashes: hashes.join("|") - } - }).send(); + }) + }); updateMainData(); } }; @@ -565,20 +560,21 @@ const initializeWindows = () => { }); } else { - new Request({ - url: "api/v2/torrents/setAutoManagement", - method: "post", - data: { - hashes: hashes.join("|"), - enable: enableAutoTMM - }, - onSuccess: () => { + fetch("api/v2/torrents/setAutoManagement", { + method: "POST", + body: new URLSearchParams({ + hashes: hashes.join("|"), + enable: enableAutoTMM + }) + }) + .then((response) => { + if (!response.ok) { + alert("QBT_TR(Unable to set Auto Torrent Management for the selected torrents.)QBT_TR[CONTEXT=HttpServer]"); + return; + } + updateMainData(); - }, - onFailure: () => { - alert("QBT_TR(Unable to set Auto Torrent Management for the selected torrents.)QBT_TR[CONTEXT=HttpServer]"); - } - }).send(); + }); } } }; @@ -596,19 +592,20 @@ const initializeWindows = () => { }); } else { - new Request({ - url: "api/v2/torrents/recheck", - method: "post", - data: { - hashes: hashes.join("|"), - }, - onSuccess: () => { + fetch("api/v2/torrents/recheck", { + method: "POST", + body: new URLSearchParams({ + hashes: hashes.join("|"), + }) + }) + .then((response) => { + if (!response.ok) { + alert("QBT_TR(Unable to recheck torrents.)QBT_TR[CONTEXT=HttpServer]"); + return; + } + updateMainData(); - }, - onFailure: () => { - alert("QBT_TR(Unable to recheck torrents.)QBT_TR[CONTEXT=HttpServer]"); - } - }).send(); + }); } } }; @@ -616,13 +613,12 @@ const initializeWindows = () => { reannounceFN = () => { const hashes = torrentsTable.selectedRowsIds(); if (hashes.length) { - new Request({ - url: "api/v2/torrents/reannounce", - method: "post", - data: { - hashes: hashes.join("|"), - } - }).send(); + fetch("api/v2/torrents/reannounce", { + method: "POST", + body: new URLSearchParams({ + hashes: hashes.join("|") + }) + }); updateMainData(); } }; @@ -703,40 +699,42 @@ const initializeWindows = () => { startVisibleTorrentsFN = () => { const hashes = torrentsTable.getFilteredTorrentsHashes(selectedStatus, selectedCategory, selectedTag, selectedTracker); if (hashes.length > 0) { - new Request({ - url: "api/v2/torrents/start", - method: "post", - data: { - hashes: hashes.join("|") - }, - onSuccess: () => { + fetch("api/v2/torrents/start", { + method: "POST", + body: new URLSearchParams({ + hashes: hashes.join("|") + }) + }) + .then((response) => { + if (!response.ok) { + alert("QBT_TR(Unable to start torrents.)QBT_TR[CONTEXT=HttpServer]"); + return; + } + updateMainData(); updatePropertiesPanel(); - }, - onFailure: () => { - alert("QBT_TR(Unable to start torrents.)QBT_TR[CONTEXT=HttpServer]"); - } - }).send(); + }); } }; stopVisibleTorrentsFN = () => { const hashes = torrentsTable.getFilteredTorrentsHashes(selectedStatus, selectedCategory, selectedTag, selectedTracker); if (hashes.length > 0) { - new Request({ - url: "api/v2/torrents/stop", - method: "post", - data: { - hashes: hashes.join("|") - }, - onSuccess: () => { + fetch("api/v2/torrents/stop", { + method: "POST", + body: new URLSearchParams({ + hashes: hashes.join("|") + }) + }) + .then((response) => { + if (!response.ok) { + alert("QBT_TR(Unable to stop torrents.)QBT_TR[CONTEXT=HttpServer]"); + return; + } + updateMainData(); updatePropertiesPanel(); - }, - onFailure: () => { - alert("QBT_TR(Unable to stop torrents.)QBT_TR[CONTEXT=HttpServer]"); - } - }).send(); + }); } }; @@ -760,22 +758,23 @@ const initializeWindows = () => { }); } else { - new Request({ - url: "api/v2/torrents/delete", - method: "post", - data: { - hashes: hashes.join("|"), - deleteFiles: false, - }, - onSuccess: () => { + fetch("api/v2/torrents/delete", { + method: "POST", + body: new URLSearchParams({ + hashes: hashes.join("|"), + deleteFiles: false, + }) + }) + .then((response) => { + if (!response.ok) { + alert("QBT_TR(Unable to delete torrents.)QBT_TR[CONTEXT=HttpServer]"); + return; + } + torrentsTable.deselectAll(); updateMainData(); updatePropertiesPanel(); - }, - onFailure: () => { - alert("QBT_TR(Unable to delete torrents.)QBT_TR[CONTEXT=HttpServer]"); - } - }).send(); + }); } } }; @@ -809,17 +808,19 @@ const initializeWindows = () => { const categoryName = category_list.has(categoryHash) ? category_list.get(categoryHash).name : ""; - new Request({ - url: "api/v2/torrents/setCategory", - method: "post", - data: { - hashes: hashes.join("|"), - category: categoryName - }, - onSuccess: () => { + fetch("api/v2/torrents/setCategory", { + method: "POST", + body: new URLSearchParams({ + hashes: hashes.join("|"), + category: categoryName + }) + }) + .then((response) => { + if (!response.ok) + return; + updateMainData(); - } - }).send(); + }); }; createCategoryFN = () => { @@ -879,18 +880,19 @@ const initializeWindows = () => { }; removeCategoryFN = (categoryHash) => { - const categoryName = category_list.get(categoryHash).name; - new Request({ - url: "api/v2/torrents/removeCategories", - method: "post", - data: { - categories: categoryName - }, - onSuccess: () => { + fetch("api/v2/torrents/removeCategories", { + method: "POST", + body: new URLSearchParams({ + categories: category_list.get(categoryHash).name + }) + }) + .then((response) => { + if (!response.ok) + return; + setCategoryFilter(CATEGORIES_ALL); updateMainData(); - } - }).send(); + }); }; deleteUnusedCategoriesFN = () => { @@ -899,18 +901,19 @@ const initializeWindows = () => { if (torrentsTable.getFilteredTorrentsNumber("all", hash, TAGS_ALL, TRACKERS_ALL) === 0) categories.push(category.name); }); + fetch("api/v2/torrents/removeCategories", { + method: "POST", + body: new URLSearchParams({ + categories: categories.join("\n") + }) + }) + .then((response) => { + if (!response.ok) + return; - new Request({ - url: "api/v2/torrents/removeCategories", - method: "post", - data: { - categories: categories.join("\n") - }, - onSuccess: () => { setCategoryFilter(CATEGORIES_ALL); updateMainData(); - } - }).send(); + }); }; torrentAddTagsFN = () => { @@ -939,27 +942,24 @@ const initializeWindows = () => { if (hashes.length <= 0) return; - const tagName = tagList.has(tagHash) ? tagList.get(tagHash).name : ""; - new Request({ - url: (isSet ? "api/v2/torrents/addTags" : "api/v2/torrents/removeTags"), - method: "post", - data: { + fetch((isSet ? "api/v2/torrents/addTags" : "api/v2/torrents/removeTags"), { + method: "POST", + body: new URLSearchParams({ hashes: hashes.join("|"), - tags: tagName, - } - }).send(); + tags: (tagList.get(tagHash)?.name || "") + }) + }); }; torrentRemoveAllTagsFN = () => { const hashes = torrentsTable.selectedRowsIds(); if (hashes.length) { - new Request({ - url: ("api/v2/torrents/removeTags"), - method: "post", - data: { - hashes: hashes.join("|"), - } - }).send(); + fetch("api/v2/torrents/removeTags", { + method: "POST", + body: new URLSearchParams({ + hashes: hashes.join("|") + }) + }); } }; @@ -983,14 +983,12 @@ const initializeWindows = () => { }; removeTagFN = (tagHash) => { - const tagName = tagList.get(tagHash).name; - new Request({ - url: "api/v2/torrents/deleteTags", - method: "post", - data: { - tags: tagName - } - }).send(); + fetch("api/v2/torrents/deleteTags", { + method: "POST", + body: new URLSearchParams({ + tags: tagList.get(tagHash).name + }) + }); setTagFilter(TAGS_ALL); }; @@ -1000,13 +998,12 @@ const initializeWindows = () => { if (torrentsTable.getFilteredTorrentsNumber("all", CATEGORIES_ALL, hash, TRACKERS_ALL) === 0) tags.push(tag.name); }); - new Request({ - url: "api/v2/torrents/deleteTags", - method: "post", - data: { + fetch("api/v2/torrents/deleteTags", { + method: "POST", + body: new URLSearchParams({ tags: tags.join(",") - } - }).send(); + }) + }); setTagFilter(TAGS_ALL); }; @@ -1130,13 +1127,12 @@ const initializeWindows = () => { e.stopPropagation(); if (confirm("QBT_TR(Would you like to stop all torrents?)QBT_TR[CONTEXT=MainWindow]")) { - new Request({ - url: "api/v2/torrents/stop", - method: "post", - data: { + fetch("api/v2/torrents/stop", { + method: "POST", + body: new URLSearchParams({ hashes: "all" - } - }).send(); + }) + }); updateMainData(); } }); @@ -1146,13 +1142,12 @@ const initializeWindows = () => { e.stopPropagation(); if (confirm("QBT_TR(Would you like to start all torrents?)QBT_TR[CONTEXT=MainWindow]")) { - new Request({ - url: "api/v2/torrents/start", - method: "post", - data: { + fetch("api/v2/torrents/start", { + method: "POST", + body: new URLSearchParams({ hashes: "all" - } - }).send(); + }) + }); updateMainData(); } }); @@ -1165,13 +1160,12 @@ const initializeWindows = () => { const hashes = torrentsTable.selectedRowsIds(); if (hashes.length) { hashes.each((hash, index) => { - new Request({ - url: "api/v2/torrents/" + item, - method: "post", - data: { + fetch(`api/v2/torrents/${item}`, { + method: "POST", + body: new URLSearchParams({ hashes: hash - } - }).send(); + }) + }); }); updateMainData(); } @@ -1189,13 +1183,12 @@ const initializeWindows = () => { setQueuePositionFN = (cmd) => { const hashes = torrentsTable.selectedRowsIds(); if (hashes.length) { - new Request({ - url: "api/v2/torrents/" + cmd, - method: "post", - data: { + fetch(`api/v2/torrents/${cmd}`, { + method: "POST", + body: new URLSearchParams({ hashes: hashes.join("|") - } - }).send(); + }) + }); updateMainData(); } }; @@ -1229,13 +1222,15 @@ const initializeWindows = () => { e.preventDefault(); e.stopPropagation(); - new Request({ - url: "api/v2/auth/logout", - method: "post", - onSuccess: () => { + fetch("api/v2/auth/logout", { + method: "POST" + }) + .then((response) => { + if (!response.ok) + return; + window.location.reload(true); - } - }).send(); + }); }); addClickEvent("shutdown", (e) => { @@ -1243,17 +1238,19 @@ const initializeWindows = () => { e.stopPropagation(); if (confirm("QBT_TR(Are you sure you want to quit qBittorrent?)QBT_TR[CONTEXT=MainWindow]")) { - new Request({ - url: "api/v2/app/shutdown", - method: "post", - onSuccess: () => { + fetch("api/v2/app/shutdown", { + method: "POST" + }) + .then((response) => { + if (!response.ok) + return; + const shutdownMessage = "QBT_TR(%1 has been shutdown)QBT_TR[CONTEXT=HttpServer]".replace("%1", window.qBittorrent.Client.mainTitle()); document.write(` ${shutdownMessage}

${shutdownMessage}

`); document.close(); window.stop(); window.qBittorrent.Client.stop(); - } - }).send(); + }); } }); diff --git a/src/webui/www/private/scripts/prop-files.js b/src/webui/www/private/scripts/prop-files.js index f3d6fd81157f..c5b7d07206e7 100644 --- a/src/webui/www/private/scripts/prop-files.js +++ b/src/webui/www/private/scripts/prop-files.js @@ -354,7 +354,12 @@ window.qBittorrent.PropFiles ??= (() => { current_hash = new_hash; loadedNewTorrent = true; } - fetch(new URI("api/v2/torrents/files").setData("hash", current_hash), { + + const url = new URL("api/v2/torrents/files", window.location); + url.search = new URLSearchParams({ + hash: current_hash + }); + fetch(url, { method: "GET", cache: "no-store" }) diff --git a/src/webui/www/private/scripts/prop-general.js b/src/webui/www/private/scripts/prop-general.js index 698d6b508ee9..dbf80e8a91cb 100644 --- a/src/webui/www/private/scripts/prop-general.js +++ b/src/webui/www/private/scripts/prop-general.js @@ -88,7 +88,11 @@ window.qBittorrent.PropGeneral ??= (() => { return; } - fetch(new URI("api/v2/torrents/properties").setData("hash", current_id), { + const propertiesURL = new URL("api/v2/torrents/properties", window.location); + propertiesURL.search = new URLSearchParams({ + hash: current_id + }); + fetch(propertiesURL, { method: "GET", cache: "no-store" }) @@ -230,7 +234,11 @@ window.qBittorrent.PropGeneral ??= (() => { loadTorrentDataTimer = loadTorrentData.delay(5000); }); - fetch(new URI("api/v2/torrents/pieceStates").setData("hash", current_id), { + const pieceStatesURL = new URL("api/v2/torrents/pieceStates", window.location); + pieceStatesURL.search = new URLSearchParams({ + hash: current_id + }); + fetch(pieceStatesURL, { method: "GET", cache: "no-store" }) diff --git a/src/webui/www/private/scripts/prop-trackers.js b/src/webui/www/private/scripts/prop-trackers.js index a87c7f830f00..fd5b7ec4b1f1 100644 --- a/src/webui/www/private/scripts/prop-trackers.js +++ b/src/webui/www/private/scripts/prop-trackers.js @@ -58,19 +58,23 @@ window.qBittorrent.PropTrackers ??= (() => { torrentTrackersTable.clear(); current_hash = new_hash; } - const url = new URI("api/v2/torrents/trackers?hash=" + current_hash); - new Request.JSON({ - url: url, - method: "get", - noCache: true, - onComplete: () => { - clearTimeout(loadTrackersDataTimer); - loadTrackersDataTimer = loadTrackersData.delay(10000); - }, - onSuccess: (trackers) => { + + const url = new URL("api/v2/torrents/trackers", window.location); + url.search = new URLSearchParams({ + hash: current_hash + }); + fetch(url, { + method: "GET", + cache: "no-store" + }) + .then(async (response) => { + if (!response.ok) + return; + const selectedTrackers = torrentTrackersTable.selectedRowsIds(); torrentTrackersTable.clear(); + const trackers = await response.json(); if (trackers) { trackers.each((tracker) => { let status; @@ -113,8 +117,11 @@ window.qBittorrent.PropTrackers ??= (() => { if (selectedTrackers.length > 0) torrentTrackersTable.reselectRows(selectedTrackers); } - } - }).send(); + }) + .finally(() => { + clearTimeout(loadTrackersDataTimer); + loadTrackersDataTimer = loadTrackersData.delay(10000); + }); }; const updateData = () => { @@ -214,18 +221,19 @@ window.qBittorrent.PropTrackers ??= (() => { if (current_hash.length === 0) return; - const selectedTrackers = torrentTrackersTable.selectedRowsIds(); - new Request({ - url: "api/v2/torrents/removeTrackers", - method: "post", - data: { - hash: current_hash, - urls: selectedTrackers.map(encodeURIComponent).join("|") - }, - onSuccess: () => { + fetch("api/v2/torrents/removeTrackers", { + method: "POST", + body: new URLSearchParams({ + hash: current_hash, + urls: torrentTrackersTable.selectedRowsIds().map(encodeURIComponent).join("|") + }) + }) + .then((response) => { + if (!response.ok) + return; + updateData(); - } - }).send(); + }); }; const clear = () => { diff --git a/src/webui/www/private/scripts/prop-webseeds.js b/src/webui/www/private/scripts/prop-webseeds.js index b538443f240d..ef3773620ea0 100644 --- a/src/webui/www/private/scripts/prop-webseeds.js +++ b/src/webui/www/private/scripts/prop-webseeds.js @@ -58,18 +58,23 @@ window.qBittorrent.PropWebseeds ??= (() => { torrentWebseedsTable.clear(); current_hash = new_hash; } - new Request.JSON({ - url: new URI("api/v2/torrents/webseeds").setData("hash", current_hash), - method: "get", - noCache: true, - onComplete: () => { - clearTimeout(loadWebSeedsDataTimer); - loadWebSeedsDataTimer = loadWebSeedsData.delay(10000); - }, - onSuccess: (webseeds) => { + + const url = new URL("api/v2/torrents/webseeds", window.location); + url.search = new URLSearchParams({ + hash: current_hash + }); + fetch(url, { + method: "GET", + cache: "no-store" + }) + .then(async (response) => { + if (!response.ok) + return; + const selectedWebseeds = torrentWebseedsTable.selectedRowsIds(); torrentWebseedsTable.clear(); + const webseeds = await response.json(); if (webseeds) { // Update WebSeeds data webseeds.each((webseed) => { @@ -84,8 +89,11 @@ window.qBittorrent.PropWebseeds ??= (() => { if (selectedWebseeds.length > 0) torrentWebseedsTable.reselectRows(selectedWebseeds); - } - }).send(); + }) + .finally(() => { + clearTimeout(loadWebSeedsDataTimer); + loadWebSeedsDataTimer = loadWebSeedsData.delay(10000); + }); }; const updateData = () => { @@ -190,18 +198,19 @@ window.qBittorrent.PropWebseeds ??= (() => { if (current_hash.length === 0) return; - const selectedWebseeds = torrentWebseedsTable.selectedRowsIds(); - new Request({ - url: "api/v2/torrents/removeWebSeeds", - method: "post", - data: { - hash: current_hash, - urls: selectedWebseeds.map(webseed => encodeURIComponent(webseed)).join("|") - }, - onSuccess: () => { + fetch("api/v2/torrents/removeWebSeeds", { + method: "POST", + body: new URLSearchParams({ + hash: current_hash, + urls: torrentWebseedsTable.selectedRowsIds().map(webseed => encodeURIComponent(webseed)).join("|") + }) + }) + .then((response) => { + if (!response.ok) + return; + updateData(); - } - }).send(); + }); }; const clear = () => { diff --git a/src/webui/www/private/scripts/rename-files.js b/src/webui/www/private/scripts/rename-files.js index 5d4d4653022e..e6d6de6e39f8 100644 --- a/src/webui/www/private/scripts/rename-files.js +++ b/src/webui/www/private/scripts/rename-files.js @@ -47,7 +47,7 @@ window.qBittorrent.MultiRename ??= (() => { onChanged: (rows) => {}, onInvalidRegex: (err) => {}, onRenamed: (rows) => {}, - onRenameError: (err) => {}, + onRenameError: (response) => {}, _inner_update: function() { const findMatches = (regex, str) => { @@ -240,21 +240,19 @@ window.qBittorrent.MultiRename ??= (() => { const newPath = parentPath ? parentPath + window.qBittorrent.Filesystem.PathSeparator + newName : newName; - const renameRequest = new Request({ - url: isFolder ? "api/v2/torrents/renameFolder" : "api/v2/torrents/renameFile", - method: "post", - data: { - hash: this.hash, - oldPath: oldPath, - newPath: newPath - } - }); try { - await renameRequest.send(); + await fetch((isFolder ? "api/v2/torrents/renameFolder" : "api/v2/torrents/renameFile"), { + method: "POST", + body: new URLSearchParams({ + hash: this.hash, + oldPath: oldPath, + newPath: newPath + }) + }); replaced.push(match); } - catch (err) { - this.onRenameError(err, match); + catch (response) { + this.onRenameError(response, match); } }.bind(this); diff --git a/src/webui/www/private/scripts/search.js b/src/webui/www/private/scripts/search.js index e51d4ce13156..b1c0a1344644 100644 --- a/src/webui/www/private/scripts/search.js +++ b/src/webui/www/private/scripts/search.js @@ -247,13 +247,12 @@ window.qBittorrent.Search ??= (() => { tab.destroy(); - new Request({ - url: new URI("api/v2/search/delete"), - method: "post", - data: { + fetch("api/v2/search/delete", { + method: "POST", + body: new URLSearchParams({ id: searchId - }, - }).send(); + }) + }); const searchJobs = JSON.parse(LocalPreferences.get("search_jobs", "[]")); const jobIndex = searchJobs.findIndex((job) => job.id === searchId); @@ -395,42 +394,45 @@ window.qBittorrent.Search ??= (() => { const startSearch = (pattern, category, plugins) => { searchPatternChanged = false; + fetch("api/v2/search/start", { + method: "POST", + body: new URLSearchParams({ + pattern: pattern, + category: category, + plugins: plugins + }) + }) + .then(async (response) => { + if (!response.ok) + return; + + const responseJSON = await response.json(); - const url = new URI("api/v2/search/start"); - new Request.JSON({ - url: url, - method: "post", - data: { - pattern: pattern, - category: category, - plugins: plugins - }, - onSuccess: (response) => { document.getElementById("startSearchButton").lastChild.textContent = "QBT_TR(Stop)QBT_TR[CONTEXT=SearchEngineWidget]"; - const searchId = response.id; + const searchId = responseJSON.id; createSearchTab(searchId, pattern); const searchJobs = JSON.parse(LocalPreferences.get("search_jobs", "[]")); searchJobs.push({ id: searchId, pattern: pattern }); LocalPreferences.set("search_jobs", JSON.stringify(searchJobs)); - } - }).send(); + }); }; const stopSearch = (searchId) => { - const url = new URI("api/v2/search/stop"); - new Request({ - url: url, - method: "post", - data: { - id: searchId - }, - onSuccess: (response) => { + fetch("api/v2/search/stop", { + method: "POST", + body: new URLSearchParams({ + id: searchId + }) + }) + .then((response) => { + if (!response.ok) + return; + resetSearchState(searchId); // not strictly necessary to do this when the tab is being closed, but there's no harm in it updateStatusIconElement(searchId, "QBT_TR(Search aborted)QBT_TR[CONTEXT=SearchJobWidget]", "images/task-reject.svg"); - } - }).send(); + }); }; const getSelectedSearchId = () => { @@ -638,11 +640,16 @@ window.qBittorrent.Search ??= (() => { }; const getPlugins = () => { - new Request.JSON({ - url: new URI("api/v2/search/plugins"), - method: "get", - noCache: true, - onSuccess: (response) => { + fetch("api/v2/search/plugins", { + method: "GET", + cache: "no-store" + }) + .then(async (response) => { + if (!response.ok) + return; + + const responseJSON = await response.json(); + const createOption = (text, value, disabled = false) => { const option = document.createElement("option"); if (value !== undefined) @@ -652,10 +659,10 @@ window.qBittorrent.Search ??= (() => { return option; }; - if (response !== prevSearchPluginsResponse) { - prevSearchPluginsResponse = response; + if (prevSearchPluginsResponse !== responseJSON) { + prevSearchPluginsResponse = responseJSON; searchPlugins.length = 0; - response.forEach((plugin) => { + responseJSON.forEach((plugin) => { searchPlugins.push(plugin); }); @@ -697,8 +704,7 @@ window.qBittorrent.Search ??= (() => { reselectPlugin(); } - } - }).send(); + }); }; const getPlugin = (name) => { @@ -766,30 +772,30 @@ window.qBittorrent.Search ??= (() => { const loadSearchResultsData = function(searchId) { const state = searchState.get(searchId); - - const maxResults = 500; - const url = new URI("api/v2/search/results"); - new Request.JSON({ - url: url, - method: "get", - noCache: true, - data: { - id: searchId, - limit: maxResults, - offset: state.rowId - }, - onFailure: function(response) { - if ((response.status === 400) || (response.status === 404)) { - // bad params. search id is invalid - resetSearchState(searchId); - updateStatusIconElement(searchId, "QBT_TR(An error occurred during search...)QBT_TR[CONTEXT=SearchJobWidget]", "images/error.svg"); - } - else { - clearTimeout(state.loadResultsTimer); - state.loadResultsTimer = loadSearchResultsData.delay(3000, this, searchId); + const url = new URL("api/v2/search/results", window.location); + url.search = new URLSearchParams({ + id: searchId, + limit: 500, + offset: state.rowId + }); + fetch(url, { + method: "GET", + cache: "no-store" + }) + .then(async (response) => { + if (!response.ok) { + if ((response.status === 400) || (response.status === 404)) { + // bad params. search id is invalid + resetSearchState(searchId); + updateStatusIconElement(searchId, "QBT_TR(An error occurred during search...)QBT_TR[CONTEXT=SearchJobWidget]", "images/error.svg"); + } + else { + clearTimeout(state.loadResultsTimer); + state.loadResultsTimer = loadSearchResultsData.delay(3000, this, searchId); + } + return; } - }, - onSuccess: function(response) { + $("error_div").textContent = ""; const state = searchState.get(searchId); @@ -800,12 +806,13 @@ window.qBittorrent.Search ??= (() => { return; } - if (response) { + const responseJSON = await response.json(); + if (responseJSON) { const state = searchState.get(searchId); const newRows = []; - if (response.results) { - const results = response.results; + if (responseJSON.results) { + const results = responseJSON.results; for (let i = 0; i < results.length; ++i) { const result = results[i]; const row = { @@ -838,7 +845,7 @@ window.qBittorrent.Search ??= (() => { searchResultsTable.updateTable(); } - if ((response.status === "Stopped") && (state.rowId >= response.total)) { + if ((responseJSON.status === "Stopped") && (state.rowId >= responseJSON.total)) { resetSearchState(searchId); updateStatusIconElement(searchId, "QBT_TR(Search has finished)QBT_TR[CONTEXT=SearchJobWidget]", "images/task-complete.svg"); return; @@ -847,8 +854,7 @@ window.qBittorrent.Search ??= (() => { clearTimeout(state.loadResultsTimer); state.loadResultsTimer = loadSearchResultsData.delay(2000, this, searchId); - } - }).send(); + }); }; const updateSearchResultsData = function(searchId) { diff --git a/src/webui/www/private/views/cookies.html b/src/webui/www/private/views/cookies.html index 0b6c6fa5d24c..e7e301057637 100644 --- a/src/webui/www/private/views/cookies.html +++ b/src/webui/www/private/views/cookies.html @@ -122,38 +122,43 @@ expirationDate: expDate, }; }); + fetch("api/v2/app/setCookies", { + method: "POST", + body: new URLSearchParams({ + cookies: JSON.stringify(cookies) + }) + }) + .then(async (response) => { + if (!response.ok) { + let error = "Unable to save cookies"; + const responseText = await response.text(); + if (responseText.length > 0) + error += `: ${responseText}`; + alert(error); + return; + } - new Request({ - url: "api/v2/app/setCookies", - method: "post", - noCache: true, - data: { - cookies: JSON.stringify(cookies) - }, - onFailure: (response) => { - let error = "Unable to save cookies"; - if (response?.responseText) - error += `: ${response.responseText}`; - alert(error); - }, - onSuccess: (response) => { window.qBittorrent.Client.closeWindow(document.getElementById("cookiesPage")); - } - }).send(); + }); }; const loadCookies = () => { - new Request.JSON({ - url: "api/v2/app/cookies", - method: "get", - onFailure: (response) => { - let error = "Unable to load cookies"; - if (response?.responseText) - error += `: ${response.responseText}`; - alert(error); - }, - onSuccess: (response) => { - for (const cookie of response) { + fetch("api/v2/app/cookies", { + method: "GET", + cache: "no-store" + }) + .then(async (response) => { + if (!response.ok) { + let error = "Unable to load cookies"; + const responseText = await response.text(); + if (responseText.length > 0) + error += `: ${responseText}`; + alert(error); + return; + } + + const responseJSON = await response.json(); + for (const cookie of responseJSON) { const row = addCookie(); row.querySelector("td.domain input").value = cookie.domain; row.querySelector("td.path input").value = cookie.path; @@ -167,8 +172,7 @@ row.querySelector("td.expDate input").valueAsNumber = date.getTime(); } } - } - }).send(); + }); }; const setup = () => { diff --git a/src/webui/www/private/views/installsearchplugin.html b/src/webui/www/private/views/installsearchplugin.html index db0520120e15..de77a46e167d 100644 --- a/src/webui/www/private/views/installsearchplugin.html +++ b/src/webui/www/private/views/installsearchplugin.html @@ -67,16 +67,18 @@ const newPluginOk = () => { const path = $("newPluginPath").value.trim(); if (path) { - new Request({ - url: "api/v2/search/installPlugin", - method: "post", - data: { - sources: path, - }, - onRequest: () => { + fetch("api/v2/search/installPlugin", { + method: "POST", + body: new URLSearchParams({ + sources: path + }) + }) + .then((response) => { + if (!response.ok) + return; + window.qBittorrent.Client.closeWindow(document.getElementById("installSearchPlugin")); - } - }).send(); + }); } }; diff --git a/src/webui/www/private/views/preferences.html b/src/webui/www/private/views/preferences.html index 074b723197d4..16dde1347522 100644 --- a/src/webui/www/private/views/preferences.html +++ b/src/webui/www/private/views/preferences.html @@ -1888,16 +1888,17 @@ }; const sendTestEmail = () => { - new Request({ - url: "api/v2/app/sendTestEmail", - method: "post", - onFailure: () => { - alert("QBT_TR(Could not contact qBittorrent)QBT_TR[CONTEXT=HttpServer]"); - }, - onSuccess: () => { + fetch("api/v2/app/sendTestEmail", { + method: "POST" + }) + .then((response) => { + if (!response.ok) { + alert("QBT_TR(Could not contact qBittorrent)QBT_TR[CONTEXT=HttpServer]"); + return; + } + alert("QBT_TR(Attempted to send email. Check your inbox to confirm success)QBT_TR[CONTEXT=OptionsDialog]"); - } - }).send(); + }); }; const updateAutoRunOnTorrentAdded = () => { @@ -2078,16 +2079,18 @@ // Advanced Tab const updateNetworkInterfaces = (default_iface, default_iface_name) => { - const url = "api/v2/app/networkInterfaceList"; $("networkInterface").getChildren().each(c => c.destroy()); - new Request.JSON({ - url: url, - method: "get", - noCache: true, - onFailure: () => { - alert("Could not contact qBittorrent"); - }, - onSuccess: (ifaces) => { + fetch("api/v2/app/networkInterfaceList", { + method: "GET", + cache: "no-store" + }) + .then(async (response) => { + if (!response.ok) { + alert("Could not contact qBittorrent"); + return; + } + + const ifaces = await response.json(); if (!Array.isArray(ifaces)) return; @@ -2100,24 +2103,26 @@ $("networkInterface").options.add(new Option(item.name, item.value)); }); $("networkInterface").value = default_iface; - } - }).send(); + }); }; const updateInterfaceAddresses = (iface, default_addr) => { - const url = "api/v2/app/networkInterfaceAddressList"; $("optionalIPAddressToBind").getChildren().each(c => c.destroy()); - new Request.JSON({ - url: url, - method: "get", - noCache: true, - data: { - iface: iface - }, - onFailure: () => { - alert("Could not contact qBittorrent"); - }, - onSuccess: (addresses) => { + const url = new URL("api/v2/app/networkInterfaceAddressList", window.location); + url.search = new URLSearchParams({ + iface: iface + }); + fetch(url, { + method: "GET", + cache: "no-store" + }) + .then(async (response) => { + if (!response.ok) { + alert("Could not contact qBittorrent"); + return; + } + + const addresses = await response.json(); if (!addresses) return; @@ -2128,8 +2133,7 @@ $("optionalIPAddressToBind").options.add(new Option(item, item)); }); $("optionalIPAddressToBind").value = default_addr; - } - }).send(); + }); }; const updateWebuiLocaleSelect = (selected) => { diff --git a/src/webui/www/private/views/rss.html b/src/webui/www/private/views/rss.html index 7e8f59d43a56..849897709a6d 100644 --- a/src/webui/www/private/views/rss.html +++ b/src/webui/www/private/views/rss.html @@ -454,14 +454,20 @@ }; const updateRssFeedList = () => { - new Request.JSON({ - url: "api/v2/rss/items", - method: "get", - noCache: true, - data: { - withData: true - }, - onSuccess: (response) => { + const url = new URL("api/v2/rss/items", window.location); + url.search = new URLSearchParams({ + withData: true + }); + fetch(url, { + method: "GET", + cache: "no-store" + }) + .then(async (response) => { + if (!response.ok) + return; + + const responseJSON = await response.json(); + // flatten folder structure const flattenedResp = []; const recFlatten = (current, name = "", depth = 0, fullName = "") => { @@ -488,7 +494,7 @@ } } }; - recFlatten(response); + recFlatten(responseJSON); // check if rows matches flattened response const rssFeedRows = [...rssFeedTable.getRowValues()]; @@ -687,8 +693,7 @@ rssFeedTable.updateTable(false); rssFeedTable.updateIcons(); } - } - }).send(); + }); }; const refreshFeed = (feedUid) => { @@ -699,17 +704,19 @@ } rssFeedTable.updateIcons(); - new Request({ - url: "api/v2/rss/refreshItem", - method: "post", - data: { - itemPath: pathByFeedId.get(feedUid) - }, - onFailure: (response) => { - if (response.status === 409) - alert(response.responseText); - } - }).send(); + fetch("api/v2/rss/refreshItem", { + method: "POST", + body: new URLSearchParams({ + itemPath: pathByFeedId.get(feedUid) + }) + }) + .then(async (response) => { + if (!response.ok) { + if (response.status === 409) + alert(await response.text()); + return; + } + }); }; const refreshAllFeeds = () => { @@ -802,17 +809,19 @@ rssFeedTable.updateTable(true); // send request - new Request({ - url: "api/v2/rss/markAsRead", - method: "post", - data: { - itemPath: path - }, - onFailure: (response) => { - if (response.status === 409) - alert(response.responseText); - } - }).send(); + fetch("api/v2/rss/markAsRead", { + method: "POST", + body: new URLSearchParams({ + itemPath: path + }) + }) + .then(async (response) => { + if (!response.ok) { + if (response.status === 409) + alert(await response.text()); + return; + } + }); }; const markArticleAsRead = (path, id) => { @@ -843,18 +852,20 @@ rssFeedTable.updateTable(true); - new Request({ - url: "api/v2/rss/markAsRead", - method: "post", - data: { - itemPath: path, - articleId: id - }, - onFailure: (response) => { - if (response.status === 409) - alert(response.responseText); - } - }).send(); + fetch("api/v2/rss/markAsRead", { + method: "POST", + body: new URLSearchParams({ + itemPath: path, + articleId: id + }) + }) + .then(async (response) => { + if (!response.ok) { + if (response.status === 409) + alert(await response.text()); + return; + } + }); } }; diff --git a/src/webui/www/private/views/rssDownloader.html b/src/webui/www/private/views/rssDownloader.html index 33d9a73ca8e3..77b401d4bd66 100644 --- a/src/webui/www/private/views/rssDownloader.html +++ b/src/webui/www/private/views/rssDownloader.html @@ -437,31 +437,41 @@ } }); // get all categories and add to combobox - new Request.JSON({ - url: "api/v2/torrents/categories", - method: "get", - noCache: true, - onSuccess: (response) => { + fetch("api/v2/torrents/categories", { + method: "GET", + cache: "no-store" + }) + .then(async (response) => { + if (!response.ok) + return; + + const responseJSON = await response.json(); + const combobox = $("assignCategoryCombobox"); - for (const cat in response) { - if (!Object.hasOwn(response, cat)) + for (const cat in responseJSON) { + if (!Object.hasOwn(responseJSON, cat)) continue; const option = document.createElement("option"); option.text = option.value = cat; combobox.add(option); } - } - }).send(); + }); // get all rss feed - new Request.JSON({ - url: "api/v2/rss/items", - method: "get", - noCache: true, - data: { - withData: false - }, - onSuccess: (response) => { + const url = new URL("api/v2/rss/items", window.location); + url.search = new URLSearchParams({ + withData: false + }); + fetch(url, { + method: "GET", + cache: "no-store" + }) + .then(async (response) => { + if (!response.ok) + return; + + const responseJSON = await response.json(); + feedList = []; const flatten = (root) => { for (const child in root) { @@ -471,9 +481,8 @@ flatten(root[child]); } }; - flatten(response); - } - }).send(); + flatten(responseJSON); + }); $("savetoDifferentDir").addEventListener("click", () => { $("saveToText").disabled = !$("savetoDifferentDir").checked; }); @@ -482,41 +491,47 @@ const updateRulesList = () => { // get all rules - new Request.JSON({ - url: "api/v2/rss/rules", - method: "get", - noCache: true, - onSuccess: (response) => { + fetch("api/v2/rss/rules", { + method: "GET", + cache: "no-store" + }) + .then(async (response) => { + if (!response.ok) + return; + + const responseJSON = await response.json(); + rssDownloaderRulesTable.clear(); let rowCount = 0; - for (const rule in response) { - if (!Object.hasOwn(response, rule)) + for (const rule in responseJSON) { + if (!Object.hasOwn(responseJSON, rule)) continue; rssDownloaderRulesTable.updateRowData({ rowId: rowCount++, - checked: response[rule].enabled, + checked: responseJSON[rule].enabled, name: rule }); } rssDownloaderRulesTable.updateTable(false); - rulesList = response; - } - }).send(); + rulesList = responseJSON; + }); }; const modifyRuleState = (rule, setting, newState, callback = () => {}) => { rulesList[rule][setting] = newState; - new Request({ - url: "api/v2/rss/setRule", - method: "post", - data: { - ruleName: rule, - ruleDef: JSON.stringify(rulesList[rule]) - }, - onSuccess: () => { + fetch("api/v2/rss/setRule", { + method: "POST", + body: new URLSearchParams({ + ruleName: rule, + ruleDef: JSON.stringify(rulesList[rule]) + }) + }) + .then((response) => { + if (!response.ok) + return; + callback(); - } - }).send(); + }); }; const addRule = () => { @@ -639,39 +654,47 @@ break; } - new Request({ - url: "api/v2/rss/setRule", - method: "post", - data: { - ruleName: rule, - ruleDef: JSON.stringify(rulesList[rule]) - }, - onSuccess: () => { + fetch("api/v2/rss/setRule", { + method: "POST", + body: new URLSearchParams({ + ruleName: rule, + ruleDef: JSON.stringify(rulesList[rule]) + }) + }) + .then((response) => { + if (!response.ok) + return; + updateMatchingArticles(rule); - } - }).send(); + }); }; const updateMatchingArticles = (ruleName) => { - new Request.JSON({ - url: "api/v2/rss/matchingArticles", - method: "get", - noCache: true, - data: { - ruleName: ruleName - }, - onSuccess: (response) => { + const url = new URL("api/v2/rss/matchingArticles", window.location); + url.search = new URLSearchParams({ + ruleName: ruleName + }); + fetch(url, { + method: "GET", + cache: "no-store" + }) + .then(async (response) => { + if (!response.ok) + return; + + const responseJSON = await response.json(); + rssDownloaderArticlesTable.clear(); let rowCount = 0; - for (const feed in response) { - if (!Object.hasOwn(response, feed)) + for (const feed in responseJSON) { + if (!Object.hasOwn(responseJSON, feed)) continue; rssDownloaderArticlesTable.updateRowData({ rowId: rowCount++, name: feed, isFeed: true }); - response[feed].each((article) => { + responseJSON[feed].each((article) => { rssDownloaderArticlesTable.updateRowData({ rowId: rowCount++, name: article, @@ -680,8 +703,7 @@ }); } rssDownloaderArticlesTable.updateTable(false); - } - }).send(); + }); }; const showRule = (ruleName) => { diff --git a/src/webui/www/private/views/searchplugins.html b/src/webui/www/private/views/searchplugins.html index 50bb2837dc24..e6a6b5bea232 100644 --- a/src/webui/www/private/views/searchplugins.html +++ b/src/webui/www/private/views/searchplugins.html @@ -130,15 +130,12 @@

QBT_TR(Installed search plugins:)QBT_TR[CONTEXT=PluginSelectDlg]

}; const uninstallPlugin = () => { - const plugins = searchPluginsTable.selectedRowsIds().join("|"); - const url = new URI("api/v2/search/uninstallPlugin"); - new Request({ - url: url, - method: "post", - data: { - names: plugins, - } - }).send(); + fetch("api/v2/search/uninstallPlugin", { + method: "POST", + body: new URLSearchParams({ + names: searchPluginsTable.selectedRowsIds().join("|") + }) + }); }; const enablePlugin = () => { @@ -147,23 +144,19 @@

QBT_TR(Installed search plugins:)QBT_TR[CONTEXT=PluginSelectDlg]

if (plugins && plugins.length) enable = !window.qBittorrent.Search.getPlugin(plugins[0]).enabled; - const url = new URI("api/v2/search/enablePlugin"); - new Request({ - url: url, - method: "post", - data: { + fetch("api/v2/search/enablePlugin", { + method: "POST", + body: new URLSearchParams({ names: plugins.join("|"), enable: enable - } - }).send(); + }) + }); }; const checkForUpdates = () => { - const url = new URI("api/v2/search/updatePlugins"); - new Request({ - url: url, - method: "post" - }).send(); + fetch("api/v2/search/updatePlugins", { + method: "POST" + }); }; const calculateContextMenuOffsets = () => {