Link watchlist titles to providers

This commit is contained in:
Codex
2026-08-09 14:08:17 +02:00
parent 4eb5252d3a
commit 3078900087
5 changed files with 51 additions and 9 deletions
+5
View File
@@ -1,5 +1,10 @@
# Changelog
## 0.52.5 - 2026-08-09
- Changed Watchlist title links to open the anime on the provider it was added from instead of the retired metadata source.
- Added provider-route coverage for Anikoto, AniNeko, and AnimePahe Watchlist links.
## 0.52.4 - 2026-08-09
- Added provider metadata to Watchlist entries and display a `Source: ...` tag on Watchlist cards.
+1 -1
View File
@@ -13,7 +13,7 @@ Kaizoku is a local web app for searching, tracking, and downloading anime from A
- 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`.
- Save English external subtitles when the provider exposes usable subtitle tracks.
- Manage focused watchlists for `Watching`, `Planned`, `Finished`, and `Dropped`, with provider source tags on each entry.
- Manage focused watchlists for `Watching`, `Planned`, `Finished`, and `Dropped`, with provider source tags and title links back to the original provider page.
- Periodically refresh selected watchlists and auto-download newly available episodes.
- Sync watchlist download flags from the filesystem.
- Hand completed libraries to Jellyfin TV or movie folders.
+1 -1
View File
@@ -1 +1 @@
0.52.4
0.52.5
+23 -7
View File
@@ -37,7 +37,6 @@ from app_support import (
MAX_JSON_BODY_BYTES,
METADATA_API,
METADATA_REFERER,
METADATA_SITE_BASE,
MODE_CHOICES,
QUALITY_CHOICES,
send_discord_webhook_event,
@@ -161,11 +160,28 @@ def search_thumbnail_cache_key(title):
return f"search-{digest}"
def anime_source_url(show_id, title):
normalized_show_id = str(show_id or "").strip()
if not normalized_show_id:
return f"{METADATA_SITE_BASE}/search-anime?tr=sub&cty=ALL&query={quote(str(title or '').strip(), safe='')}"
return f"{METADATA_SITE_BASE}/bangumi/{quote(normalized_show_id, safe='')}"
PROVIDER_SITE_BASES = {
"anikoto": "https://anikototv.to",
"anineko": "https://anineko.to",
"pahe": "https://animepahe.pw",
}
def anime_source_url(show_id, title, provider=None):
parsed_provider, provider_id = provider_bridge.parse_provider_id(show_id, provider or "anikoto")
normalized_provider = normalize_provider(provider, parsed_provider)
base_url = PROVIDER_SITE_BASES[normalized_provider]
normalized_provider_id = str(provider_id or "").strip()
if normalized_provider_id:
if normalized_provider == "pahe":
return f"{base_url}/anime/{quote(normalized_provider_id, safe='')}"
return f"{base_url}/watch/{quote(normalized_provider_id, safe='')}"
query = quote(str(title or "").strip(), safe="")
if normalized_provider == "anineko":
return f"{base_url}/browser?keyword={query}"
if normalized_provider == "pahe":
return f"{base_url}/api?m=search&q={query}"
return f"{base_url}/search?keyword={query}"
def infer_image_extension(url, content_type):
@@ -1705,7 +1721,7 @@ class WatchlistStore:
item["finished_badge"] = (
"downloaded" if item["category"] == "finished" and item["downloaded"] else "manual" if item["category"] == "finished" else ""
)
item["source_url"] = anime_source_url(item.get("show_id"), item.get("title"))
item["source_url"] = anime_source_url(item.get("show_id"), item.get("title"), item.get("provider"))
thumb_path = thumbnail_file_path(item.get("thumbnail_path"))
item["thumbnail_ready"] = bool(thumb_path and thumb_path.exists())
item["thumbnail_url"] = cover_route(item.get("show_id"))
+21
View File
@@ -1642,6 +1642,27 @@ class WatchlistCompletionTests(unittest.TestCase):
self.assertEqual(item["provider"], "pahe")
self.assertEqual(item["provider_label"], "AnimePahe")
def test_watchlist_source_url_uses_anikoto_show_page(self):
self.seed_watchlist_item(show_id="anikoto:provider-show", title="Provider Show", provider="anikoto")
item = APP.WATCHLIST.get("anikoto:provider-show")
self.assertEqual(item["source_url"], "https://anikototv.to/watch/provider-show")
def test_watchlist_source_url_uses_anineko_show_page(self):
self.seed_watchlist_item(show_id="anineko:provider-show", title="Provider Show", provider="anineko")
item = APP.WATCHLIST.get("anineko:provider-show")
self.assertEqual(item["source_url"], "https://anineko.to/watch/provider-show")
def test_watchlist_source_url_uses_animepahe_show_page(self):
self.seed_watchlist_item(show_id="pahe:provider-show", title="Provider Show", provider="pahe")
item = APP.WATCHLIST.get("pahe:provider-show")
self.assertEqual(item["source_url"], "https://animepahe.pw/anime/provider-show")
def test_refresh_persists_expected_episode_total_and_ready_flags(self):
self.seed_watchlist_item()