Untertitel vorher wählen, wie den Ton auch
Auf der Detailseite stand „Ton: Deutsch" und liess sich vor dem Start umstellen — aus gutem Grund: im Spieler umzuschalten heisst, den Strom neu aufzubauen. Für Untertitel gab es das nicht, obwohl dort dieselbe Überlegung gilt und die Spuren ohnehin schon dastehen. Jetzt steht „Untertitel: Keine" daneben. Auch bei nur einer Spur: anders als beim Ton gibt es hier immer mindestens zwei Möglichkeiten — diese eine, oder gar keine. Der Wunsch wird gemerkt wie beim Ton, mit einem dritten Zustand. „Keine" ist eine Antwort und kein Fehlen einer: wer sie abgewählt hat, will sie beim nächsten Film auch nicht, während „nie etwas gesagt" offen lässt, ob eine erzwungene Spur erscheinen darf. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ab43656f71
commit
2c51f639be
+25
-1
@@ -138,6 +138,26 @@ export function tonspracheMerken(sprache: string): void {
|
|||||||
try { localStorage.setItem(SPRACHSCHLUESSEL, sprache); } catch { /* Privates Fenster. */ }
|
try { localStorage.setItem(SPRACHSCHLUESSEL, sprache); } catch { /* Privates Fenster. */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Dasselbe für den Untertitel — mit einem dritten Zustand.
|
||||||
|
*
|
||||||
|
* **„Keine" ist eine Antwort, nicht das Fehlen einer.** Wer Untertitel
|
||||||
|
* abgewählt hat, will sie beim nächsten Film auch nicht; ein leerer Eintrag
|
||||||
|
* hiesse dagegen „noch nie etwas gesagt", und dann darf eine erzwungene
|
||||||
|
* Spur durchaus erscheinen. */
|
||||||
|
const UNTERTITELSCHLUESSEL = 'flaro.untertitelsprache';
|
||||||
|
|
||||||
|
/** Der gemerkte Wunsch: eine Sprache, `'keine'` oder `null` für „nie gesagt". */
|
||||||
|
export function untertitelsprache(): string | null {
|
||||||
|
try { return localStorage.getItem(UNTERTITELSCHLUESSEL); } catch { return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function untertitelspracheMerken(wunsch: string): void {
|
||||||
|
try { localStorage.setItem(UNTERTITELSCHLUESSEL, wunsch); } catch { /* Privates Fenster. */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Was „keine Untertitel" im Speicher heisst. */
|
||||||
|
export const OHNE_UNTERTITEL = 'keine';
|
||||||
|
|
||||||
/** „Ohne Versatz", „Ton 200 ms später", „Ton 1 s früher". */
|
/** „Ohne Versatz", „Ton 200 ms später", „Ton 1 s früher". */
|
||||||
function versatzWort(ms: number): string {
|
function versatzWort(ms: number): string {
|
||||||
if (ms === 0) return 'Ohne Versatz';
|
if (ms === 0) return 'Ohne Versatz';
|
||||||
@@ -166,6 +186,8 @@ export type Spieleranfrage = {
|
|||||||
laenge: number | null;
|
laenge: number | null;
|
||||||
/** Mit welcher Tonspur gestartet wird — gewählt auf der Detailseite. */
|
/** Mit welcher Tonspur gestartet wird — gewählt auf der Detailseite. */
|
||||||
tonspur?: number | null;
|
tonspur?: number | null;
|
||||||
|
/** Mit welchem Untertitel gestartet wird. `null` heisst: keiner. */
|
||||||
|
untertitelspur?: number | null;
|
||||||
danach: () => void;
|
danach: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -213,7 +235,9 @@ export function spieler(anfrage: Spieleranfrage): void {
|
|||||||
/** Die gewählte Stufe und Spuren — sie überleben einen Neustart des Stroms. */
|
/** Die gewählte Stufe und Spuren — sie überleben einen Neustart des Stroms. */
|
||||||
let stufe: number | null = null;
|
let stufe: number | null = null;
|
||||||
let tonspur: number | null = anfrage.tonspur ?? null;
|
let tonspur: number | null = anfrage.tonspur ?? null;
|
||||||
let untertitel: number | null = null;
|
// **Die Wahl von der Detailseite gilt ab dem ersten Bild.** Sie erst im
|
||||||
|
// Spieler zu treffen hiesse, die ersten Sätze ohne Untertitel zu sehen.
|
||||||
|
let untertitel: number | null = anfrage.untertitelspur ?? null;
|
||||||
|
|
||||||
/** Ob der Server umkodieren soll, egal was das Profil sagt.
|
/** Ob der Server umkodieren soll, egal was das Profil sagt.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ import {
|
|||||||
import { icons, icon } from '../icons';
|
import { icons, icon } from '../icons';
|
||||||
import { kachel, alsKachel, reihe } from '../card';
|
import { kachel, alsKachel, reihe } from '../card';
|
||||||
import { vergessen } from '../ansichten';
|
import { vergessen } from '../ansichten';
|
||||||
import { spieler, tonsprache, tonspracheMerken } from '../spieler';
|
import {
|
||||||
|
spieler, tonsprache, tonspracheMerken,
|
||||||
|
untertitelsprache, untertitelspracheMerken, OHNE_UNTERTITEL,
|
||||||
|
} from '../spieler';
|
||||||
import { menueAnZiel } from '../menue';
|
import { menueAnZiel } from '../menue';
|
||||||
import { meldedialog } from '../melden';
|
import { meldedialog } from '../melden';
|
||||||
|
|
||||||
@@ -409,6 +412,9 @@ export function detailseite(art: Art, id: string, zurueck: () => void): HTMLElem
|
|||||||
const tonwahl = tonspurwahl(datei);
|
const tonwahl = tonspurwahl(datei);
|
||||||
if (tonwahl) reihe.appendChild(tonwahl);
|
if (tonwahl) reihe.appendChild(tonwahl);
|
||||||
|
|
||||||
|
const textwahl = untertitelwahl(datei);
|
||||||
|
if (textwahl) reihe.appendChild(textwahl);
|
||||||
|
|
||||||
// **Kein „Herunterladen" im Browser.**
|
// **Kein „Herunterladen" im Browser.**
|
||||||
//
|
//
|
||||||
// Hier stand ein Knopf, der die Datei als MP4 herausgab. Das ist etwas
|
// Hier stand ein Knopf, der die Datei als MP4 herausgab. Das ist etwas
|
||||||
@@ -518,6 +524,65 @@ export function detailseite(art: Art, id: string, zurueck: () => void): HTMLElem
|
|||||||
return k;
|
return k;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Welcher Untertitel beim Start laufen soll — `null` heisst: keiner. */
|
||||||
|
let gewaehlterText: number | null = null;
|
||||||
|
/** Ob oben schon eine Wahl getroffen wurde. Sonst gilt der gemerkte Wunsch. */
|
||||||
|
let textGewaehlt = false;
|
||||||
|
|
||||||
|
/** Dasselbe für den Untertitel — mit „Keine" als erster Wahl.
|
||||||
|
*
|
||||||
|
* **Auch bei nur einer Spur.** Anders als beim Ton gibt es hier immer
|
||||||
|
* mindestens zwei Möglichkeiten: diese eine, oder gar keine. Ein Knopf,
|
||||||
|
* der nur „Deutsch" bestätigt, wäre beim Ton überflüssig — hier ist er
|
||||||
|
* der einzige Weg, die Untertitel vorher abzuschalten. */
|
||||||
|
function untertitelwahl(datei: Datei): HTMLElement | null {
|
||||||
|
const spuren = datei.streams.filter(s => s.kind === 'Subtitle');
|
||||||
|
if (spuren.length === 0) return null;
|
||||||
|
|
||||||
|
if (!textGewaehlt) {
|
||||||
|
const wunsch = untertitelsprache();
|
||||||
|
gewaehlterText = wunsch === null || wunsch === OHNE_UNTERTITEL
|
||||||
|
? null
|
||||||
|
: spuren.find(s => s.language === wunsch)?.index ?? null;
|
||||||
|
textGewaehlt = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = (s: Spur): string =>
|
||||||
|
s.languageName ?? s.display?.split(' · ')[0] ?? s.language ?? `Spur ${s.index}`;
|
||||||
|
|
||||||
|
const k = knopf('', 'btn');
|
||||||
|
const zeichnen = (): void => {
|
||||||
|
const jetzt = spuren.find(s => s.index === gewaehlterText);
|
||||||
|
k.textContent = `Untertitel: ${jetzt ? name(jetzt) : 'Keine'}`;
|
||||||
|
k.prepend(icon('untertitel'));
|
||||||
|
};
|
||||||
|
zeichnen();
|
||||||
|
|
||||||
|
k.onclick = () => menueAnZiel(k, [
|
||||||
|
{
|
||||||
|
text: 'Keine',
|
||||||
|
icon: gewaehlterText === null ? ('haken' as const) : undefined,
|
||||||
|
tun: () => {
|
||||||
|
gewaehlterText = null;
|
||||||
|
untertitelspracheMerken(OHNE_UNTERTITEL);
|
||||||
|
zeichnen();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...spuren.map(s => ({
|
||||||
|
text: name(s),
|
||||||
|
icon: s.index === gewaehlterText ? ('haken' as const) : undefined,
|
||||||
|
tun: () => {
|
||||||
|
gewaehlterText = s.index;
|
||||||
|
// Wer hier wechselt, meint es auch beim nächsten Film so.
|
||||||
|
if (s.language) untertitelspracheMerken(s.language);
|
||||||
|
zeichnen();
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
function rundKnopf(
|
function rundKnopf(
|
||||||
bild: keyof typeof icons, an: boolean, titel: string, tun: () => Promise<void>,
|
bild: keyof typeof icons, an: boolean, titel: string, tun: () => Promise<void>,
|
||||||
): HTMLButtonElement {
|
): HTMLButtonElement {
|
||||||
@@ -874,6 +939,7 @@ export function detailseite(art: Art, id: string, zurueck: () => void): HTMLElem
|
|||||||
ab,
|
ab,
|
||||||
laenge: datei.durationSeconds,
|
laenge: datei.durationSeconds,
|
||||||
tonspur: gewaehlteSpur,
|
tonspur: gewaehlteSpur,
|
||||||
|
untertitelspur: gewaehlterText,
|
||||||
danach: laden,
|
danach: laden,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user