feat(i18n): the server's closed English vocabularies named in the reader's language (E30 S2)
- lib/vocab + the `vocab` locale area: TMDB crew jobs and departments, auto-tags (D1 A: the data
stays English, the catalog is named at its one source, useTags), built-in download profiles,
YouTube topics; held key-for-key to the backend's own lists by backendIds.contract.
- Countries by ISO code: TMDB parse stores {code, name}; rows stored before are coded from TMDB's
own country list in one request (run_enrich); Intl names them, historic codes (YU, SU, XI…)
from the catalog.
- Player tracks named from the language tag, forced/SDH flags and codec tail, not Plex's
server-language displayTitle; no invented "Audio N" on the server.
- Download lifecycle notices (shared / expiring / expired / evicted) named from data; the share
notice carries the title.
- Scheduler: the job result rides along as data and is named per key (dashboard + run notice).
- Invite status named; a render crash's notice no longer shows the engine's English message.
- Fix: YouTube links …/wiki/Humour, the tagger looked for "Humor" — Comedy never came from a topic.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
"""Plex show/item detail: the show drill-down + the per-item player payload (markers, cast, nav)."""
|
||||
|
||||
import re
|
||||
import threading
|
||||
import time
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
@@ -342,6 +343,26 @@ def _episode_nav(db: Session, it: PlexItem) -> tuple[str | None, str | None]:
|
||||
return (keys[i - 1] if i > 0 else None), (keys[i + 1] if i < len(keys) - 1 else None)
|
||||
|
||||
|
||||
# The technical tail of Plex's track title — "English (AC3 5.1)" → "AC3 5.1".
|
||||
_TRACK_TAIL = re.compile(r"\(([^()]+)\)\s*$")
|
||||
|
||||
|
||||
def _track_naming(s: dict) -> dict:
|
||||
"""What the player names a track from (E30). Plex's `displayTitle` is in the SERVER's language
|
||||
("English (AC3 5.1)", "magyar Forced", "中文 (EAC3 Stereo)"), so it is sent only as the fallback
|
||||
`label`; the client builds the name in the reader's language from the language tag, the
|
||||
forced / SDH flags and the codec tail. No title and no language → no label (the client numbers it)."""
|
||||
title = s.get("displayTitle") or s.get("language") or None
|
||||
tail = _TRACK_TAIL.search(title) if title else None
|
||||
return {
|
||||
"label": title,
|
||||
"lang_tag": s.get("languageTag") or None,
|
||||
"forced": bool(s.get("forced")),
|
||||
"hearing_impaired": bool(s.get("hearingImpaired")),
|
||||
"detail": tail.group(1).strip() if tail else None,
|
||||
}
|
||||
|
||||
|
||||
@router.get("/item/{rating_key}")
|
||||
def item_detail(
|
||||
rating_key: str,
|
||||
@@ -387,9 +408,7 @@ def item_detail(
|
||||
audio_streams.append(
|
||||
{
|
||||
"ord": len(audio_streams),
|
||||
"label": s.get("displayTitle")
|
||||
or s.get("language")
|
||||
or f"Audio {len(audio_streams) + 1}",
|
||||
**_track_naming(s),
|
||||
"language": s.get("languageTag") or s.get("language"),
|
||||
"default": bool(s.get("selected") or s.get("default")),
|
||||
}
|
||||
@@ -399,9 +418,7 @@ def item_detail(
|
||||
subtitle_streams.append(
|
||||
{
|
||||
"ord": len(subtitle_streams),
|
||||
"label": s.get("displayTitle")
|
||||
or s.get("language")
|
||||
or f"Subtitle {len(subtitle_streams) + 1}",
|
||||
**_track_naming(s),
|
||||
"language": s.get("languageTag") or s.get("language"),
|
||||
"codec": codec,
|
||||
# Text subs (srt/ass/…) can be served as WebVTT tracks; image subs (PGS/VobSub)
|
||||
|
||||
Reference in New Issue
Block a user