Zwei Titel je Film, und die Sprache lässt sich wählen

Die Mediathek sah zweisprachig aus — "Despicable Me" neben "Ich einfach
unverbesserlich". Es gab nur ein Titelfeld, und welche Sprache darin
landete, entschied die Einstellung zum Zeitpunkt des Scans.

Movie und Series tragen jetzt zusätzlich OriginalTitle. Gesucht wird über
beide: wer "Despicable Me" tippt, findet "Ich einfach unverbesserlich".
Auf der Detailseite steht der Originaltitel unter dem übersetzten, wenn er
abweicht.

Die Sprache der Metadaten ist in der Verwaltung eine Auswahl statt eines
Textfelds. Vorher musste man "de-DE" wissen, und ein Tippfehler darin nahm
TMDB stillschweigend als Englisch an.

Dazu eine eigene Sprache je Benutzer. Das Ehrliche daran steht im Dialog:
die Daten liegen in einer Sprache vor, und wer eine andere wählt, bekommt,
was übersetzt vorliegt — für den Rest die Serversprache, nicht ein leeres
Feld. ItemTranslation hält die zweite Sprache, gefüllt über eine
Sammelaktion. Nicht nebenbei beim Öffnen: das verteilte die Wartezeit über
Wochen und träfe immer den, der gerade etwas sucht.

Alle Sprachen für alle Titel vorzuhalten wäre der Bestand mal Sprachen an
Abrufen und Speicher, für einen Haushalt mit zweien. Deshalb auf Ansage.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-10-04 12:23:47 +02:00
co-authored by Claude Opus 5
parent f359807561
commit 921533050b
20 changed files with 1533 additions and 26 deletions
+42 -1
View File
@@ -3,7 +3,11 @@
* Eine Datei, weil es eine Schnittstelle ist: wer wissen will, was der Server
* kann, liest hier und nicht in fünf Ansichten nach. */
export type Rolle = { id: string; userName: string; isAdministrator: boolean };
export type Rolle = {
id: string; userName: string; isAdministrator: boolean;
/** Die bevorzugte Sprache — `null` heisst: die des Servers. */
language?: string | null;
};
export type Spur = {
index: number; kind: 'Video' | 'Audio' | 'Subtitle';
codec: string | null; language: string | null; title: string | null;
@@ -39,6 +43,8 @@ export type Film = {
/** Leer in Listen, gefüllt beim Einzelabruf. */
genres: string[] | null;
credits: Mitwirkender[] | null;
/** Der Titel in der Sprache des Originals, wenn er abweicht. */
originalTitle: string | null;
};
export type Serie = {
id: string; title: string; year: number | null; overview: string | null;
@@ -51,6 +57,8 @@ export type Serie = {
contentRating: string | null;
genres: string[] | null;
credits: Mitwirkender[] | null;
/** Der Titel in der Sprache des Originals, wenn er abweicht. */
originalTitle: string | null;
};
export type Folge = {
id: string; season: number; episode: number;
@@ -147,6 +155,9 @@ function merken(): void {
export const angemeldet = (): boolean => Boolean(sitzung.token);
export const istAdmin = (): boolean => Boolean(sitzung.wer?.isAdministrator);
/** Die eingestellte Sprache dieses Benutzers — `null` heisst: die des Servers. */
export const meineSprache = (): string | null => sitzung.wer?.language ?? null;
/* ---------- Anfragen ---------- */
export class ApiFehler extends Error {
@@ -432,6 +443,36 @@ export type Ordnerstand = {
note: string | null;
};
/** Die Sprachen, die zur Auswahl stehen.
*
* **Eine Auswahl statt eines Textfelds.** Vorher musste man „de-DE" wissen —
* und ein Tippfehler darin nahm TMDB stillschweigend als Englisch an. */
export const sprachen: { wert: string; text: string }[] = [
{ wert: 'de-DE', text: 'Deutsch (de-DE)' },
{ wert: 'en-US', text: 'Englisch (en-US)' },
{ wert: 'en-GB', text: 'Englisch, UK (en-GB)' },
{ wert: 'fr-FR', text: 'Französisch (fr-FR)' },
{ wert: 'it-IT', text: 'Italienisch (it-IT)' },
{ wert: 'es-ES', text: 'Spanisch (es-ES)' },
{ wert: 'nl-NL', text: 'Niederländisch (nl-NL)' },
{ wert: 'pl-PL', text: 'Polnisch (pl-PL)' },
{ wert: 'pt-BR', text: 'Portugiesisch, Brasilien (pt-BR)' },
{ wert: 'tr-TR', text: 'Türkisch (tr-TR)' },
];
export type Sprachstand = { language: string | null; translated: number; total: number };
/** Setzt die eigene Sprache — `null` heisst: die des Servers. */
export const spracheSetzen = (language: string | null) =>
anfrage<Sprachstand>('/users/me/language', { method: 'PUT', body: JSON.stringify({ language }) });
export type Uebersetzungsbericht = { filme: number; serien: number; fehlend: number; gesamt: number };
/** Holt die fehlenden Übersetzungen für eine Sprache — kostet je Titel eine Anfrage. */
export const uebersetzungenHolen = (language: string) =>
anfrage<Uebersetzungsbericht>('/system/translations',
{ method: 'POST', body: JSON.stringify({ language }) });
export type Protokolltag = { date: string; bytes: number };
/** Welche Tage protokolliert sind, neueste zuerst. */