diff --git a/CHANGELOG.md b/CHANGELOG.md index 820b35d..6ffd8c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # 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 - 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. diff --git a/README.md b/README.md index 230ad95..7d0766d 100644 --- a/README.md +++ b/README.md @@ -163,7 +163,7 @@ Use it to: 1. Monitor active, pending, failed, and finished download jobs. 2. Inspect recent per-job logs with newest lines first. 3. Retry failed or canceled jobs. -4. Remove finished jobs. +4. Remove finished and canceled jobs. 5. Cancel running or pending jobs. Highlights: diff --git a/VERSION b/VERSION index 9d7e3b3..631073f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.45.8 +0.45.9 diff --git a/app_support.py b/app_support.py index 4b6dc9b..a4d91ca 100644 --- a/app_support.py +++ b/app_support.py @@ -20,7 +20,7 @@ from uuid import uuid4 PROJECT_ROOT = Path(__file__).resolve().parent ANI_CLI = os.environ.get("ANI_CLI_BIN") or shutil.which("ani-cli") or "ani-cli" APP_NAME = "ani-cli-web" -VERSION = "0.45.8" +VERSION = "0.45.9" ALLANIME_BASE = "allanime.day" ALLANIME_API = f"https://api.{ALLANIME_BASE}" ALLANIME_REFERER = "https://allmanga.to" diff --git a/queue_jobs.py b/queue_jobs.py index 2e96fca..21fb18c 100644 --- a/queue_jobs.py +++ b/queue_jobs.py @@ -1163,7 +1163,10 @@ class DownloadQueue: if watchlist_sync_error: job.setdefault("log", []).append(f"Watchlist sync failed: {watchlist_sync_error}") elif self.watchlist_sync_fn is not None: - job.setdefault("log", []).append("Watchlist download state synced.") + 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.") else: job["status"] = "failed" job.setdefault("log", []).append(f"Download failed with exit code {exit_code}.") diff --git a/queue_page.py b/queue_page.py index 83c0631..36c0129 100644 --- a/queue_page.py +++ b/queue_page.py @@ -547,7 +547,7 @@ QUEUE_HTML = r""" } } - $("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")); (async function init() { diff --git a/test_app.py b/test_app.py index ab78898..b062e29 100644 --- a/test_app.py +++ b/test_app.py @@ -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]["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): def test_refresh_all_iterates_only_watching_and_planned_show_ids(self):