Zwei Uhren, und niemand hatte sie verglichen

Untertitel zeigten auf keiner Spur Text. Der Grund: die Zeitstempel einer
WebVTT-Spur zählen ab Dateianfang, der Strom zählt ab null. Wer bei Minute
30 einstieg, bekam jeden Satz dreissig Minuten in die Zukunft geschoben —
und weil ein Strom nur bis zum Filmende reicht, erschien überhaupt keiner
mehr.

Gemessen im Spieler, vorher: „Fortsetzen ab 8:37" geklickt, deutsche Spur
gewählt, Standanzeige 8:38 — angezeigt wurde der Satz von Sekunde 0.
Versatz 515 Sekunden, also genau der Einstiegspunkt.

Nachher, derselbe Weg: Standanzeige 9:02, angezeigt „Deutsch bei 540 s".

Verschoben wird auf dem Server, nicht im Browser: dort hiesse es, die Spur
selbst zu zerlegen und als VTTCue neu aufzubauen — ein eigener Parser für
ein Format, das der Browser schon kann. Hier ist es eine Zeilenersetzung,
und sie lässt sich prüfen. Was vor dem Einstieg endet, fällt samt Text weg;
was darüber hinwegreicht, fängt bei null an.

Zwei Kleinigkeiten lagen daneben und sind mit erledigt:

- `srclang` bekam „ger".slice(0, 2) und damit „ge" — kein gültiges Kürzel.
  Jetzt bleibt das Feld leer, wenn das Kürzel nicht zweistellig ist.
- Scheiterte das Laden einer Spur, geschah nichts Sichtbares. Jetzt sagt
  es der Spieler.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-10-07 21:28:39 +02:00
co-authored by Claude Opus 5
parent 57fff63516
commit 51d1db4641
5 changed files with 275 additions and 9 deletions
+10 -2
View File
@@ -661,8 +661,16 @@ export const stromStarten = (datei: string, wunsch: Stromwunsch) =>
export const stromBeenden = (sitzung: string) =>
anfrage<void>(`/stream/${sitzung}`, { method: 'DELETE' });
export const untertitelAdresse = (datei: string, spur: number) =>
`/api/v1/files/${datei}/subtitles/${spur}.vtt`;
/** Die Untertitelspur als WebVTT, passend zur Zeitachse des Stroms.
*
* **`vorlauf` ist nicht geschmückt, sondern nötig.** Die Zeitstempel in der
* Datei zählen ab Filmanfang, der Strom zählt ab null. Ohne diesen Wert lägen
* beim Einstieg in Minute 30 alle Einblendungen dreissig Minuten in der
* Zukunft — und weil der Strom nur bis zum Filmende reicht, käme kein
* einziger Satz mehr. */
export const untertitelAdresse = (datei: string, spur: number, vorlauf = 0) =>
`/api/v1/files/${datei}/subtitles/${spur}.vtt`
+ (vorlauf > 0 ? `?ab=${vorlauf.toFixed(3)}` : '');
/* ---------- Verwaltung ---------- */
+17 -3
View File
@@ -3,7 +3,7 @@ import {
stromStarten, stromBeenden, untertitelAdresse, fortschritt,
type Stromauskunft, type Spurauskunft,
} from './api';
import { dauer, knopf, meldung } from './util';
import { dauer, knopf, meldung, hinweisZeigen } from './util';
import { icons, icon } from './icons';
/* ---------- Was der Browser kann ---------- */
@@ -745,9 +745,23 @@ export function spieler(anfrage: Spieleranfrage): void {
const t = document.createElement('track');
t.kind = 'subtitles';
t.label = s.label;
if (s.language) t.srclang = s.language.slice(0, 2);
t.src = untertitelAdresse(anfrage.dateiId, s.index);
// **Nicht abschneiden.** `s.language` ist ein Dreibuchstabencode;
// „ger".slice(0, 2) ergibt „ge", und das ist keine Sprache — Deutsch
// wäre „de". Lieber keine Angabe als eine falsche: die Beschriftung
// steht ohnehin in `label`.
if (s.language?.length === 2) t.srclang = s.language;
// Mit dem Vorlauf: der Strom beginnt bei null, die Datei nicht.
t.src = untertitelAdresse(anfrage.dateiId, s.index, vorlauf);
t.dataset.spur = String(s.index);
// **Ein gescheiterter Abruf war bisher vollkommen stumm.** Der Haken
// stand im Menü, als liefe es — und niemand konnte unterscheiden, ob
// die Spur leer ist oder der Abruf schiefging.
t.onerror = () => {
if (untertitel === s.index) hinweisZeigen('Diese Untertitelspur liess sich nicht laden.', 'fehler');
};
video.appendChild(t);
});
if (untertitel !== null) untertitelWaehlen(untertitel);