diff --git a/src/webui/www/private/index.html b/src/webui/www/private/index.html
index 0243723eb8d8..976ff6c3378c 100644
--- a/src/webui/www/private/index.html
+++ b/src/webui/www/private/index.html
@@ -29,6 +29,7 @@
+
diff --git a/src/webui/www/private/scripts/addtorrent.js b/src/webui/www/private/scripts/addtorrent.js
index 7252a97ae0a0..a3b87ccdf587 100644
--- a/src/webui/www/private/scripts/addtorrent.js
+++ b/src/webui/www/private/scripts/addtorrent.js
@@ -45,10 +45,10 @@ window.qBittorrent.AddTorrent ??= (() => {
let source = "";
let downloader = "";
- const localPreferences = new window.qBittorrent.LocalPreferences.LocalPreferences();
+ const clientData = window.parent.qBittorrent.ClientData;
const getCategories = () => {
- const defaultCategory = localPreferences.get("add_torrent_default_category", "");
+ const defaultCategory = clientData.get("add_torrent_default_category") ?? "";
const categorySelect = document.getElementById("categorySelect");
for (const name of window.parent.qBittorrent.Client.categoryMap.keys()) {
const option = document.createElement("option");
@@ -320,10 +320,7 @@ window.qBittorrent.AddTorrent ??= (() => {
if (document.getElementById("setDefaultCategory").checked) {
const category = document.getElementById("category").value.trim();
- if (category.length === 0)
- localPreferences.remove("add_torrent_default_category");
- else
- localPreferences.set("add_torrent_default_category", category);
+ clientData.set({ add_torrent_default_category: (category.length > 0) ? category : null }).catch(console.error);
}
};
diff --git a/src/webui/www/private/scripts/client-data.js b/src/webui/www/private/scripts/client-data.js
new file mode 100644
index 000000000000..74b3a0dd56b9
--- /dev/null
+++ b/src/webui/www/private/scripts/client-data.js
@@ -0,0 +1,129 @@
+/*
+ * Bittorrent Client using Qt and libtorrent.
+ * Copyright (C) 2025 Thomas Piccirello
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give permission to
+ * link this program with the OpenSSL project's "OpenSSL" library (or with
+ * modified versions of it that use the same license as the "OpenSSL" library),
+ * and distribute the linked executables. You must obey the GNU General Public
+ * License in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s), you may extend this exception to your version of the file(s),
+ * but you are not obligated to do so. If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+"use strict";
+
+window.qBittorrent ??= {};
+window.qBittorrent.ClientData ??= (() => {
+ const exports = () => {
+ return new ClientData();
+ };
+
+ // this is exposed as a singleton
+ class ClientData {
+ /**
+ * @type Map
+ */
+ #cache = new Map();
+
+ #keyPrefix = "qbt_";
+
+ #addKeyPrefix(data) {
+ return Object.fromEntries(Object.entries(data).map(([key, value]) => ([`${this.#keyPrefix}${key}`, value])));
+ }
+
+ #removeKeyPrefix(data) {
+ return Object.fromEntries(Object.entries(data).map(([key, value]) => ([key.substring(this.#keyPrefix.length), value])));
+ }
+
+ /**
+ * @param {string[]} keys
+ * @returns {Record}
+ */
+ async #fetch(keys) {
+ keys = keys.map(key => `${this.#keyPrefix}${key}`);
+ return await fetch("api/v2/clientdata/load", {
+ method: "POST",
+ body: new URLSearchParams({
+ keys: JSON.stringify(keys)
+ })
+ })
+ .then(async (response) => {
+ if (!response.ok)
+ return;
+
+ const data = await response.json();
+ return this.#removeKeyPrefix(data);
+ });
+ }
+
+ /**
+ * @param {Record} data
+ */
+ async #set(data) {
+ data = this.#addKeyPrefix(data);
+ await fetch("api/v2/clientdata/store", {
+ method: "POST",
+ body: new URLSearchParams({
+ data: JSON.stringify(data)
+ })
+ })
+ .then((response) => {
+ if (!response.ok)
+ throw new Error("Failed to store client data");
+ });
+ }
+
+ /**
+ * @param {string} key
+ * @returns {any}
+ */
+ get(key) {
+ return this.#cache.get(key);
+ }
+
+ /**
+ * @param {string[]} keys
+ * @returns {Record}
+ */
+ async fetch(keys = []) {
+ const keysToFetch = keys.filter((key) => !this.#cache.has(key));
+ if (keysToFetch.length > 0) {
+ const fetchedData = await this.#fetch(keysToFetch);
+ for (const [key, value] of Object.entries(fetchedData))
+ this.#cache.set(key, value);
+ }
+
+ return Object.fromEntries(keys.map((key) => ([key, this.#cache.get(key)])));
+ }
+
+ /**
+ * @param {Record} data
+ */
+ async set(data) {
+ await this.#set(data);
+
+ // update cache
+ for (const [key, value] of Object.entries(data))
+ this.#cache.set(key, value);
+ }
+ }
+
+ return exports();
+})();
+Object.freeze(window.qBittorrent.ClientData);
diff --git a/src/webui/www/private/scripts/client.js b/src/webui/www/private/scripts/client.js
index 3aecd340905e..f5414cb286d6 100644
--- a/src/webui/www/private/scripts/client.js
+++ b/src/webui/www/private/scripts/client.js
@@ -31,6 +31,7 @@ window.qBittorrent.Client ??= (() => {
return {
setup: setup,
initializeCaches: initializeCaches,
+ initializeClientData: initializeClientData,
closeWindow: closeWindow,
closeFrameWindow: closeFrameWindow,
getSyncMainDataInterval: getSyncMainDataInterval,
@@ -56,15 +57,47 @@ window.qBittorrent.Client ??= (() => {
const tagMap = new Map();
let cacheAllSettled;
+ let clientDataPromise;
const setup = () => {
// fetch various data and store it in memory
+ clientDataPromise = window.qBittorrent.ClientData.fetch([
+ "show_search_engine",
+ "show_rss_reader",
+ "show_log_viewer",
+ "speed_in_browser_title_bar",
+ "show_top_toolbar",
+ "show_status_bar",
+ "show_filters_sidebar",
+ "hide_zero_status_filters",
+ "color_scheme",
+ "full_url_tracker_column",
+ "use_alt_row_colors",
+ "use_virtual_list",
+ "dblclick_complete",
+ "dblclick_download",
+ "dblclick_filter",
+ "search_in_filter",
+ "qbt_selected_log_levels",
+ "add_torrent_default_category",
+ ]);
+
cacheAllSettled = Promise.allSettled([
window.qBittorrent.Cache.buildInfo.init(),
window.qBittorrent.Cache.preferences.init(),
- window.qBittorrent.Cache.qbtVersion.init()
+ window.qBittorrent.Cache.qbtVersion.init(),
+ clientDataPromise,
]);
};
+ const initializeClientData = async () => {
+ try {
+ await clientDataPromise;
+ }
+ catch (error) {
+ console.error(`Failed to initialize client data. Reason: "${error}".`);
+ }
+ };
+
const initializeCaches = async () => {
const results = await cacheAllSettled;
for (const [idx, result] of results.entries()) {
@@ -223,8 +256,8 @@ let queueing_enabled = true;
let serverSyncMainDataInterval = 1500;
let customSyncMainDataInterval = null;
let useSubcategories = true;
-const useAutoHideZeroStatusFilters = localPreferences.get("hide_zero_status_filters", "false") === "true";
-const displayFullURLTrackerColumn = localPreferences.get("full_url_tracker_column", "false") === "true";
+let useAutoHideZeroStatusFilters = false;
+let displayFullURLTrackerColumn = false;
/* Categories filter */
const CATEGORIES_ALL = "b4af0e4c-e76d-4bac-a392-46cbc18d9655";
@@ -250,6 +283,8 @@ const TRACKERS_WARNING = "82a702c5-210c-412b-829f-97632d7557e9";
// Map>
const trackerMap = new Map();
+const clientData = window.qBittorrent.ClientData;
+
let selectedTracker = localPreferences.get("selected_tracker", TRACKERS_ALL);
let setTrackerFilter = () => {};
@@ -258,7 +293,13 @@ let selectedStatus = localPreferences.get("selected_filter", "all");
let setStatusFilter = () => {};
let toggleFilterDisplay = () => {};
-window.addEventListener("DOMContentLoaded", (event) => {
+window.addEventListener("DOMContentLoaded", async (event) => {
+ await window.qBittorrent.Client.initializeClientData();
+ window.qBittorrent.ColorScheme.update();
+
+ useAutoHideZeroStatusFilters = clientData.get("hide_zero_status_filters") === true;
+ displayFullURLTrackerColumn = clientData.get("full_url_tracker_column") === true;
+
window.qBittorrent.LocalPreferences.upgrade();
let isSearchPanelLoaded = false;
@@ -405,6 +446,13 @@ window.addEventListener("DOMContentLoaded", (event) => {
localPreferences.set(`filter_${filterListID.replace("FilterList", "")}_collapsed`, filterList.classList.toggle("invisible").toString());
};
+ const highlightSelectedStatus = () => {
+ const statusFilter = document.getElementById("statusFilterList");
+ const filterID = `${selectedStatus}_filter`;
+ for (const status of statusFilter.children)
+ status.classList.toggle("selectedFilter", (status.id === filterID));
+ };
+
new MochaUI.Panel({
id: "Filters",
title: "Panel",
@@ -426,35 +474,35 @@ window.addEventListener("DOMContentLoaded", (event) => {
initializeWindows();
// Show Top Toolbar is enabled by default
- let showTopToolbar = localPreferences.get("show_top_toolbar", "true") === "true";
+ let showTopToolbar = clientData.get("show_top_toolbar") !== false;
if (!showTopToolbar) {
document.getElementById("showTopToolbarLink").firstElementChild.style.opacity = "0";
document.getElementById("mochaToolbar").classList.add("invisible");
}
// Show Status Bar is enabled by default
- let showStatusBar = localPreferences.get("show_status_bar", "true") === "true";
+ let showStatusBar = clientData.get("show_status_bar") !== false;
if (!showStatusBar) {
document.getElementById("showStatusBarLink").firstElementChild.style.opacity = "0";
document.getElementById("desktopFooterWrapper").classList.add("invisible");
}
// Show Filters Sidebar is enabled by default
- let showFiltersSidebar = localPreferences.get("show_filters_sidebar", "true") === "true";
+ let showFiltersSidebar = clientData.get("show_filters_sidebar") !== false;
if (!showFiltersSidebar) {
document.getElementById("showFiltersSidebarLink").firstElementChild.style.opacity = "0";
document.getElementById("filtersColumn").classList.add("invisible");
document.getElementById("filtersColumn_handle").classList.add("invisible");
}
- let speedInTitle = localPreferences.get("speed_in_browser_title_bar") === "true";
+ let speedInTitle = clientData.get("speed_in_browser_title_bar") === true;
if (!speedInTitle)
document.getElementById("speedInBrowserTitleBarLink").firstElementChild.style.opacity = "0";
// After showing/hiding the toolbar + status bar
- window.qBittorrent.Client.showSearchEngine(localPreferences.get("show_search_engine") !== "false");
- window.qBittorrent.Client.showRssReader(localPreferences.get("show_rss_reader") !== "false");
- window.qBittorrent.Client.showLogViewer(localPreferences.get("show_log_viewer") === "true");
+ window.qBittorrent.Client.showSearchEngine(clientData.get("show_search_engine") !== false);
+ window.qBittorrent.Client.showRssReader(clientData.get("show_rss_reader") !== false);
+ window.qBittorrent.Client.showLogViewer(clientData.get("show_log_viewer") === true);
// After Show Top Toolbar
MochaUI.Desktop.setDesktopSize();
@@ -571,13 +619,6 @@ window.addEventListener("DOMContentLoaded", (event) => {
window.qBittorrent.Filters.clearStatusFilter();
};
- const highlightSelectedStatus = () => {
- const statusFilter = document.getElementById("statusFilterList");
- const filterID = `${selectedStatus}_filter`;
- for (const status of statusFilter.children)
- status.classList.toggle("selectedFilter", (status.id === filterID));
- };
-
const updateCategoryList = () => {
const categoryList = document.getElementById("categoryFilterList");
if (!categoryList)
@@ -1223,7 +1264,7 @@ window.addEventListener("DOMContentLoaded", (event) => {
document.getElementById("showTopToolbarLink").addEventListener("click", (e) => {
showTopToolbar = !showTopToolbar;
- localPreferences.set("show_top_toolbar", showTopToolbar.toString());
+ clientData.set({ show_top_toolbar: showTopToolbar }).catch(console.error);
if (showTopToolbar) {
document.getElementById("showTopToolbarLink").firstElementChild.style.opacity = "1";
document.getElementById("mochaToolbar").classList.remove("invisible");
@@ -1237,7 +1278,7 @@ window.addEventListener("DOMContentLoaded", (event) => {
document.getElementById("showStatusBarLink").addEventListener("click", (e) => {
showStatusBar = !showStatusBar;
- localPreferences.set("show_status_bar", showStatusBar.toString());
+ clientData.set({ show_status_bar: showStatusBar }).catch(console.error);
if (showStatusBar) {
document.getElementById("showStatusBarLink").firstElementChild.style.opacity = "1";
document.getElementById("desktopFooterWrapper").classList.remove("invisible");
@@ -1274,7 +1315,7 @@ window.addEventListener("DOMContentLoaded", (event) => {
document.getElementById("showFiltersSidebarLink").addEventListener("click", (e) => {
showFiltersSidebar = !showFiltersSidebar;
- localPreferences.set("show_filters_sidebar", showFiltersSidebar.toString());
+ clientData.set({ show_filters_sidebar: showFiltersSidebar }).catch(console.error);
if (showFiltersSidebar) {
document.getElementById("showFiltersSidebarLink").firstElementChild.style.opacity = "1";
document.getElementById("filtersColumn").classList.remove("invisible");
@@ -1290,7 +1331,7 @@ window.addEventListener("DOMContentLoaded", (event) => {
document.getElementById("speedInBrowserTitleBarLink").addEventListener("click", (e) => {
speedInTitle = !speedInTitle;
- localPreferences.set("speed_in_browser_title_bar", speedInTitle.toString());
+ clientData.set({ speed_in_browser_title_bar: speedInTitle }).catch(console.error);
if (speedInTitle)
document.getElementById("speedInBrowserTitleBarLink").firstElementChild.style.opacity = "1";
else
@@ -1300,19 +1341,19 @@ window.addEventListener("DOMContentLoaded", (event) => {
document.getElementById("showSearchEngineLink").addEventListener("click", (e) => {
window.qBittorrent.Client.showSearchEngine(!window.qBittorrent.Client.isShowSearchEngine());
- localPreferences.set("show_search_engine", window.qBittorrent.Client.isShowSearchEngine().toString());
+ clientData.set({ show_search_engine: window.qBittorrent.Client.isShowSearchEngine() }).catch(console.error);
updateTabDisplay();
});
document.getElementById("showRssReaderLink").addEventListener("click", (e) => {
window.qBittorrent.Client.showRssReader(!window.qBittorrent.Client.isShowRssReader());
- localPreferences.set("show_rss_reader", window.qBittorrent.Client.isShowRssReader().toString());
+ clientData.set({ show_rss_reader: window.qBittorrent.Client.isShowRssReader() }).catch(console.error);
updateTabDisplay();
});
document.getElementById("showLogViewerLink").addEventListener("click", (e) => {
window.qBittorrent.Client.showLogViewer(!window.qBittorrent.Client.isShowLogViewer());
- localPreferences.set("show_log_viewer", window.qBittorrent.Client.isShowLogViewer().toString());
+ clientData.set({ show_log_viewer: window.qBittorrent.Client.isShowLogViewer() }).catch(console.error);
updateTabDisplay();
});
@@ -1369,7 +1410,7 @@ window.addEventListener("DOMContentLoaded", (event) => {
// main window tabs
const showTransfersTab = () => {
- const showFiltersSidebar = localPreferences.get("show_filters_sidebar", "true") === "true";
+ const showFiltersSidebar = clientData.get("show_filters_sidebar") !== false;
if (showFiltersSidebar) {
document.getElementById("filtersColumn").classList.remove("invisible");
document.getElementById("filtersColumn_handle").classList.remove("invisible");
diff --git a/src/webui/www/private/scripts/color-scheme.js b/src/webui/www/private/scripts/color-scheme.js
index 3e41ddc523fe..15d277a56cf1 100644
--- a/src/webui/www/private/scripts/color-scheme.js
+++ b/src/webui/www/private/scripts/color-scheme.js
@@ -36,12 +36,12 @@ window.qBittorrent.ColorScheme ??= (() => {
};
};
- const localPreferences = new window.qBittorrent.LocalPreferences.LocalPreferences();
const colorSchemeQuery = window.matchMedia("(prefers-color-scheme: dark)");
+ const clientData = window.parent.qBittorrent.ClientData;
const update = () => {
const root = document.documentElement;
- const colorScheme = localPreferences.get("color_scheme");
+ const colorScheme = clientData.get("color_scheme");
const validScheme = (colorScheme === "light") || (colorScheme === "dark");
const isDark = colorSchemeQuery.matches;
root.classList.toggle("dark", ((!validScheme && isDark) || (colorScheme === "dark")));
@@ -52,5 +52,3 @@ window.qBittorrent.ColorScheme ??= (() => {
return exports();
})();
Object.freeze(window.qBittorrent.ColorScheme);
-
-window.qBittorrent.ColorScheme.update();
diff --git a/src/webui/www/private/scripts/dynamicTable.js b/src/webui/www/private/scripts/dynamicTable.js
index 13480ab24cf9..18b173aa6a0e 100644
--- a/src/webui/www/private/scripts/dynamicTable.js
+++ b/src/webui/www/private/scripts/dynamicTable.js
@@ -66,6 +66,7 @@ window.qBittorrent.DynamicTable ??= (() => {
};
const localPreferences = new window.qBittorrent.LocalPreferences.LocalPreferences();
+ const clientData = window.qBittorrent.ClientData ?? window.parent.qBittorrent.ClientData;
class DynamicTable {
#DynamicTableHeaderContextMenuClass = null;
@@ -74,7 +75,7 @@ window.qBittorrent.DynamicTable ??= (() => {
this.dynamicTableDivId = dynamicTableDivId;
this.dynamicTableFixedHeaderDivId = dynamicTableFixedHeaderDivId;
this.dynamicTableDiv = document.getElementById(dynamicTableDivId);
- this.useVirtualList = useVirtualList && (localPreferences.get("use_virtual_list", "false") === "true");
+ this.useVirtualList = useVirtualList && (clientData.get("use_virtual_list") === true);
this.fixedTableHeader = document.querySelector(`#${dynamicTableFixedHeaderDivId} thead tr`);
this.hiddenTableHeader = this.dynamicTableDiv.querySelector("thead tr");
this.table = this.dynamicTableDiv.querySelector("table");
@@ -725,7 +726,7 @@ window.qBittorrent.DynamicTable ??= (() => {
}
setupAltRow() {
- const useAltRowColors = (localPreferences.get("use_alt_row_colors", "true") === "true");
+ const useAltRowColors = clientData.get("use_alt_row_colors") !== false;
if (useAltRowColors)
document.getElementById(this.dynamicTableDivId).classList.add("altRowColors");
}
@@ -1800,7 +1801,7 @@ window.qBittorrent.DynamicTable ??= (() => {
? "dblclick_download"
: "dblclick_complete";
- if (localPreferences.get(prefKey, "1") !== "1")
+ if (clientData.get(prefKey) === "0")
return true;
if (state.includes("stopped"))
diff --git a/src/webui/www/private/scripts/search.js b/src/webui/www/private/scripts/search.js
index 7525e763328e..fb06c4a781a4 100644
--- a/src/webui/www/private/scripts/search.js
+++ b/src/webui/www/private/scripts/search.js
@@ -109,8 +109,7 @@ window.qBittorrent.Search ??= (() => {
});
const init = () => {
- // load "Search in" preference from local storage
- document.getElementById("searchInTorrentName").value = (localPreferences.get("search_in_filter") === "names") ? "names" : "everywhere";
+ document.getElementById("searchInTorrentName").value = (window.qBittorrent.ClientData.get("search_in_filter") === "names") ? "names" : "everywhere";
const searchResultsTableContextMenu = new window.qBittorrent.ContextMenu.ContextMenu({
targets: "#searchResultsTableDiv tbody tr",
menu: "searchResultsTableMenu",
@@ -802,7 +801,8 @@ window.qBittorrent.Search ??= (() => {
};
const searchInTorrentName = () => {
- localPreferences.set("search_in_filter", getSearchInTorrentName());
+ // don't await this
+ window.qBittorrent.ClientData.set({ search_in_filter: getSearchInTorrentName() }).catch(console.error);
searchFilterChanged();
};
diff --git a/src/webui/www/private/views/createtorrent.html b/src/webui/www/private/views/createtorrent.html
index c5f1a3bc4ab7..781205b6566e 100644
--- a/src/webui/www/private/views/createtorrent.html
+++ b/src/webui/www/private/views/createtorrent.html
@@ -127,8 +127,6 @@
};
};
- const localPreferences = new window.qBittorrent.LocalPreferences.LocalPreferences();
-
const formatUrls = (urls) => {
return urls.split("\n").map(encodeURIComponent).join("|");
};
@@ -140,7 +138,7 @@
return option;
};
- const init = () => {
+ const init = async () => {
const pieceSizeSelect = document.getElementById("pieceSize");
pieceSizeSelect.appendChild(createSizeOption(0));
for (let i = 4; i <= 17; ++i)
@@ -165,7 +163,7 @@
submit();
});
- loadPreference();
+ await loadPreference();
window.qBittorrent.pathAutofill.attachPathAutofill();
};
@@ -209,11 +207,13 @@
comments: document.getElementById("comments").value,
source: document.getElementById("source").value,
};
- localPreferences.set("torrent_creator", JSON.stringify(preference));
+ window.parent.qBittorrent.ClientData.set({ torrent_creator: preference }).catch(console.error);
};
- const loadPreference = () => {
- const preference = JSON.parse(localPreferences.get("torrent_creator") ?? "{}");
+ const loadPreference = async () => {
+ const clientData = await window.parent.qBittorrent.ClientData.fetch(["torrent_creator"]);
+ const preference = clientData.torrent_creator ?? {};
+
document.getElementById("sourcePath").value = preference.sourcePath ?? "";
document.getElementById("torrentFormat").value = preference.torrentFormat ?? "hybrid";
document.getElementById("pieceSize").value = preference.pieceSize ?? 0;
diff --git a/src/webui/www/private/views/filters.html b/src/webui/www/private/views/filters.html
index 5c8f0404a0bb..578d66a13fc1 100644
--- a/src/webui/www/private/views/filters.html
+++ b/src/webui/www/private/views/filters.html
@@ -263,7 +263,7 @@
});
document.getElementById("Filters_pad").addEventListener("dblclick", (event) => {
- if (localPreferences.get("dblclick_filter", "1") !== "1")
+ if (window.qBittorrent.ClientData.get("dblclick_filter") === "0")
return;
const filterItem = event.target.closest("li");
diff --git a/src/webui/www/private/views/log.html b/src/webui/www/private/views/log.html
index 2b51b5643f25..e3530469a705 100644
--- a/src/webui/www/private/views/log.html
+++ b/src/webui/www/private/views/log.html
@@ -182,12 +182,10 @@
}
};
- const localPreferences = new window.qBittorrent.LocalPreferences.LocalPreferences();
-
let logFilterTimer = -1;
let inputtedFilterText = "";
let selectBox;
- let selectedLogLevels = JSON.parse(localPreferences.get("qbt_selected_log_levels")) || ["1", "2", "4", "8"];
+ let selectedLogLevels = window.qBittorrent.ClientData.get("qbt_selected_log_levels") ?? ["1", "2", "4", "8"];
const init = () => {
for (const option of document.getElementById("logLevelSelect").options)
@@ -277,7 +275,8 @@
if (selectedLogLevels !== value) {
tableInfo[currentSelectedTab].last_id = -1;
selectedLogLevels = value;
- localPreferences.set("qbt_selected_log_levels", JSON.stringify(selectedLogLevels));
+ // don't await this
+ window.qBittorrent.ClientData.set({ qbt_selected_log_levels: selectedLogLevels }).catch(console.error);
logFilterChanged();
}
};
diff --git a/src/webui/www/private/views/preferences.html b/src/webui/www/private/views/preferences.html
index 7325197cf2e0..2ace34e45c2e 100644
--- a/src/webui/www/private/views/preferences.html
+++ b/src/webui/www/private/views/preferences.html
@@ -1797,8 +1797,6 @@
};
};
- const localPreferences = new window.qBittorrent.LocalPreferences.LocalPreferences();
-
// Behavior tab
const numberInputLimiter = (input) => {
const inputNumber = Number(input.value);
@@ -2217,10 +2215,8 @@
document.getElementById("locale_select").value = selected;
};
- const updateColoSchemeSelect = () => {
+ const updateColoSchemeSelect = (colorScheme) => {
const combobox = document.getElementById("colorSchemeSelect");
- const colorScheme = localPreferences.get("color_scheme");
-
if (colorScheme === "light")
combobox.options[1].selected = true;
else if (colorScheme === "dark")
@@ -2232,20 +2228,31 @@
const loadPreferences = () => {
window.parent.qBittorrent.Cache.preferences.init()
.then((pref) => {
+ const clientData = window.parent.qBittorrent.ClientData;
+ const colorScheme = clientData.get("color_scheme");
+ const fullUrlTrackerColumn = clientData.get("full_url_tracker_column");
+ const useVirtualList = clientData.get("use_virtual_list");
+ const hideZeroStatusFilters = clientData.get("hide_zero_status_filters");
+ const dblclickDownload = clientData.get("dblclick_download");
+ const dblclickComplete = clientData.get("dblclick_complete");
+ const dblclickFilter = clientData.get("dblclick_filter");
+ const useAltRowColors = clientData.get("use_alt_row_colors");
+
// Behavior tab
// Language
updateWebuiLocaleSelect(pref.locale);
- updateColoSchemeSelect();
+ updateColoSchemeSelect(colorScheme);
+
document.getElementById("statusBarExternalIP").checked = pref.status_bar_external_ip;
document.getElementById("performanceWarning").checked = pref.performance_warning;
- document.getElementById("displayFullURLTrackerColumn").checked = (localPreferences.get("full_url_tracker_column", "false") === "true");
- document.getElementById("useVirtualList").checked = (localPreferences.get("use_virtual_list", "false") === "true");
- document.getElementById("hideZeroFiltersCheckbox").checked = (localPreferences.get("hide_zero_status_filters", "false") === "true");
- document.getElementById("dblclickDownloadSelect").value = localPreferences.get("dblclick_download", "1");
- document.getElementById("dblclickCompleteSelect").value = localPreferences.get("dblclick_complete", "1");
- document.getElementById("dblclickFiltersSelect").value = localPreferences.get("dblclick_filter", "1");
+ document.getElementById("displayFullURLTrackerColumn").checked = fullUrlTrackerColumn === true;
+ document.getElementById("useVirtualList").checked = useVirtualList === true;
+ document.getElementById("hideZeroFiltersCheckbox").checked = hideZeroStatusFilters === true;
+ document.getElementById("dblclickDownloadSelect").value = dblclickDownload ?? "1";
+ document.getElementById("dblclickCompleteSelect").value = dblclickComplete ?? "1";
+ document.getElementById("dblclickFiltersSelect").value = dblclickFilter ?? "1";
document.getElementById("confirmTorrentDeletion").checked = pref.confirm_torrent_deletion;
- document.getElementById("useAltRowColorsInput").checked = (localPreferences.get("use_alt_row_colors", "true") === "true");
+ document.getElementById("useAltRowColorsInput").checked = useAltRowColors !== false;
document.getElementById("filelog_checkbox").checked = pref.file_log_enabled;
document.getElementById("filelog_save_path_input").value = pref.file_log_path;
document.getElementById("filelog_backup_checkbox").checked = pref.file_log_backup_enabled;
@@ -2658,8 +2665,9 @@
});
};
- const applyPreferences = () => {
+ const applyPreferences = async () => {
const settings = {};
+ const clientData = {};
// Validate form data
// Behavior tab
@@ -2667,21 +2675,21 @@
settings["locale"] = document.getElementById("locale_select").value;
const colorScheme = Number(document.getElementById("colorSchemeSelect").value);
if (colorScheme === 0)
- localPreferences.remove("color_scheme");
+ clientData.color_scheme = null;
else if (colorScheme === 1)
- localPreferences.set("color_scheme", "light");
+ clientData.color_scheme = "light";
else
- localPreferences.set("color_scheme", "dark");
+ clientData.color_scheme = "dark";
settings["status_bar_external_ip"] = document.getElementById("statusBarExternalIP").checked;
settings["performance_warning"] = document.getElementById("performanceWarning").checked;
- localPreferences.set("full_url_tracker_column", document.getElementById("displayFullURLTrackerColumn").checked.toString());
- localPreferences.set("use_virtual_list", document.getElementById("useVirtualList").checked.toString());
- localPreferences.set("hide_zero_status_filters", document.getElementById("hideZeroFiltersCheckbox").checked.toString());
- localPreferences.set("dblclick_download", document.getElementById("dblclickDownloadSelect").value);
- localPreferences.set("dblclick_complete", document.getElementById("dblclickCompleteSelect").value);
- localPreferences.set("dblclick_filter", document.getElementById("dblclickFiltersSelect").value);
+ clientData.full_url_tracker_column = document.getElementById("displayFullURLTrackerColumn").checked;
+ clientData.use_virtual_list = document.getElementById("useVirtualList").checked;
+ clientData.hide_zero_status_filters = document.getElementById("hideZeroFiltersCheckbox").checked;
+ clientData.dblclick_download = document.getElementById("dblclickDownloadSelect").value;
+ clientData.dblclick_complete = document.getElementById("dblclickCompleteSelect").value;
+ clientData.dblclick_filter = document.getElementById("dblclickFiltersSelect").value;
settings["confirm_torrent_deletion"] = document.getElementById("confirmTorrentDeletion").checked;
- localPreferences.set("use_alt_row_colors", document.getElementById("useAltRowColorsInput").checked.toString());
+ clientData.use_alt_row_colors = document.getElementById("useAltRowColorsInput").checked;
settings["file_log_enabled"] = document.getElementById("filelog_checkbox").checked;
settings["file_log_path"] = document.getElementById("filelog_save_path_input").value;
settings["file_log_backup_enabled"] = document.getElementById("filelog_backup_checkbox").checked;
@@ -3172,16 +3180,19 @@
settings["i2p_inbound_length"] = Number(document.getElementById("i2pInboundLength").value);
settings["i2p_outbound_length"] = Number(document.getElementById("i2pOutboundLength").value);
- // Send it to qBT
- window.parent.qBittorrent.Cache.preferences.set(settings)
- .then(() => {
- // Close window
- window.parent.location.reload();
- window.parent.qBittorrent.Client.closeWindow(document.getElementById("preferencesPage"));
- }).catch((error) => {
- alert("QBT_TR(Unable to save program preferences, qBittorrent is probably unreachable.)QBT_TR[CONTEXT=HttpServer]");
- window.parent.qBittorrent.Client.closeWindow(document.getElementById("preferencesPage"));
- });
+ // Send data to qBT
+ const results = await Promise.allSettled([
+ window.parent.qBittorrent.ClientData.set(clientData),
+ window.parent.qBittorrent.Cache.preferences.set(settings)
+ ]);
+ if (results.every((result) => result.status === "fulfilled")) {
+ window.parent.location.reload();
+ window.parent.qBittorrent.Client.closeWindow(document.getElementById("preferencesPage"));
+ }
+ else {
+ // keep window open so user can reattempt saving
+ alert("QBT_TR(Unable to save preferences, qBittorrent is probably unreachable.)QBT_TR[CONTEXT=HttpServer]");
+ }
};
const setup = () => {
diff --git a/src/webui/www/webui.qrc b/src/webui/www/webui.qrc
index f17da4d50677..cc0af3384aa6 100644
--- a/src/webui/www/webui.qrc
+++ b/src/webui/www/webui.qrc
@@ -390,6 +390,7 @@
private/scripts/addtorrent.js
private/scripts/cache.js
private/scripts/client.js
+ private/scripts/client-data.js
private/scripts/color-scheme.js
private/scripts/contextmenu.js
private/scripts/dynamicTable.js