Release v1.0.12: fetch English TheTVDB episode titles for matching.
Anime and other non-English shows returned Japanese default titles while filenames use English; add Episode titles language control defaulting to English. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
+82
-51
@@ -24,6 +24,12 @@ SEASON_TYPE_CHOICES: list[tuple[str, str]] = [
|
||||
("Regional order", "regional"),
|
||||
]
|
||||
|
||||
# Language path segment for /series/{id}/episodes/{season-type}/{language}
|
||||
LANGUAGE_CHOICES: list[tuple[str, str]] = [
|
||||
("Default (show language)", ""),
|
||||
("English", "eng"),
|
||||
]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TvdbSeries:
|
||||
@@ -154,37 +160,73 @@ class TvdbClient:
|
||||
)
|
||||
return results
|
||||
|
||||
def get_season_episodes(
|
||||
def _episodes_path(self, series_id: int, season_type: str, language: str = "") -> str:
|
||||
base = f"/series/{series_id}/episodes/{season_type}"
|
||||
if language:
|
||||
return f"{base}/{language}"
|
||||
return base
|
||||
|
||||
def _parse_episode_batch(
|
||||
self,
|
||||
series_id: int,
|
||||
season: int,
|
||||
season_type: str = "default",
|
||||
batch: list[dict[str, Any]],
|
||||
*,
|
||||
season: Optional[int] = None,
|
||||
skip_specials: bool = False,
|
||||
) -> list[TvdbEpisode]:
|
||||
episodes: list[TvdbEpisode] = []
|
||||
for ep in batch:
|
||||
ep_season = ep.get("seasonNumber")
|
||||
number = ep.get("number")
|
||||
name = ep.get("name")
|
||||
if number is None or ep_season is None or not name:
|
||||
continue
|
||||
ep_season = int(ep_season)
|
||||
if skip_specials and ep_season == 0:
|
||||
continue
|
||||
if season is not None and ep_season != season:
|
||||
continue
|
||||
episodes.append(
|
||||
TvdbEpisode(
|
||||
number=int(number),
|
||||
season_number=ep_season,
|
||||
name=str(name),
|
||||
)
|
||||
)
|
||||
return episodes
|
||||
|
||||
def _fetch_episodes(
|
||||
self,
|
||||
series_id: int,
|
||||
season_type: str,
|
||||
*,
|
||||
season: Optional[int] = None,
|
||||
language: str = "",
|
||||
skip_specials: bool = False,
|
||||
) -> list[TvdbEpisode]:
|
||||
"""Fetch episodes; optional translated titles via language code (e.g. eng)."""
|
||||
episodes: list[TvdbEpisode] = []
|
||||
page = 0
|
||||
use_api_season = season is not None and not language
|
||||
while True:
|
||||
params: dict[str, Any] = {"page": page}
|
||||
if use_api_season:
|
||||
params["season"] = season
|
||||
payload = self._request(
|
||||
"GET",
|
||||
f"/series/{series_id}/episodes/{season_type}",
|
||||
params={"page": page, "season": season},
|
||||
self._episodes_path(series_id, season_type, language),
|
||||
params=params,
|
||||
)
|
||||
batch = (payload.get("data") or {}).get("episodes") or []
|
||||
if not batch:
|
||||
break
|
||||
for ep in batch:
|
||||
if ep.get("seasonNumber") != season:
|
||||
continue
|
||||
number = ep.get("number")
|
||||
name = ep.get("name")
|
||||
if number is None or not name:
|
||||
continue
|
||||
episodes.append(
|
||||
TvdbEpisode(
|
||||
number=int(number),
|
||||
season_number=int(season),
|
||||
name=str(name),
|
||||
)
|
||||
filter_season = season if language else None
|
||||
episodes.extend(
|
||||
self._parse_episode_batch(
|
||||
batch,
|
||||
season=filter_season,
|
||||
skip_specials=skip_specials,
|
||||
)
|
||||
)
|
||||
links = payload.get("links") or {}
|
||||
if not links.get("next"):
|
||||
break
|
||||
@@ -192,41 +234,30 @@ class TvdbClient:
|
||||
episodes.sort(key=lambda e: (e.season_number, e.number))
|
||||
return episodes
|
||||
|
||||
def get_season_episodes(
|
||||
self,
|
||||
series_id: int,
|
||||
season: int,
|
||||
season_type: str = "default",
|
||||
language: str = "",
|
||||
) -> list[TvdbEpisode]:
|
||||
return self._fetch_episodes(
|
||||
series_id,
|
||||
season_type,
|
||||
season=season,
|
||||
language=language,
|
||||
)
|
||||
|
||||
def get_all_episodes(
|
||||
self,
|
||||
series_id: int,
|
||||
season_type: str = "default",
|
||||
language: str = "",
|
||||
) -> list[TvdbEpisode]:
|
||||
"""Fetch every episode for a series (all seasons), paginated."""
|
||||
episodes: list[TvdbEpisode] = []
|
||||
page = 0
|
||||
while True:
|
||||
payload = self._request(
|
||||
"GET",
|
||||
f"/series/{series_id}/episodes/{season_type}",
|
||||
params={"page": page},
|
||||
)
|
||||
batch = (payload.get("data") or {}).get("episodes") or []
|
||||
if not batch:
|
||||
break
|
||||
for ep in batch:
|
||||
number = ep.get("number")
|
||||
season = ep.get("seasonNumber")
|
||||
name = ep.get("name")
|
||||
if number is None or season is None or not name:
|
||||
continue
|
||||
if int(season) == 0:
|
||||
continue
|
||||
episodes.append(
|
||||
TvdbEpisode(
|
||||
number=int(number),
|
||||
season_number=int(season),
|
||||
name=str(name),
|
||||
)
|
||||
)
|
||||
links = payload.get("links") or {}
|
||||
if not links.get("next"):
|
||||
break
|
||||
page += 1
|
||||
episodes.sort(key=lambda e: (e.season_number, e.number))
|
||||
return episodes
|
||||
return self._fetch_episodes(
|
||||
series_id,
|
||||
season_type,
|
||||
language=language,
|
||||
skip_specials=True,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user