diff --git a/CHANGELOG.md b/CHANGELOG.md index 7771630..b08f709 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # 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 - Fixed watchlist-synced movie downloads so successful movie jobs move existing watchlist entries into `Finished` instead of leaving them stuck in `Watching` or `Planned`. diff --git a/README.md b/README.md index 4a10bcb..7b6f37a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Small local web UI for a system-wide `ani-cli` install. -Current version: `0.45.10` +Current version: `0.45.11` ## Project Layout @@ -123,7 +123,7 @@ Container notes: - Track shows on `/watchlist`. - Refresh watchlist episode counts in the background. - 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 completed downloads. - Optionally move fully completed watchlist libraries into separate Jellyfin TV and movie folders. @@ -214,6 +214,7 @@ Jellyfin handoff: 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. +- 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. - 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. diff --git a/VERSION b/VERSION index 91d6116..a1cf49f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.45.10 +0.45.11 diff --git a/app.py b/app.py index 34e65d9..d345aee 100644 --- a/app.py +++ b/app.py @@ -2411,8 +2411,6 @@ def queue_watchlist_auto_download(item, refresh_source="manual"): return {"queued": False, "reason": "config_disabled"} if normalize_watchlist_category(item.get("category")) != "watching": 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() quality = str(item.get("auto_download_quality") or config.get("auto_download_quality") or "best").strip().lower() diff --git a/test_app.py b/test_app.py index a551e4f..fa63ca3 100644 --- a/test_app.py +++ b/test_app.py @@ -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): item = { "show_id": "show-77",