„Diese Untertitelspur liess sich nicht laden", und auf der zweiten Spur kam gar nichts. Nicht das Format war schuld und auch nicht das Verschieben, sondern die Zeit: jeder Abruf startete ffmpeg, ffmpeg musste die ganze Datei durchlesen, und nach dreissig Sekunden wurde abgebrochen. An einem Prüffilm von fünfzehn Megabyte fällt das nie auf; bei einem Dreistünder auf einem NAS reicht es nie. Schlimmer wurde es durch meine eigene Korrektur von gestern: seit die Adresse den Einstiegspunkt mitführt (`?ab=`), ändert sie sich bei jedem Sprung — der Browser konnte nichts mehr aus seinem Zwischenspeicher nehmen, und jeder Sprung stiess einen neuen Durchlauf an. Jetzt: - Ein ffmpeg-Durchlauf holt alle Textspuren auf einmal. Das Lesen der Datei ist der teure Teil und wird nicht mehr je Spur bezahlt. - Das Ergebnis bleibt liegen, ungeschoben; das Verschieben ist eine Zeilenersetzung und kostet nichts. Gemessen: erster Abruf 0,42 s, jeder weitere 2 bis 3 ms — und der zweite Sprung in derselben Datei trifft dieselbe Ablage statt eines neuen Durchlaufs. - Angestossen wird beim Start des Stroms, nicht erst beim Einschalten. Wer nach einer halben Stunde Untertitel will, wartet dann gar nicht. - Geschrieben wird unter `.teil` und erst danach umbenannt. Eine abgebrochene Datei am richtigen Namen gälte sonst für immer als fertig. - Die Geduld steigt von 30 Sekunden auf 30 Minuten. Sie ist jetzt eine Obergrenze gegen einen hängenden Prozess, keine Wartezeit mehr. Und der Spieler kann endlich unterscheiden, was los ist. Ein `<track>` meldet jeden Misserfolg gleich — „es gibt sie nicht", „sie enthält keinen Text" und „der Server holt sie gerade" sahen identisch aus. Geholt wird deshalb selbst, mit Token statt Keks (gemessen: ohne Keks kam jeder Abruf mit 401 zurück, ohne dass irgendwo etwas stand), und bei 503 fragt der Spieler nach, statt aufzugeben. Der Text geht als Blob an den `<track>` — dieselbe Anzeige, ein ehrlicher Weg dahin. Dazu eine Unsauberkeit im Verschieben: die Trennzeile eines weggefallenen Blocks blieb stehen. Gemessen am echten Film 52 Leerzeilen vor dem ersten Satz. Geprüft im Spieler, kalter Vorrat, Einstieg bei Sekunde 500: Blob am `<track>`, zehn Einblendungen, die erste bei 0 bis 3 s mit „Deutsch bei 500 s". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
145 lines
4.9 KiB
C#
145 lines
4.9 KiB
C#
namespace Flaro.Media.Tests;
|
|
|
|
/// <summary>Untertitel auf der Zeitachse eines Stroms, der mittendrin anfängt.</summary>
|
|
///
|
|
/// <remarks>
|
|
/// <b>Der Fehler, den diese Prüfung festhält.</b> Die Zeitstempel zählen ab
|
|
/// Dateianfang, der Strom zählt ab null. Beim Einstieg bei Minute 30 lag
|
|
/// damit jeder Satz dreissig Minuten in der Zukunft — und weil ein Strom nur
|
|
/// bis zum Filmende reicht, erschien überhaupt keiner mehr. Gemeldet wurde
|
|
/// das als „auf keiner Spur kommt Text".
|
|
/// </remarks>
|
|
public class UntertitelversatzTests
|
|
{
|
|
private const string Probe = """
|
|
WEBVTT
|
|
|
|
1
|
|
00:00:01.000 --> 00:00:04.000
|
|
Ganz am Anfang
|
|
|
|
2
|
|
00:00:09.000 --> 00:00:12.000
|
|
Über den Einstieg hinweg
|
|
|
|
3
|
|
00:00:20.000 --> 00:00:24.000
|
|
Weit dahinter
|
|
|
|
""";
|
|
|
|
[Test]
|
|
public void Ohne_Versatz_bleibt_alles_wie_es_war() =>
|
|
Assert.That(Untertitelversatz.Verschieben(Probe, 0), Is.EqualTo(Probe));
|
|
|
|
[Test]
|
|
public void Die_Zeiten_rutschen_um_den_Einstieg_nach_vorn()
|
|
{
|
|
var heraus = Untertitelversatz.Verschieben(Probe, 10);
|
|
|
|
Assert.That(heraus, Does.Contain("00:00:10.000 --> 00:00:14.000"),
|
|
"Sekunde 20 im Film ist Sekunde 10 in einem Strom, der bei 10 beginnt.");
|
|
}
|
|
|
|
/// <summary>Was schon vorbei war, fällt weg.</summary>
|
|
///
|
|
/// <remarks>
|
|
/// Und zwar samt seinem Text: bliebe der Absatz ohne Zeitzeile stehen,
|
|
/// hinge er als Fliesstext in der Datei und der Browser verschluckt sich
|
|
/// daran.
|
|
/// </remarks>
|
|
[Test]
|
|
public void Was_vor_dem_Einstieg_endet_faellt_weg()
|
|
{
|
|
var heraus = Untertitelversatz.Verschieben(Probe, 10);
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(heraus, Does.Not.Contain("Ganz am Anfang"));
|
|
Assert.That(heraus, Does.Not.Contain("00:00:01.000"));
|
|
});
|
|
}
|
|
|
|
/// <summary>Ein Satz, der beim Einstieg schon lief, bleibt stehen.</summary>
|
|
///
|
|
/// <remarks>
|
|
/// Abgeschnitten ist er immer noch lesbar — ihn wegzuwerfen hiesse, beim
|
|
/// Wiedereinstieg mitten in einem Dialog mit Stille zu beginnen.
|
|
/// </remarks>
|
|
[Test]
|
|
public void Was_ueber_den_Einstieg_hinwegreicht_faengt_bei_null_an()
|
|
{
|
|
var heraus = Untertitelversatz.Verschieben(Probe, 10);
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(heraus, Does.Contain("Über den Einstieg hinweg"));
|
|
Assert.That(heraus, Does.Contain("00:00:00.000 --> 00:00:02.000"));
|
|
});
|
|
}
|
|
|
|
/// <summary>Weggefallene Sätze hinterlassen keine Leerzeilen.</summary>
|
|
///
|
|
/// <remarks>
|
|
/// <b>Gemessen am echten Film.</b> Bei einem Einstieg nach achteinhalb
|
|
/// Minuten standen vor dem ersten Satz 52 leere Zeilen — eine je
|
|
/// weggefallenem Block. Lesbar war das noch, sauber nicht: die Trennzeile
|
|
/// gehört dem Block und fällt mit ihm weg.
|
|
/// </remarks>
|
|
[Test]
|
|
public void Weggefallene_Saetze_hinterlassen_keine_Leerzeilen()
|
|
{
|
|
var heraus = Untertitelversatz.Verschieben(Probe, 10);
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(heraus, Does.StartWith("WEBVTT\n\n2\n00:00:00.000"),
|
|
"nach dem Kopf kommt sofort der erste Satz, der noch gilt");
|
|
Assert.That(heraus, Does.Not.Contain("\n\n\n"),
|
|
"zwei Leerzeilen hintereinander heisst, dass ein Block seine Trennzeile zurückliess");
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public void Der_Kopf_bleibt_stehen() =>
|
|
Assert.That(Untertitelversatz.Verschieben(Probe, 10), Does.StartWith("WEBVTT"));
|
|
|
|
/// <summary>Die kurze Schreibweise ohne Stunden ist erlaubt.</summary>
|
|
[Test]
|
|
public void Auch_ohne_Stundenangabe()
|
|
{
|
|
var kurz = "WEBVTT\n\n01:05.000 --> 01:09.000\nKurz geschrieben\n";
|
|
|
|
Assert.That(Untertitelversatz.Verschieben(kurz, 60),
|
|
Does.Contain("00:00:05.000 --> 00:00:09.000"));
|
|
}
|
|
|
|
/// <summary>Was hinter der Zeitzeile steht, bleibt dran.</summary>
|
|
///
|
|
/// <remarks>
|
|
/// Dort stehen Platzangaben wie <c>line:90% align:center</c>. Sie
|
|
/// abzuschneiden hiesse, erzwungene Untertitel mitten ins Bild zu setzen.
|
|
/// </remarks>
|
|
[Test]
|
|
public void Die_Platzangaben_bleiben_erhalten()
|
|
{
|
|
var mitLage = "WEBVTT\n\n00:00:20.000 --> 00:00:24.000 line:90% align:center\nUnten\n";
|
|
|
|
Assert.That(Untertitelversatz.Verschieben(mitLage, 10),
|
|
Does.Contain("00:00:10.000 --> 00:00:14.000 line:90% align:center"));
|
|
}
|
|
|
|
/// <summary>Eine Spur, die ganz vor dem Einstieg liegt, wird leer — aber gültig.</summary>
|
|
[Test]
|
|
public void Eine_leere_Spur_bleibt_lesbares_WebVTT()
|
|
{
|
|
var heraus = Untertitelversatz.Verschieben(Probe, 3600);
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(heraus, Does.StartWith("WEBVTT"));
|
|
Assert.That(heraus, Does.Not.Contain("-->"));
|
|
});
|
|
}
|
|
}
|