Link watchlist titles to providers
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user