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:
2026-09-29 21:21:19 +02:00
parent f6298d29af
commit f3a85916ec
50 changed files with 1475 additions and 94 deletions
+15 -6
View File
@@ -73,7 +73,8 @@ export interface PlexTmdb {
keywords: string[];
crew: PlexTmdbCrew[];
studios: string[];
production_countries: string[];
// ISO code (null on a row enriched before E30 whose name TMDB's list lacked) + TMDB's English name.
production_countries: { code: string | null; name: string }[];
recommendations: { tmdb_id?: number | null; title?: string | null; in_library: boolean }[];
backdrops: string[];
}
@@ -132,6 +133,16 @@ export interface PlexMarker {
start_s: number;
end_s: number;
}
// What the player names an audio/subtitle track from (E30, `lib/vocab` trackLabel). Plex's own title
// (`label`) is in the Plex SERVER's language, so it is only the fallback for a track with no language tag.
export interface PlexTrackNaming {
ord: number;
label: string | null;
lang_tag?: string | null;
forced?: boolean;
hearing_impaired?: boolean;
detail?: string | null; // the codec tail, "AC3 5.1"
}
export interface PlexItemDetail {
id: string;
kind: "movie" | "episode";
@@ -157,15 +168,13 @@ export interface PlexItemDetail {
studio?: string | null;
tagline?: string | null;
markers: PlexMarker[];
audio_streams: { ord: number; label: string; language?: string | null; default: boolean }[];
audio_streams: (PlexTrackNaming & { language?: string | null; default: boolean })[];
// `text`=true → offered as a WebVTT <track>; image subs (PGS/VobSub) are text=false (hidden).
subtitle_streams: {
ord: number;
label: string;
subtitle_streams: (PlexTrackNaming & {
language?: string | null;
codec?: string;
text: boolean;
}[];
})[];
status: string;
position_seconds: number;
show_title?: string | null;