diff --git a/CHANGELOG.md b/CHANGELOG.md index c0cc5d1..1926f26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.50.1 - 2026-08-01 + +- Passed the selected Search result number through queued `ani-cli` jobs as `-S`, so downloads avoid the interactive series picker when multiple similar titles are returned. +- Preserved the selected result index in queued jobs for more reliable watchlist identity sync after ambiguous downloads. + ## 0.50.0 - 2026-08-01 - Added `curl-impersonate` to Docker images using the maintained `lexiforest/curl-impersonate` image so `ani-cli` v5 can use browser-like curl wrappers before falling back to plain `curl`. diff --git a/README.md b/README.md index 77c04ed..a56835f 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,12 @@ Local web UI for a system-wide `ani-cli` install. -Current version: `0.50.0` +Current version: `0.50.1` ## What it does - Search anime in `sub` or `dub`, with poster-style results shown below the selection panel and the first English alternate title when available -- Queue downloads with folder, quality, media type, season, and episode controls +- Queue downloads with folder, quality, media type, season, episode controls, and the selected upstream search result when a Search card is used - Choose which download backends queue jobs may use: `ani-cli`, `anipy-cli`, and `animdl` - Track shows in a watchlist with `Watching`, `Planned`, `Finished`, and `Dropped` categories - Expose a small JSON summary endpoint for Homepage dashboard widgets @@ -120,6 +120,7 @@ Docker notes: - Selecting one method disables fallback retry; selecting multiple methods makes later methods automatic fallbacks - Queue `ani-cli` downloads explicitly run with `ANI_CLI_PLAYER=download` so headless Docker installs do not need `mpv` or `vlc` just to save files - Docker images include `curl-impersonate` wrappers before plain `curl` on `PATH`, matching the preference order used by `ani-cli` v5 +- Queue jobs created from Search pass the selected result number to `ani-cli -S`, avoiding the interactive series picker when the provider returns multiple similar titles - Fallback retries keep the queued episode numbers when fallback downloader filenames are renumbered from `1` - Docker images install `anipy-cli` and `animdl` automatically; non-Docker installs should provide the selected tools in `PATH` themselves diff --git a/VERSION b/VERSION index 564edf8..a3968ef 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.50.0 +0.50.1 diff --git a/app_support.py b/app_support.py index 3107fda..6a01b7d 100644 --- a/app_support.py +++ b/app_support.py @@ -1216,7 +1216,7 @@ def build_job(payload, config): "season": normalize_season(payload.get("season")), "episode_offset": normalize_episode_offset(payload.get("episode_offset")), "query": query, - "result_index": None, + "result_index": normalize_result_index(payload.get("result_index")), "mode": mode, "quality": quality, "episodes": validate_episode_spec(payload.get("episodes")), @@ -1232,6 +1232,18 @@ def build_job(payload, config): } +def normalize_result_index(value): + if value in (None, ""): + return None + try: + index = int(str(value).strip()) + except (TypeError, ValueError): + return None + if index < 1: + return None + return index + + def anipy_search_spec(job): query = re.sub(r"\s+", " ", str(job.get("query") or "").replace(":", " ")).strip() mode = "dub" if str(job.get("mode") or "").strip().lower() == "dub" else "sub" @@ -1276,6 +1288,9 @@ def command_for_job(job, backend="ani-cli", download_path=None): "-e", job["episodes"], ] + result_index = normalize_result_index(job.get("result_index")) + if result_index is not None: + command.extend(["-S", str(result_index)]) if job["mode"] == "dub": command.append("--dub") command.append(job["query"]) diff --git a/search_page.py b/search_page.py index c362c4a..3bc608a 100644 --- a/search_page.py +++ b/search_page.py @@ -688,6 +688,7 @@ INDEX_HTML = r""" const payload = { show_id: state.selected.id, query: state.selected.title, + result_index: state.selected.index, title: state.selected.title, anime_name: $("animeNameInput").value || state.selected.title, media_type: $("trackMediaType").value, diff --git a/test_app.py b/test_app.py index bf5e22d..e3a75c6 100644 --- a/test_app.py +++ b/test_app.py @@ -1943,7 +1943,7 @@ class WatchlistCompletionTests(unittest.TestCase): self.assertNotIn("-S", APP.app_support.command_for_job(job)) - def test_command_for_job_ignores_legacy_result_index(self): + def test_command_for_job_includes_result_switch_for_selected_search_result(self): command = APP.app_support.command_for_job( { "query": "Queue Show", @@ -1954,6 +1954,20 @@ class WatchlistCompletionTests(unittest.TestCase): } ) + self.assertIn("-S", command) + self.assertEqual(command[command.index("-S") + 1], "2") + + def test_command_for_job_ignores_invalid_result_index(self): + command = APP.app_support.command_for_job( + { + "query": "Queue Show", + "quality": "best", + "episodes": "1-10", + "mode": "dub", + "result_index": "nope", + } + ) + self.assertNotIn("-S", command) def test_command_for_job_builds_anipy_fallback_search_spec(self): @@ -4018,9 +4032,9 @@ class TemplateHelperTests(unittest.TestCase): self.assertIn('const progress = `${job.completed || 0}/${job.total || 0} entries`;', APP.QUEUE_HTML) self.assertIn('const moved = `${job.moved || 0} moved`;', APP.QUEUE_HTML) - def test_search_page_queues_selected_title_without_result_index(self): + def test_search_page_queues_selected_title_with_result_index(self): self.assertIn('query: state.selected.title,', APP.INDEX_HTML) - self.assertNotIn('index: state.selected.index,', APP.INDEX_HTML) + self.assertIn('result_index: state.selected.index,', APP.INDEX_HTML) def test_search_page_renders_poster_results_under_selection_panel(self): self.assertIn('class="results-grid" id="results"', APP.INDEX_HTML)