Clarify queue cleanup and detached sync logs

This commit is contained in:
Dymas
2026-05-26 07:20:54 +02:00
parent f5c476162e
commit 28410b2946
7 changed files with 42 additions and 5 deletions
+5
View File
@@ -1,5 +1,10 @@
# Changelog # Changelog
## 0.45.9 - 2026-05-26
- Clarified queue cleanup wording so the UI and README now match the real `Clear finished` behavior, which removes both finished and canceled jobs.
- Fixed successful detached queue jobs to log that watchlist sync was skipped instead of incorrectly claiming that watchlist download state synced.
## 0.45.8 - 2026-05-26 ## 0.45.8 - 2026-05-26
- Made watchlist removal sticky by recording removed show IDs, detaching related queue jobs from watchlist sync, and preventing later completed downloads from silently recreating deleted entries. - Made watchlist removal sticky by recording removed show IDs, detaching related queue jobs from watchlist sync, and preventing later completed downloads from silently recreating deleted entries.
+1 -1
View File
@@ -163,7 +163,7 @@ Use it to:
1. Monitor active, pending, failed, and finished download jobs. 1. Monitor active, pending, failed, and finished download jobs.
2. Inspect recent per-job logs with newest lines first. 2. Inspect recent per-job logs with newest lines first.
3. Retry failed or canceled jobs. 3. Retry failed or canceled jobs.
4. Remove finished jobs. 4. Remove finished and canceled jobs.
5. Cancel running or pending jobs. 5. Cancel running or pending jobs.
Highlights: Highlights:
+1 -1
View File
@@ -1 +1 @@
0.45.8 0.45.9
+1 -1
View File
@@ -20,7 +20,7 @@ from uuid import uuid4
PROJECT_ROOT = Path(__file__).resolve().parent PROJECT_ROOT = Path(__file__).resolve().parent
ANI_CLI = os.environ.get("ANI_CLI_BIN") or shutil.which("ani-cli") or "ani-cli" ANI_CLI = os.environ.get("ANI_CLI_BIN") or shutil.which("ani-cli") or "ani-cli"
APP_NAME = "ani-cli-web" APP_NAME = "ani-cli-web"
VERSION = "0.45.8" VERSION = "0.45.9"
ALLANIME_BASE = "allanime.day" ALLANIME_BASE = "allanime.day"
ALLANIME_API = f"https://api.{ALLANIME_BASE}" ALLANIME_API = f"https://api.{ALLANIME_BASE}"
ALLANIME_REFERER = "https://allmanga.to" ALLANIME_REFERER = "https://allmanga.to"
+3
View File
@@ -1163,6 +1163,9 @@ class DownloadQueue:
if watchlist_sync_error: if watchlist_sync_error:
job.setdefault("log", []).append(f"Watchlist sync failed: {watchlist_sync_error}") job.setdefault("log", []).append(f"Watchlist sync failed: {watchlist_sync_error}")
elif self.watchlist_sync_fn is not None: elif self.watchlist_sync_fn is not None:
if job.get("skip_watchlist_sync"):
job.setdefault("log", []).append("Watchlist sync skipped for detached job.")
else:
job.setdefault("log", []).append("Watchlist download state synced.") job.setdefault("log", []).append("Watchlist download state synced.")
else: else:
job["status"] = "failed" job["status"] = "failed"
+1 -1
View File
@@ -547,7 +547,7 @@ QUEUE_HTML = r"""<!doctype html>
} }
} }
$("removeFinishedBtn").addEventListener("click", () => queueBulkAction("/api/queue/clear-finished", "Removed finished jobs")); $("removeFinishedBtn").addEventListener("click", () => queueBulkAction("/api/queue/clear-finished", "Removed finished and canceled jobs"));
$("retryFailedBtn").addEventListener("click", () => queueBulkAction("/api/queue/retry-failed", "Retried failed jobs")); $("retryFailedBtn").addEventListener("click", () => queueBulkAction("/api/queue/retry-failed", "Retried failed jobs"));
(async function init() { (async function init() {
+29
View File
@@ -622,6 +622,35 @@ class DownloadQueueWorkerFailureTests(unittest.TestCase):
self.assertEqual(send_webhook.call_args.args[2]["category"], "finished") self.assertEqual(send_webhook.call_args.args[2]["category"], "finished")
self.assertEqual(send_webhook.call_args.args[2]["moved_files"], ["/tmp/example/Queue Show - S01E01.mp4"]) self.assertEqual(send_webhook.call_args.args[2]["moved_files"], ["/tmp/example/Queue Show - S01E01.mp4"])
def test_successful_detached_download_logs_sync_skipped(self):
queue = APP.DownloadQueue(
lambda: {
"mode": "sub",
"quality": "best",
"download_dir": "/tmp/example",
},
watchlist_sync_fn=lambda _job: None,
start_worker=False,
)
job = queue.add({"query": "Queue Show", "title": "Queue Show", "season": "1", "episodes": "1"})
job["skip_watchlist_sync"] = True
class FakeProc:
pid = 4321
stdout = []
def wait(self):
return 0
with mock.patch.object(queue_jobs.subprocess, "Popen", return_value=FakeProc()), mock.patch.object(
queue_jobs, "finalize_library_files", return_value=["/tmp/example/Queue Show - S01E01.mp4"]
):
queue._run_job(job)
stored = queue._find(job["id"])
self.assertIn("Watchlist sync skipped for detached job.", stored["log"])
self.assertNotIn("Watchlist download state synced.", stored["log"])
class WatchlistRefreshAllTests(unittest.TestCase): class WatchlistRefreshAllTests(unittest.TestCase):
def test_refresh_all_iterates_only_watching_and_planned_show_ids(self): def test_refresh_all_iterates_only_watching_and_planned_show_ids(self):