Clarify queue cleanup and detached sync logs
This commit is contained in:
@@ -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.
|
||||||
|
|||||||
@@ -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
@@ -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"
|
||||||
|
|||||||
+4
-1
@@ -1163,7 +1163,10 @@ 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:
|
||||||
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:
|
else:
|
||||||
job["status"] = "failed"
|
job["status"] = "failed"
|
||||||
job.setdefault("log", []).append(f"Download failed with exit code {exit_code}.")
|
job.setdefault("log", []).append(f"Download failed with exit code {exit_code}.")
|
||||||
|
|||||||
+1
-1
@@ -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
@@ -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):
|
||||||
|
|||||||
Reference in New Issue
Block a user