Fix first refresh watchlist auto-download backlog
This commit is contained in:
@@ -1,5 +1,10 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.45.11 - 2026-05-26
|
||||||
|
|
||||||
|
- Fixed watchlist auto-download queueing so the first manual or scheduled refresh after adding a `Watching` series can pick up already-available missing episodes instead of waiting for a later refresh cycle.
|
||||||
|
- Kept add-time `initial` watchlist refreshes non-downloading, so tracking a show still seeds metadata first and only later refreshes are allowed to enqueue backlog or newly released episodes.
|
||||||
|
|
||||||
## 0.45.10 - 2026-05-26
|
## 0.45.10 - 2026-05-26
|
||||||
|
|
||||||
- Fixed watchlist-synced movie downloads so successful movie jobs move existing watchlist entries into `Finished` instead of leaving them stuck in `Watching` or `Planned`.
|
- Fixed watchlist-synced movie downloads so successful movie jobs move existing watchlist entries into `Finished` instead of leaving them stuck in `Watching` or `Planned`.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Small local web UI for a system-wide `ani-cli` install.
|
Small local web UI for a system-wide `ani-cli` install.
|
||||||
|
|
||||||
Current version: `0.45.10`
|
Current version: `0.45.11`
|
||||||
|
|
||||||
## Project Layout
|
## Project Layout
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ Container notes:
|
|||||||
- Track shows on `/watchlist`.
|
- Track shows on `/watchlist`.
|
||||||
- Refresh watchlist episode counts in the background.
|
- Refresh watchlist episode counts in the background.
|
||||||
- Schedule periodic watchlist refresh jobs.
|
- Schedule periodic watchlist refresh jobs.
|
||||||
- Auto-download newly discovered episodes for `Watching` entries after later refreshes.
|
- Auto-download newly discovered episodes for `Watching` entries after later refreshes, including backlog episodes already available on the first non-initial refresh.
|
||||||
- Send Discord webhook notifications for refresh discoveries and failures.
|
- Send Discord webhook notifications for refresh discoveries and failures.
|
||||||
- Send Discord webhook notifications for completed downloads.
|
- Send Discord webhook notifications for completed downloads.
|
||||||
- Optionally move fully completed watchlist libraries into separate Jellyfin TV and movie folders.
|
- Optionally move fully completed watchlist libraries into separate Jellyfin TV and movie folders.
|
||||||
@@ -214,6 +214,7 @@ Jellyfin handoff:
|
|||||||
Watchlist queueing:
|
Watchlist queueing:
|
||||||
|
|
||||||
- Manual `Download all` and automatic watchlist downloads now queue directly from the stored watchlist title and `show_id`, so they no longer fail just because a fresh provider search returns a shifted or incomplete result list.
|
- Manual `Download all` and automatic watchlist downloads now queue directly from the stored watchlist title and `show_id`, so they no longer fail just because a fresh provider search returns a shifted or incomplete result list.
|
||||||
|
- The first manual or scheduled refresh after adding a `Watching` show can now queue already-available missing episodes even if the seeded add-time refresh has not populated `last_checked` yet.
|
||||||
- Removing a watchlist entry now detaches its existing queue jobs from later watchlist sync, so finishing an old queued download cannot silently recreate that deleted show.
|
- Removing a watchlist entry now detaches its existing queue jobs from later watchlist sync, so finishing an old queued download cannot silently recreate that deleted show.
|
||||||
- Manual watchlist `Download all` now uses the anime's own configured auto-download quality when one is set.
|
- Manual watchlist `Download all` now uses the anime's own configured auto-download quality when one is set.
|
||||||
- Completed movie downloads now keep their `movie` media type during watchlist sync and move movie-tagged watchlist entries into `Finished`, which keeps later Jellyfin handoff eligibility consistent.
|
- Completed movie downloads now keep their `movie` media type during watchlist sync and move movie-tagged watchlist entries into `Finished`, which keeps later Jellyfin handoff eligibility consistent.
|
||||||
|
|||||||
@@ -2411,8 +2411,6 @@ def queue_watchlist_auto_download(item, refresh_source="manual"):
|
|||||||
return {"queued": False, "reason": "config_disabled"}
|
return {"queued": False, "reason": "config_disabled"}
|
||||||
if normalize_watchlist_category(item.get("category")) != "watching":
|
if normalize_watchlist_category(item.get("category")) != "watching":
|
||||||
return {"queued": False, "reason": "category"}
|
return {"queued": False, "reason": "category"}
|
||||||
if not item.get("previous_last_checked"):
|
|
||||||
return {"queued": False, "reason": "initial_refresh"}
|
|
||||||
|
|
||||||
mode = str(item.get("auto_download_mode") or config.get("auto_download_mode") or "dub").strip().lower()
|
mode = str(item.get("auto_download_mode") or config.get("auto_download_mode") or "dub").strip().lower()
|
||||||
quality = str(item.get("auto_download_quality") or config.get("auto_download_quality") or "best").strip().lower()
|
quality = str(item.get("auto_download_quality") or config.get("auto_download_quality") or "best").strip().lower()
|
||||||
|
|||||||
+54
@@ -2422,6 +2422,60 @@ class AutoDownloadQueueTests(unittest.TestCase):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_queue_watchlist_auto_download_can_queue_backlog_on_first_manual_refresh(self):
|
||||||
|
item = {
|
||||||
|
"show_id": "show-first-refresh",
|
||||||
|
"title": "First Refresh Show",
|
||||||
|
"category": "watching",
|
||||||
|
"previous_last_checked": None,
|
||||||
|
"had_new_episodes": False,
|
||||||
|
"dub_episode_values": ["1", "2", "3"],
|
||||||
|
"sub_episode_values": [],
|
||||||
|
"auto_download_mode": "dub",
|
||||||
|
"auto_download_quality": "best",
|
||||||
|
"auto_download_name": "First Refresh Show",
|
||||||
|
"auto_download_series": "1",
|
||||||
|
"auto_download_offset": None,
|
||||||
|
"downloaded_dub_episodes": [],
|
||||||
|
"downloaded_sub_episodes": [],
|
||||||
|
}
|
||||||
|
download_queue = mock.Mock()
|
||||||
|
download_queue.covered_episodes.return_value = set()
|
||||||
|
download_queue.add.return_value = {"id": "job-first-refresh", "episodes": "1-3"}
|
||||||
|
runtime = {
|
||||||
|
"config": {
|
||||||
|
"auto_download_enabled": True,
|
||||||
|
"auto_download_mode": "dub",
|
||||||
|
"auto_download_quality": "best",
|
||||||
|
"download_dir": "/tmp/downloads",
|
||||||
|
},
|
||||||
|
"download_queue": download_queue,
|
||||||
|
}
|
||||||
|
with mock.patch.object(APP, "ensure_runtime", return_value=runtime), mock.patch.object(
|
||||||
|
APP,
|
||||||
|
"search_anime",
|
||||||
|
side_effect=AssertionError("search should not be required"),
|
||||||
|
):
|
||||||
|
result = APP.queue_watchlist_auto_download(item, refresh_source="manual")
|
||||||
|
|
||||||
|
self.assertTrue(result["queued"])
|
||||||
|
self.assertEqual(result["episodes"], ["1-3"])
|
||||||
|
download_queue.add.assert_called_once_with(
|
||||||
|
{
|
||||||
|
"show_id": "show-first-refresh",
|
||||||
|
"query": "First Refresh Show",
|
||||||
|
"title": "First Refresh Show",
|
||||||
|
"anime_name": "First Refresh Show",
|
||||||
|
"media_type": "tv",
|
||||||
|
"season": "1",
|
||||||
|
"episode_offset": None,
|
||||||
|
"mode": "dub",
|
||||||
|
"quality": "best",
|
||||||
|
"episodes": "1-3",
|
||||||
|
"download_dir": "/tmp/downloads",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
def test_queue_watchlist_auto_download_passes_episode_offset_to_jobs(self):
|
def test_queue_watchlist_auto_download_passes_episode_offset_to_jobs(self):
|
||||||
item = {
|
item = {
|
||||||
"show_id": "show-77",
|
"show_id": "show-77",
|
||||||
|
|||||||
Reference in New Issue
Block a user