Add watchlist backup import export

This commit is contained in:
Codex
2026-07-21 10:03:45 +02:00
parent 0e40c9bda9
commit 6ecebcd389
7 changed files with 341 additions and 4 deletions
+59 -1
View File
@@ -624,6 +624,22 @@ CONFIG_HTML = r"""<!doctype html>
<div class="muted" id="watchlistReconcileStatus">No watchlist filesystem sync run yet.</div>
</section>
<section class="settings settings-main">
<div class="toolbar">
<div>
<h2>Watchlist backup</h2>
<p class="muted">Export or restore a complete watchlist database snapshot, including removed-show records.</p>
</div>
<div class="row">
<button id="exportWatchlistBtn" type="button">Export</button>
<button id="importWatchlistBtn" type="button">Import</button>
</div>
</div>
<input id="importWatchlistFile" type="file" accept="application/json,.json" hidden>
<div class="field-hint">Import replaces the current watchlist with the selected backup file.</div>
<div class="muted" id="watchlistBackupStatus">No watchlist backup action run yet.</div>
</section>
<section class="settings settings-main">
<div class="toolbar">
<div>
@@ -719,7 +735,8 @@ CONFIG_HTML = r"""<!doctype html>
},
jellyfinSyncRunning: false,
jellyfinSyncJobId: null,
watchlistReconcileRunning: false
watchlistReconcileRunning: false,
watchlistImportRunning: false
};
const $ = (id) => document.getElementById(id);
@@ -765,6 +782,15 @@ CONFIG_HTML = r"""<!doctype html>
$("watchlistReconcileStatus").textContent = text || "No watchlist filesystem sync run yet.";
}
function setWatchlistBackupStatus(text) {
$("watchlistBackupStatus").textContent = text || "No watchlist backup action run yet.";
}
function setWatchlistImportButton(running) {
$("importWatchlistBtn").disabled = !!running;
$("importWatchlistBtn").textContent = running ? "Importing..." : "Import";
}
function selectedWebhookEvents() {
return [...document.querySelectorAll(".webhook-event:checked")].map((input) => input.value);
}
@@ -906,6 +932,35 @@ CONFIG_HTML = r"""<!doctype html>
}
}
function exportWatchlistBackup() {
setWatchlistBackupStatus("Preparing watchlist backup download...");
window.location.href = "/api/config/watchlist/export";
}
async function importWatchlistBackup(file) {
if (!file) return;
state.watchlistImportRunning = true;
setWatchlistImportButton(true);
setWatchlistBackupStatus("Reading backup file...");
try {
const payload = JSON.parse(await file.text());
setWatchlistBackupStatus("Importing backup...");
const data = await api("/api/config/watchlist/import", {
method: "POST",
body: JSON.stringify(payload)
});
setWatchlistBackupStatus(`${data.imported || 0} watchlist entries imported.`);
setNotice(data.message || "Watchlist backup imported.");
} catch (error) {
setWatchlistBackupStatus("Watchlist backup import failed.");
setNotice(error.message);
} finally {
$("importWatchlistFile").value = "";
state.watchlistImportRunning = false;
setWatchlistImportButton(false);
}
}
function closePathBrowser() {
state.pathBrowser = {
targetId: "",
@@ -1053,6 +1108,9 @@ CONFIG_HTML = r"""<!doctype html>
$("testWebhookBtn").addEventListener("click", testWebhook);
$("runJellyfinSyncBtn").addEventListener("click", runJellyfinSync);
$("reconcileWatchlistDownloadsBtn").addEventListener("click", reconcileWatchlistDownloads);
$("exportWatchlistBtn").addEventListener("click", exportWatchlistBackup);
$("importWatchlistBtn").addEventListener("click", () => $("importWatchlistFile").click());
$("importWatchlistFile").addEventListener("change", (event) => importWatchlistBackup((event.target.files || [])[0]));
$("openChangelogBtn").addEventListener("click", openChangelog);
$("closeChangelogBtn").addEventListener("click", closeChangelog);
$("pathBrowserCloseBtn").addEventListener("click", closePathBrowser);