From f79cbd9b7dd7bbc809595bbc0c0d5ac6d2e49a5e Mon Sep 17 00:00:00 2001 From: Codex Date: Sun, 9 Aug 2026 12:08:10 +0200 Subject: [PATCH] Add search language availability badges --- CHANGELOG.md | 5 +++++ README.md | 2 +- VERSION | 2 +- app.py | 44 +++++++++++++++++++++++++++++++++++++++++++- search_page.py | 21 +++++++++++++-------- 5 files changed, 63 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4837aa5..4837bee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.51.2 - 2026-08-09 + +- Changed Search to inspect both subbed and dubbed episode availability for each result. +- Added `SUB` and `DUB` language badges to Search result cards when those episode versions are present. + ## 0.51.1 - 2026-08-09 - Removed the manual add form from the Watchlist page so entries are added through Search. diff --git a/README.md b/README.md index 839edf3..55c073d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Kaizoku is a local web app for searching, tracking, and downloading anime from A ## Features -- Search anime through the configured provider: `anikoto`, `anineko`, or `pahe`. +- Search anime through the configured provider and tag results with available `SUB` and `DUB` episode languages. - Queue single episodes or batches in subbed or dubbed mode. - Fall back across the other configured providers when an episode or stream cannot be resolved on the primary provider. - Save files with Jellyfin-friendly layout: `TV/Series Name/Season 01/Series Name - S01E01.mp4`. diff --git a/VERSION b/VERSION index 1e0c609..969eb25 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.51.1 +0.51.2 diff --git a/app.py b/app.py index 9c0276a..05ab2f8 100644 --- a/app.py +++ b/app.py @@ -879,6 +879,43 @@ def cache_thumbnail_image(show_id, image_url): return final_path.name +def episode_language_summary(show_id, provider): + summary = { + "sub": False, + "dub": False, + "sub_count": 0, + "dub_count": 0, + "total": 0, + } + try: + rows = provider_bridge.episodes(show_id, provider=provider) + except Exception as exc: + debug_log("search.episode_language_summary_failed", show_id=show_id, provider=provider, error=exc) + return summary + sub_values = set() + dub_values = set() + all_values = set() + for row in rows: + value = str(row.get("number") or "").strip() + if value: + all_values.add(value) + langs = {str(lang or "").strip().lower() for lang in row.get("langs") or []} + if not langs: + langs.add("sub") + if "hsub" in langs: + langs.add("sub") + if "sub" in langs and value: + sub_values.add(value) + if "dub" in langs and value: + dub_values.add(value) + summary["sub"] = bool(sub_values) + summary["dub"] = bool(dub_values) + summary["sub_count"] = len(sub_values) + summary["dub_count"] = len(dub_values) + summary["total"] = len(all_values) + return summary + + def search_anime(query, mode): runtime = ensure_runtime() provider = str((runtime.get("config") or {}).get("provider") or "anikoto").strip().lower() @@ -890,13 +927,18 @@ def search_anime(query, mode): show_id = str(show.get("id") or "").strip() if not show_id or not title: continue + language_summary = episode_language_summary(show_id, show.get("provider") or provider) alternative_titles = extract_anidb_alternative_titles_for_title(title, primary_title=title) index = len(results) + 1 results.append( { "id": show_id, "title": title, - "episodes": "", + "episodes": language_summary["total"], + "has_sub": language_summary["sub"], + "has_dub": language_summary["dub"], + "sub_episodes": language_summary["sub_count"], + "dub_episodes": language_summary["dub_count"], "index": index, "query": query, "mode": mode, diff --git a/search_page.py b/search_page.py index 31b51c5..92a9d81 100644 --- a/search_page.py +++ b/search_page.py @@ -423,7 +423,7 @@ INDEX_HTML = r""" - +