Genau dazwischen fehlte die Unterscheidung. Ein Film, bei dem die Suche
einmal nichts fand, trägt einen Vermerk und keine Marke — und war damit
für jeden weiteren Lauf erledigt, obwohl er der interessanteste Fall ist.
Es gab nur alles oder nichts, und der Knopf bei der Mediathek schickte
immer „alles": 453 Filme mal zwanzig Minuten Schlüsselbilder, auch für
die vierhundert, bei denen längst etwas stand.
Aus `bool erneut` wird `Markenumfang` mit drei Werten. `Neue` ist, was
der Scan tut, und ändert sich nicht. `Luecken` nimmt sich vor, wo keine
Marke steht — gleich ob dort schon einmal gesucht wurde. `Alles` ist der
alte Holzhammer.
Bei der Mediathek jetzt zwei Knöpfe, weil es zwei Fragen sind: „Fehlende
Marken suchen" ist die alltägliche, „Alles neu durchsuchen" die seltene
und teure, und deren Rückfrage nennt die Zahl der Titel. Auf der
Detailseite bleibt es bei einem Knopf mit „alles" — wer ihn bei genau
diesem Titel drückt, ist mit genau diesem Titel unzufrieden.
Bei einer Serie wird eine Staffel ganz gelesen, sobald einer ihrer
Folgen etwas fehlt. Billiger ginge es nur, indem man die Lücke gar nicht
erst angeht, und genau dafür drückt jemand den Knopf.
Vier Prüfungen am Endpunkt, die messen, ob der Lauf den Film anfasst:
`Neue` überspringt den Durchsuchten, `Luecken` nimmt ihn sich vor,
`Alles` sowieso, und ein Film mit Marke bleibt bei `Luecken` in Ruhe.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`log.LogInformation("… {Folgen} … {Folgen}.", a, b, c, d, e)` — sechs
Platzhalter, fünf Argumente. Vorlagen sind positionsbezogen: auch ein
wiederholter Name verbraucht ein weiteres Argument. Der Logger warf eine
Ausnahme, die riss `StaffelMeldenAsync` mit, und die wiederum den ganzen
Serienlauf. Gefunden war dann alles umsonst — „A Knight of the Seven
Kingdoms" und „Alice in Borderland" haben ihre Marken deshalb verloren,
nachdem sie längst berechnet waren.
**CA2017 hatte das gemeldet.** Als Warnung, unter hundert anderen Zeilen,
und ich habe beim Bauen nach „warning CS" gegriffen. Eine neue
`Directory.Build.props` macht genau diese eine Regel zum Fehler — nicht
alle Warnungen, das wäre eine andere Entscheidung. Gegengeprüft: mit der
kaputten Zeile bricht der Bau jetzt ab.
**Und der zweite Fund, aus „Alice im Wunderland".** Die Zeile meldete
„Schwelle 247,6", und das kann bei 0–255 nicht entstehen: die Schwelle
ist Grundton mal 0,55, das wären 450 gewesen. `signalstats` rechnet in
der Skala des Bildformats — bei acht Bit 0–255, bei zehn Bit 0–1023.
Gemessen am selben erzeugten Bild: YAVG 127 gegen YAVG 500. Alle
Schwellen hier sind an acht Bit gemessen; bei einer 10-Bit-Datei bedeutete
dieselbe Zahl ein Viertel.
`format=gray` vor `signalstats` bringt jede Datei auf dieselbe Skala. Es
rechnet dabei auch den Fernsehbereich 16–235 auf 0–255 um — linear, und
die dunklen Werte trifft das am stärksten: der gemessene Abspann liegt
jetzt bei 28 statt bei 40. Der Boden wandert entsprechend von 35 auf 22.
Dazu: die Schlusszeile je Serie trennt Vorspann und Abspann, statt eine
Zahl aus beidem zu machen — „14 Marken" beantwortet nicht, ob die Serie
ihre Vorspänne bekommen hat.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
„Markensuche für ‚A Knight of the Seven Kingdoms' abgebrochen — An error
occurred while saving the entity changes." Das war meiner, und er hat
mehr gekostet als eine Fehlermeldung: der Lauf brach beim Speichern ab,
und damit war alles weg, was er für diese Serie gefunden hatte.
`VermerkenAsync` sah in der Datenbank nach, ob die Staffel schon
vermerkt ist — und eine Abfrage sieht nicht, was derselbe Lauf eben erst
hinzugefügt und noch nicht gespeichert hat. Seit der Bildweg dazukam,
wird für dieselbe Staffel zweimal vermerkt: einmal nach dem Tonabgleich,
einmal vor dem Bild. Die zweite Abfrage fand nichts, legte eine zweite
Zeile an, und der eindeutige Schlüssel über (Serie, Staffel, Art) warf
sie beim Speichern wieder hinaus. Jetzt wird erst im Änderungsspeicher
nachgesehen, dann in der Datenbank.
**Und die Meldung taugte nichts.** Das Tagebuch hängt nur `Message` an,
und EF Core schreibt dort „See the inner exception for details." — eine
Zeile, die ausdrücklich sagt, dass sie die Auskunft nicht enthält. Alle
vier Abbruchmeldungen nennen jetzt den innersten Grund über
`Fehlertext.FuerMenschen`.
**Dazu der zweite Fund aus demselben Protokoll.** „(T)Raumschiff
Surprise": kein Abspann, 1318 Proben, Schwelle 51,1, letzte Probe 17 —
das Ende ist also dunkel, und trotzdem kein Fund. 1318 Proben auf zwanzig
Minuten heissen eine alle 0,9 Sekunden, und die Nachsicht gegenüber
hellen Stellen mitten im Abspann war in *Proben* angegeben: zwei Stück,
dort also 1,8 Sekunden. Bei einer Datei mit einem Schlüsselbild alle zehn
Sekunden waren dieselben zwei Proben zwanzig Sekunden. Dasselbe
Zugeständnis, zehnfach verschieden — genau der Fehler, den ich eine Zeile
höher bei der Schlusstafel schon behoben hatte. `Nachsicht` zählt jetzt
Sekunden, zehn davon.
**Und die Suche sagt, woran es lag.** „kein Abspann — das Ende ist hell"
gegen „die dunkle Strecke ist mit 20 s zu kurz" sind zwei verschiedene
Ursachen mit zwei verschiedenen Antworten; bisher stand dort beides Mal
dasselbe.
Vier neue Prüfungen, darunter dieselbe Szene bei 0,9 und bei 10 Sekunden
Abtastung. Mit der alten Probenzählung ist die dichte rot.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Markensuche lief stundenlang über 95 Serien, 4213 Folgen und 453
Filme und schrieb dabei fünf Zeilen, in denen Kennungen standen statt
Namen. „Markenerkennung für 4f3a…: 12 Marken." beantwortet keine der
Fragen, die in den letzten Tagen tatsächlich aufkamen — welche Staffel
nichts bekommen hat, warum, und ob die Serie überhaupt dran war.
Jetzt nennt jede Zeile den Titel, und es gibt eine je Serie, je Staffel
und je Folge:
„Biohackers": 2 Staffel(n), 18 Folge(n).
„Biohackers", Staffel 2: Vorspann in 8 von 8 Folge(n), Abspann in 1 von 8.
Biohackers S02E01 · Neuanfang: Vorspann 0:31–1:24, Abspann ab 41:12 (über das Bild).
Biohackers S02E07 · Letzte Fahrt: Vorspann 0:31–1:24, kein Abspann.
„Biohackers" fertig: 14 Marke(n).
Die Folgenzeilen werden **aus der Datenbank gelesen, nicht mitgezählt**:
so steht dort, was danach wirklich an der Folge hängt, und nicht, was
dieser eine Lauf geschrieben hat. Nur das beantwortet „warum kommt bei
dieser Folge kein Knopf?".
**Drei Gründe statt eines `false`.** `FilmAsync` gab für „keine Datei",
„schon durchsucht" und „nichts gefunden" dasselbe zurück, und von aussen
waren sie nicht zu unterscheiden. Jetzt sagt jeder seinen Namen.
**Und eine Live-Zeile bei der Mediathek**, wie der Scan sie hat. Sie
benutzt denselben Mechanismus — `Scanschritt` mit einer neuen Phase,
derselbe Endpunkt, ein eigenes Feld. Ein eigenes, weil der Scan sich auf
„fertig" setzt, sobald er die Markensuche angestossen hat; in sein Feld
geschrieben sähe er wieder laufend aus.
**Zwei Fehler sind dabei herausgefallen.** In `NachScanAsync` stand
`if (serien.Count == 0) return;` vor dem Filmteil — eine reine
Filmmediathek bekam nach einem Scan also nie eine Abspannsuche. Und die
fpcalc-Prüfung stand ganz oben und galt für beides, obwohl der Bildweg
fpcalc gar nicht braucht: ohne fpcalc fand der Server keinen einzigen
Filmabspann.
Dazu ein Suchfeld im Protokoll und tausend Anfangszeilen statt
dreihundert. Ohne das wäre eine Zeile je Folge keine Auskunft, sondern
eine Mauer — genau der Fall, gegen den sich `ScanCoordinator` für den
Scan schon einmal entschieden hat.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Abspannsuche lief gegen eine feste Schwelle von 35. Die stammte aus
einem erzeugten Prüfbild mit 0,8 Prozent weisser Fläche — Helligkeit 18,
weit unter 35, alles schien zu stimmen. Ein echter Abspann hat mehr
Schrift.
Nachgemessen an 1080p mit zwei Spalten Namen, durch dieselbe ffmpeg-Kette
wie im Betrieb:
Handlung 126
Abspann, realistische Schriftdichte 32
Abspann, dicht gesetzt 47
Die Fünfunddreissig lag mitten zwischen den beiden Abspannfällen und
schnitt sie auseinander. Dasselbe noch einmal an der erzeugten Prüfdatei,
nachdem ihr Abspann realistische Dichte bekam: 40,2 — und die Prüfung,
die vorher grün war, wurde rot. Das ist der Fehler, lokal reproduziert.
**Dunkel ist keine absolute Grösse.** Entscheidend ist nicht, wie dunkel
der Abspann ist, sondern dass er viel dunkler ist als das, was davor
lief. Gemessen wird jetzt gegen den Grundton des Films — das obere
Viertel der Proben, mal 0,55. Die alte Schwelle bleibt als Boden, damit
ein durchweg dämmriger Film nicht in Fehlalarme rutscht.
**Und die Logotafel.** Die Suche verlangte, dass die allerletzte Probe
dunkel ist. Hinter dem Abspann steht bei vielen Produktionen noch eine
Tafel — Sender, Studio, Förderer —, oft hell. Ein einziges weisses
Schlussbild machte damit den ganzen Abspann unsichtbar. Zwanzig Sekunden
sind jetzt erlaubt, in Sekunden gezählt und nicht in Proben: wie viele
Messpunkte auf eine Tafel fallen, hängt allein an der Schlüsselbilddichte
der Datei.
**Ein Lauf ohne Fund sagt jetzt, warum.** Anzahl Proben, Schwelle,
letzte Probe — ohne diese drei Zahlen ist „kein Abspann erkannt" keine
Auskunft, und man rät wieder.
Sieben neue Prüfungen mit den gemessenen Werten. Die beiden
entscheidenden sind mit der alten Schwelle rot; gegengeprüft, nicht
behauptet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nein, die Erkennung sieht sich nicht die ganze Serie an — sie gruppiert
nach Staffel, vermerkt je Staffel, und der eindeutige Schlüssel führt die
Staffel mit. Andere Besetzung, anderer Abspann, andere Musik in Staffel 2
stören den Abgleich also nicht. Der Verdacht war berechtigt, die Stelle
eine andere.
Gefunden hat ihn die Frage trotzdem. Ganz oben in der Staffelschleife
stand `if (geordnet.Count < 2) continue;` — richtig für den Tonabgleich,
der zwei Folgen zum Vergleichen braucht. Seit es den Bildweg gibt, ist es
zu früh: der braucht keine zweite Folge, nur eine Datei. Eine laufende
Serie, von der erst die Premiere der neuen Staffel da ist, bekam deshalb
gar nichts — weder über den Ton, was stimmt, noch über das Bild, was
hätte gehen müssen.
Jetzt läuft der Tonabgleich nur, wenn es etwas zu vergleichen gibt, und
der Bildweg in jedem Fall. Mit Vermerk, sonst liefe er bei jedem Scan
von Neuem.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Es gab ihn: auf der Serienseite, das Uhrsymbol. Nur gilt er für genau
eine Serie — und nach jeder Änderung an der Erkennung will man ohnehin
alles noch einmal. Der Weg dorthin fehlte da, wo man ihn sucht: bei der
Mediathek, neben „Scannen" und „Metadaten auffrischen".
Der Lauf selbst war schon da. `NachScanAsync` macht genau das, Serien
über den Tonabgleich und Filme über das Bild — er hing bloss am Scan und
liess sich nicht von Hand anstossen. Jetzt nimmt er ein „erneut" entgegen
und ist über einen Endpunkt erreichbar. Die Antwort kommt sofort: über
einen grossen Bestand dauert das Stunden, und eine Anfrage so lange offen
zu halten hiesse, sie in ein Zeitlimit laufen zu lassen. Wie weit es ist,
steht im Protokoll von heute.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Und genau daran fiel er durch. Der Tonabgleich lebt davon, dass der
Abspann in jeder Folge derselbe ist; beim Finale ist er es oft nicht —
anderer Schluss, andere Musik, kein Treffer. In jeder Staffel fehlte
deshalb genau eine Marke, immer die letzte, und wer dort weitersah, sah
keinen Knopf. Das war der ganze Fehler. Der Spieler war nie schuld, und
meine Schranken-Vermutung von eben war falsch.
Was der Ton nicht findet, sucht jetzt das Bild — derselbe Weg wie bei
einem Film: dunkel werden und bis zum Ende durchhalten. Das Verfahren
lag fertig in `FilmAsync` und war nur dort eingebaut; es steht jetzt für
sich und wird von beiden benutzt. Nur für die Lücken: bei einer Staffel
ohne fehlende Marke läuft der Abtaster gar nicht.
**Drei Punkte heissen „mehr davon", nicht „tu etwas".** Bei einer Folge
taten sie etwas — wegräumen aus „Fortsetzen", ohne Rückfrage. Jeder, der
sie sah, hat ein Menü erwartet, draufgetippt, und dann baute die Seite
sich neu auf und die Punkte waren weg. Jetzt ein Kreuz, und eine Zeile,
die sagt, was geschehen ist.
Und die Markenanzeige nennt die Zeiten, nicht nur die Namen: dass eine
Marke da ist, sagt noch nicht, dass sie an der richtigen Stelle steht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
**Stream, nicht Strom.** Mein Fehler, und die Regel kenne ich: Fachbegriffe
bleiben englisch. Betroffen war eine Meldung im Spieler und vier Zeilen im
Tagesprotokoll.
**Der Abspann fiel durch die eigene Schranke.** `Hoechstlaenge` von fünf
Minuten galt für Vorspann und Abspann gleichermassen. Fünf Minuten sind
für einen Abspann aber keine Obergrenze, sondern die Regel — eine deutsche
Serienproduktion listet Sprecher, Synchronregie und Studio hinterher, und
sechs Minuten kommen vor. Der Tonabgleich fand das Stück, und die Zeile
darunter warf es weg. Von aussen sah das aus wie „nichts gefunden".
Der Abspann darf jetzt so lang sein wie sein Suchfenster; weiter reicht
eine Marke ohnehin nie.
**Vermutung, nicht Messung** — das Stück steht auf einem Server, an den ich
nicht komme. Deshalb dasselbe Commit:
**Man kann jetzt nachsehen.** Neben jeder Folge steht für Verwalter, was
die Erkennung gefunden hat: „Vorspann + Abspann", „Vorspann" oder „keine
Marken". Ob eine Marke fehlt oder bloss nicht angezeigt wird, war von
aussen nicht zu unterscheiden — und beides hat eine andere Ursache. Die
Angabe kostet nichts: sie steht längst in der Antwort des Servers.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
**Der Aufmacher wusste nur die Serie.** `Heroeintrag.titel` ist bei einer
angefangenen Folge der Name der Serie — richtig, denn der Aufmacher zeigt
die Serie. Wer von dort startete, las über dem Spieler „Biohackers" und
nicht, welche Folge läuft. Staffel, Nummer und Folgentitel reisen jetzt
mit. Dasselbe im Aufmacher selbst: „Staffel 1 · Folge 2" statt „S1 · F2".
**Der Abspann ist kein Angebot, sondern eine Frage.** Die Acht-Sekunden-
Frist von gestern war für das Intro gedacht und traf den Abspann mit:
„Nächste Folge" verschwand genau dort, wo man hinsieht. Ein Intro-Knopf
geht, wenn man ihn nicht nimmt; der Abspann-Knopf bleibt, bis die Folge
zu Ende ist. Web und App.
**„deu" stand da, weil es in der Datei steht.** Der Server reichte einen
vorhandenen Spurnamen unbesehen durch — und viele Dateien tragen als
Titel schlicht das Sprachkürzel. Wiederholt er bloss die Sprache, gilt er
nicht, und die Tabelle darunter macht „Deutsch" daraus.
**Und zweimal „Deutsch" ist keine Wahl.** Zwei deutsche Untertitelspuren
sind der Normalfall — Vollfassung und Nur-Fremdsprachiges. Gleichnamige
bekommen jetzt ihren Codec dazu, und wenn der auch gleich ist, die
Nummer. Das kann keine Spur für sich beantworten: ob ein Name eindeutig
ist, hängt an den anderen. Deshalb steht es in `Benennung` und hat fünf
Prüfungen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bei einer lokalen Datei kehrte der Spieler zurück, sobald sie lief — und
liess `item` leer. Daran hing mehr als der Titel: keine Marken, also kein
„Intro überspringen"; keine Nachbarfolgen, also keine Knöpfe zum Blättern.
Nur bei Heruntergeladenem, und darum sah es so lange nach einem Fehler in
der Erkennung aus. Jetzt werden die Angaben nachgeholt, während das Bild
schon läuft.
Dazu, was derselbe Weg betrifft: der Spieler sagt „Staffel 1 · Folge 6"
statt „S1 E6" — der Folgenname wird an einer Stelle gebaut, nicht an
dreien. Im Abspann einer Serie stehen zwei Knöpfe statt einem, weil der
Abspann zwei Fragen stellt.
Und die Abdrücke, mit denen der Tonabgleich geprüft wird, sind jetzt
echte: zwei Aufnahmen desselben dreissigsekündigen Stücks, verschiedene
Bitraten, durch fpcalc gedreht. Vorher war jede Prüfung mit erfundenen
Zahlen bestanden — und genau deshalb war nie belegt, dass der Abgleich an
echten Daten etwas findet. Er findet: 17,8–51,0 s und 72,7–105,9 s, bei
20–50 und 75–105.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Untertitel kamen vier Sekunden zu spät. Nicht an ihnen lag es: der
Server meldete dem Spieler einen Anfang, der vor dem lag, an dem der
Strom wirklich begann, und genau diese Differenz landet eins zu eins im
Versatz.
Die Schlüsselbildsuche las mit `-read_intervals` bis eine halbe Sekunde
hinter den Wunsch. ffprobe bricht am Ende eines Fensters auf Paketebene
ab — das letzte Schlüsselbild darin kam regelmässig gar nicht mehr
heraus, und die Suche gab das davor zurück. Bei Bildgruppen von vier
Sekunden sind das vier Sekunden.
Nachgemessen gegen die Stelle, an der ffmpeg tatsächlich anfängt, an
einer Datei mit Schlüsselbildern alle vier Sekunden:
Wunsch Suche (0,5 s) ffmpeg Abweichung
52,0 48 52,0 −4,0
73,3 68 72,0 −4,0
100,2 96 100,0 −4,0
Mit zehn Sekunden Nachlauf lag dieselbe Messung bei allen sieben Werten
auf 0,000. Was über den Wunsch hinausgelesen wird, verwirft die
Filterung ohnehin.
Die Prüfung vergleicht nicht gegen eine erwartete Zahl, sondern gegen
ffmpeg selbst — mit dem alten Wert fällt sie um.
Dazu der Filmteil der Abspannerkennung: `Abspannkante` als reine
Rechnung mit elf Prüfungen, `Bildabtaster` über ffmpeg, Marken auch an
Filmen, Knöpfe zum Suchen und Verwerfen auf der Filmseite. An einer
erzeugten Datei trifft die Kante auf vier Sekunden genau.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Gemerkt wurde nur, was gefunden wurde. Eine Staffel ohne Abspann — es
gibt genug davon — sah damit bei jedem Scan aus wie eine, in der noch
niemand nachgesehen hat, und wurde wieder abgetastet: jede Tonspur jeder
Folge noch einmal von der Platte, für immer, ohne Ertrag. Bei
sechstausend Folgen ist das kein Schönheitsfehler.
Jetzt steht auch „gesucht und nichts gefunden" in der Datenbank, je
Staffel und Art. Ausgelöst wird ein zweiter Lauf von der Folgenzahl:
kommt eine Folge dazu, gibt es etwas Neues zu vergleichen, sonst nicht.
Ein Zeitstempel allein täte das nicht — eine Staffel, die sich nie
ändert, hat nie einen Grund für einen zweiten Lauf.
Dazu der Knopf, der ausdrücklich doch noch einmal rechnet: die Uhr auf
der Serienseite sucht ab jetzt immer neu. Von selbst läuft die Suche
nach jedem Scan und überspringt dort das Abgesuchte; wer hier drückt,
will genau das Gegenteil.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Im Protokoll stand für jede einzelne Folge, fpcalc könne die Datei nicht
öffnen — während der Spieler dieselbe Datei im selben Augenblick
umpackte. Null Marken, und der Grund stand nicht da.
`-ts` ist bei fpcalc kein Zeitstempel, sondern ein Schalter: „gib
Zeitstempel aus". Die Sekundenzahl dahinter las fpcalc als zweiten
Dateinamen, fand ihn nicht und brach ab, bevor es die echte Datei
überhaupt ansah.
Nachgemessen am Binärprogramm aus dem Abbild: einen Startpunkt gibt es
bei fpcalc gar nicht. `-length` begrenzt nur die Dauer vom Anfang an —
für den Abspann braucht es aber das Ende. Also einmal die ganze Spur je
Folge, und Vorspann wie Abspann sind danach zwei Ausschnitte aus
demselben Abdruck.
Das ist nicht nur richtig, sondern billiger: eine Folge von 45 Minuten
kostet 1,6 Sekunden und ergibt 21 786 Werte — gegenüber zwei
Ausschnitten, die zusammen mehr gekostet hätten und nie funktionierten.
Die Rate von 0,1239 Sekunden je Wert steht jetzt als Prüfung daneben.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Eine neue Folge bekommt keine Vorspann-Marke, wenn niemand den Knopf
drückt — und niemand drückt ihn, weil man nicht weiss, dass es etwas zu
drücken gibt. Der Scan ist der Augenblick, in dem neue Folgen entstehen;
jetzt hängt sich der Lauf dort hinten an.
Eine Serie nach der anderen, nicht alle zugleich: jede liest von jeder
ihrer Folgen ein Stück Ton, und zwanzig gleichzeitig wären zwanzig
Leseköpfe auf derselben Platte. Gerechnet wird nur, wo noch nichts
steht — beim ersten Mal über einen grossen Bestand dauert das
entsprechend, danach findet der Lauf nichts mehr zu tun.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
**Der Aufmacher darf wechseln.** Bisher hing hinter jedem Titel genau ein
Hintergrundbild, und das Karussell war nach einer Woche ein Standbild.
Jetzt lassen sich bis zu fünf Aufnahmen aussuchen — im Bilderdialog unter
einem vierten Reiter, gespeist aus denselben Hintergründen.
Welches gerade gilt, entscheidet der Server aus Uhrzeit und Kennung: alle
fünf Stunden ein anderes, überall dasselbe — im Web, in der App, nach
einem Neustart. Ein Zufall je Abruf hiesse, dass das Bild beim Blättern
springt; eine gespeicherte Wahl hiesse einen Schreibvorgang je Titel und
Fenster. Die Kennung geht mit in die Rechnung, damit nicht alle Titel im
selben Augenblick umschalten.
Plakat und Hintergrund bleiben unberührt. Sie stehen hinter Kacheln und
Kopfzeilen, und dort soll nichts wechseln.
**Und der Abspann zählt als Schluss.** Eine Serie hängt sechs Minuten
Abspann an eine Folge von fünfundvierzig; neunzig Prozent liegen dann
mittendrin, und die Folge gilt erst als gesehen, wenn man ihn sich ganz
ansieht. Die Schwelle herunterzudrehen hiesse, jeden anderen Titel zu
früh abzuhaken. Der erkannte Abspann ist die genauere Antwort auf
dieselbe Frage — er zieht die Schwelle nach vorn, nie nach hinten.
Dazu der Verlauf über dem Aufmacher: er war immer noch zu dunkel. Jetzt
trägt der Schattenwurf der Schrift, was vorher eine Decke über dem
ganzen Bild tat.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Beides mit derselben Rechnung, nur in einem anderen Fenster: vorn die
ersten fünfzehn Minuten, hinten die letzten zehn. Was `Tonabgleich`
findet, hängt allein daran, welchen Ausschnitt man ihm gibt — eine
zweite Erkennung für den Abspann wäre dieselbe Suche ein zweites Mal.
Je Staffel verglichen, nicht je Serie: Serien wechseln ihren Vorspann
zwischen Staffeln, und über alle hinweg fände man nichts oder das
Falsche. Gefundenes über fünf Minuten wird verworfen — zwei Folgen, die
dieselbe Datei tragen, teilen vierzig, und darüber stünde sonst ein
Überspringen-Knopf.
Der Lauf geht auf Zuruf, nicht bei jedem Scan: er liest von jeder Folge
ein Stück Ton und dauert Minuten. Der Knopf dafür steht auf der
Serienseite, bei den Verwalterknöpfen.
Die Marken gehen im Folgenvertrag mit — als „intro" und „credits", die
Wörter, die die Apps von Jellyfin her ohnehin kennen. In der App zeichnet
sie der vorhandene Überspringen-Knopf, der bisher nie etwas zu tun
hatte; im Web ist er neu, rechtsbündig über der Zeitleiste.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Wer eine Serie schaut, will blättern können. Bisher gab es genau einen
Knopf dafür, und der erschien erst am Ende: „Nächste Folge", wenn der
Abspann durchgelaufen war. Zurück ging gar nicht — nur über die
Serienseite, und die beendet die Wiedergabe.
Jetzt stehen beide Knöpfe in der Bedienleiste, in der App wie im Web,
links und rechts vom Abspielknopf. Nur bei Serien: bei einem Film
antwortet der Server mit 404, bei der letzten Folge mit 204, und beides
heisst hier „kein Knopf". Sie erscheinen also von selbst nur dort, wo
sie etwas tun.
Der Server kannte nur den Weg vorwärts. Jetzt gibt es `/previous`
daneben, und beide Richtungen kommen aus derselben Rechnung — als zwei
Endpunkte abgeschrieben liefen sie auseinander, sobald einer etwas
dazubekommt. Jellyfin hat kein Gegenstück zu „NextUp"; dort entsteht
die vorige Folge aus der Folgenliste der Serie.
Beim Wechsel gilt weiter, was für die Serie an Sprache eingestellt ist.
Ohne das spränge die nächste Folge auf die erste Tonspur der Datei
zurück — bei deutschen Veröffentlichungen also auf Deutsch, mitten in
einer Serie, die man im Original schaut.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ein Vorspann ist das, was in jeder Folge einer Staffel gleich klingt.
Sonst ist an einer Folge nichts, woran er sich erkennen liesse: er sieht
aus wie Handlung und klingt wie Handlung, er wiederholt sich nur.
`Tonabgleich` sucht das längste Stück, das zwei Abdruckreihen teilen —
reine Rechnung, ohne Datei und ohne ffmpeg, deshalb mit sieben Prüfungen
belegt. Darunter die wichtigere Richtung: zwei Folgen ohne Gemeinsamkeit
ergeben nichts. Ein nicht gefundener Vorspann kostet einen Knopf, ein
erfundener schneidet dem Zuschauer eine halbe Minute Handlung weg.
Die Abdrücke holt `fpcalc` aus Chromaprint, nicht selbstgeschriebene
Signalverarbeitung. Dasselbe tut Jellyfins Intro Skipper, aus demselben
Grund. Das Programk liegt ab jetzt im Abbild; fehlt es, gibt es eben
keine Marken — sie sind eine Zugabe, kein Teil der Wiedergabe.
Noch ruft das niemand auf. Tabelle, Lauf und Knöpfe kommen als Nächstes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Zwei Dinge, die zusammengehören.
**Den Bilderdialog gab es nur an der Kachel.** Wer auf der Seite eines
Films stand und sah, dass das Plakat falsch ist, stand genau dort, wo er
nichts dagegen tun konnte — der Weg führte zurück in die Liste und über
das Dreipunktmenü. Jetzt steht „Bearbeiten" in der Knopfreihe der
Detailseite, bei Filmen wie bei Serien, und nur im Kopf: dieselbe Reihe
steht auch über jeder Fassung und jeder Folge.
**Und es waren vierundzwanzig, weil dort vierundzwanzig stand.** Bei
„365 Tage" hat TMDB neunundsechzig Plakate. Dazu ein zweiter, leiserer
Filter: gefragt wurden nur drei Sprachen. Fürs automatische Holen ist
das richtig — dort wird eines genommen, und es soll passen. Wer von Hand
wählt, will sehen, was es gibt; sortiert wird ohnehin nach Sprache, also
steht das Passende weiterhin oben.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Vier Dinge, die beim Ausprobieren auffielen.
**Die Plakate blieben grau.** Sie lagen auf der Platte, der Lader wollte
sie aber nicht: er verlangte von jeder Antwort eine HTTPURLResponse, und
eine Datei antwortet nicht so. Jede lokale Adresse kam als `nil` zurück.
**Eine Serie hatte ohnehin nie ein Plakat.** Heruntergeladen wird nie die
Serie, nur ihre Folgen — und deren Bild ist das Szenenbild, quer. Jetzt
kommt das Serienplakat unter dem Serienschlüssel mit und geht mit der
letzten Folge wieder.
**Zwanzig Folgen waren zwanzig Kacheln.** Jetzt steht die Serie einmal
da, darunter wie viele Folgen bereitliegen; welche genau, zeigt ihre
Seite — auch ohne Server, dann aus dem, was auf dem Gerät liegt.
**Das Bild wurde schwarz, sobald jemand auf „Bildfüllend" wechselte.**
Daran war ein eigener ViewModifier schuld, der zwischen
`ignoresSafeArea()` und gar nichts umschaltete. Genau das ist in SwiftUI
ein Zweig: der Baum darunter wurde neu gebaut, die Bildebene mit ihm.
`edges: []` ignoriert nichts und bleibt doch derselbe Modifikator.
Dazu „Als Nächstes": die Reihe begann am Anfang der Serie statt dort, wo
der Benutzer steht. Wer bei Folge 2 war und Folge 1 nie abgehakt hatte,
bekam Folge 1 vorgeschlagen — und das Abhaken half nicht, weil die
Kachel sich nur selbst einen Haken malte, statt zu verschwinden.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Fortschrittsbalken ist an **einer** Stelle gebaut und wird in jeder
Kachel verwendet — Web wie App. Fehlte er, dann nicht an der Kachel,
sondern weil der Server `State: null` schickte. Drei Listen taten das,
obwohl die Stände in derselben Abfrage danebenlagen:
/home/suggestions — „Weil du Ähnliches gesehen hast"
/home/seasonal — die saisonale Reihe
/movies/{id}/similar — „Ähnliche Titel"
Ausgerechnet die erste entsteht aus dem Sehverhalten: ein halb gesehener
Film sah dort aus wie ein unangetasteter, während dieselbe Kachel in der
Mediathek einen Balken trug. `ZuständeZu` gab es längst, es war nur
privat.
**Und „Als Nächstes" lässt sich jetzt räumen.** Das vorhandene Weglegen
reichte nicht dorthin: die Reihe liest `Watched` und `InProgress`, nie
`Dismissed` — und bei einer nie gestarteten Folge ist `InProgress` ohnehin
`false`. Weggelegt wird deshalb die **Serie**, nicht die Folge: beim
nächsten Laden rückte sonst die übernächste nach, und man legte
zwanzigmal weg. Den Zustand dafür gibt es schon, er wurde nur nie
gelesen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Zwei Beschwerden, zwei Ursachen.
**„Niedrig" war nicht niedrig.** Mit 62 vh war es mittel mit anderem
Namen; die Spanne reichte nur nach oben, weil die fünfte Stufe damals
oben gebraucht wurde. Darunter steht jetzt „Flach" mit 46 vh — für den,
der den Aufmacher als Kopfzeile will und nicht als halbe Seite.
**Der Verlauf fing auf halber Höhe schon mit 55 % Deckung an** und wurde
erst bei 85 % durchsichtig. Von einem starken Bild blieb dadurch vor
allem Grau übrig. Jetzt kurz und steil: unten dunkel genug, dass die
Schrift trägt, und die obere Hälfte frei. Der Text sitzt unten links —
genau dort muss es tragen, und darüber nicht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Im Spieler stand „der Server hat den Download mit Fehler 500 abgelehnt",
und im Tagesprotokoll war davon nichts zu finden. Der Haken, der
Ausnahmen aufschreibt, fängt nur Ausnahmen; ein Endpunkt, der von sich
aus `Results.Problem(… 500)` zurückgibt, läuft daran vorbei.
Jetzt wird jede Antwort ab 500 vermerkt, mit Verfahren, Weg und Abfrage —
egal woher sie kommt. Dieselbe Lehre wie beim ffmpeg-Fehler: eine
Fehlermeldung, die nur der Benutzer sieht, ist eine Sackgasse.
Dazu, weil es beim Suchen im Weg stand: „Hole Metadaten zu Phineas und
Ferb 4×23 — 1334 von 1347" steht nicht mehr im Protokoll. Ein Auffrischen
schrieb vierzehnhundert solche Zeilen und schob alles andere aus der
Ansicht. Das ist Fortschritt, keine Auskunft; er gehört in die
Fortschrittszeile, wo er auch steht. Was bleibt, sind die Vermerke — sie
erklären, warum etwas so zugeordnet wurde — und die Zusammenfassung am
Ende.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Das Menü auf der Serienseite bot zwanzig Sprachen an, von denen die Serie
zwei hatte. Um das zu ändern, muss jemand wissen, welche es wirklich
gibt — und zwar in einer Abfrage: zwanzig Folgen einzeln zu holen wären
zwanzig Rundreisen, und am Ende müsste der Client dieselbe Rechnung
anstellen.
`GET /api/v1/series/{id}/languages` nennt Ton- und Textsprachen als
**Schnittmenge** über alle Folgen. Eine Sprache anzubieten, die es nur in
Staffel 1 gibt, hiesse: eine Einstellung für die Serie, die ab Staffel 2
stillschweigend etwas anderes tut. Was dabei wegfällt, wird gezählt —
damit die Oberfläche es sagen kann, statt es zu verschweigen.
Zwei Dinge, die sonst falsche Ergebnisse gäben:
Gruppiert wird nach dem **Namen**, nicht nach dem Kürzel. Eine gewachsene
Sammlung trägt „ger" und „deu" nebeneinander; nach dem Kürzel gezählt
fiele Deutsch aus der Schnittmenge, obwohl es überall liegt.
Gezählt werden nur Folgen, die eine **Datei** haben. Eine angekündigte
Folge kann keine Sprache beisteuern; sie als fehlend zu zählen liesse das
Menü bei jeder laufenden Serie leer.
Die Rechnung steht in `Flaro.Core`, nicht im Endpunkt: sie hängt an keiner
Datenbank und lässt sich so prüfen, ohne eine Serie anzulegen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
„Keks", „Keksglas", „Kekse" — eingedeutscht, wo es nichts zu
verdeutschen gibt. Ein Cookie ist ein Cookie; wer danach sucht, sucht
nicht nach Keksen, und wer den Quelltext liest, liest daneben die
Schnittstelle, in der es `HTTPCookieStorage` heisst.
Betrifft Kommentare und Prüfungsnamen, kein Verhalten.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Start, Ende, ein paar Fehler — mehr stand nie drin. Dabei meldet der
Server längst, was er tut: „Scan gestartet", „Strom angelegt",
„Untertitel aus … gezogen", „Bibliothek ohne Treffer". Das ging alles an
`ILogger` und damit in die Container-Ausgabe, wo es zwischen den
EF-Abfragen untergeht.
Eine Senke hängt das Tagebuch jetzt an die Protokollkette. Nur Flaros
eigene Kategorien — `Microsoft.*` käme mit einer Zeile je Datenbank-
abfrage und machte das Protokoll nach einem Scan unlesbar. Debug und
Trace bleiben draussen, die sind fürs Entwickeln.
Damit fallen die beiden Stellen weg, an denen ich gestern von Hand ins
Tagebuch geschrieben habe; sie kommen jetzt über denselben Weg wie alles
andere. Der Name der Quelle steht davor: „Streamkoordinator: …".
Nebenbei: das Tagebuch wird von Hand gebaut statt aus dem Behälter
geholt. Die Senke braucht es, bevor der Behälter steht, und ein
`BuildServiceProvider()` dafür legte eine zweite Ausgabe an — zwei
Tagebücher mit zwei Schlössern, die sich beim Schreiben ins Gehege
kommen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Messung sagt es deutlich:
20:15:53 Umpacken — Schlüsselbildsuche 2348 ms, Vorbereitung 2394 ms.
20:16:05 Umkodieren — Schlüsselbildsuche 0 ms, Vorbereitung 0 ms.
20:16:08 erstes Stück nach 2750 ms.
Fünfzehn Sekunden, und zwölf davon liegen zwischen zwei Bestellungen für
denselben Film. Der erste Strom wurde angelegt und dann nie abgeholt —
keine Medienliste, kein Stück, kein ffmpeg-Fehler. Danach gab die App auf
und bestellte neu, diesmal mit Umkodieren, und das lief nach knapp drei
Sekunden.
Dazwischen lag die Hauptliste. Es gibt sie allein wegen der
Untertitelspuren, und bei diesem Film waren die noch gar nicht aus der
Datei gezogen — sie war also leer bis auf einen Verweis auf die
Medienliste. Eine Hülle um nichts, und keine umsonst: ohne `CODECS` muss
ein Spieler sie erst aufmachen, um zu sehen, was darin liegt.
Jetzt gibt es sie nur, wenn wenigstens eine Spur abholbar ist. Sonst
bekommt die App die Medienliste unmittelbar — denselben Weg wie der
Browser, der nie gestolpert ist.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Zwischen dem Antippen und dem ersten Bild liegen drei Strecken: die
Schlüsselbildsuche, der Start von ffmpeg und das Warten auf das erste
Stück. Keine davon war irgendwo abzulesen — jede Erklärung wäre Raterei
gewesen.
Jetzt steht je Wiedergabe eine Zeile im Tagesprotokoll, mit den ersten
beiden Zeiten. Dauert das erste Stück länger als zwei Sekunden, kommt
eine zweite Zeile dazu. Eine Zeile je Wiedergabe ist kein Lärm; sie ist
genau das, wonach man im Protokoll sucht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ffmpeg endete mit 234. No VA display found for device
/dev/dri/renderD128. Device creation failed: -22.
Hier stand `File.Exists("/dev/dri/renderD128")`, und das war keine
Prüfung, sondern eine Vermutung. Auf dem NAS liegt das Gerät
durchgereicht da — eine VA-Anzeige steht nicht dahinter. ffmpeg bricht
dann schon an der globalen Option ab, vor dem ersten Bild, und für den
Zuschauer sieht das aus wie ein kaputter Server: HTTP 500 auf die
Abspielliste, „Die Wiedergabe wurde unterbrochen".
Jetzt wird ffmpeg einmal je Lauf wirklich gefragt: ein Bild aus dem
Nichts, nirgendwohin, mit Frist. Geht die Zeile vor dem ersten Bild nicht
durch, läuft es in Software weiter — langsamer, aber es läuft.
FLARO_HWACCEL=vaapi überspringt die Probe für den, der es ausdrücklich
verlangt.
Dazu, aus derselben Gegend: in der Hauptliste stehen nur Untertitelspuren,
die jetzt sofort abholbar sind. Sie werden beim ersten Bedarf aus der
Datei gezogen, und bei einem Dreistünder dauert das Minuten. Der Browser
verträgt das — er fragt die Spur einzeln an und wartet. AVPlayer bricht
daran die ganze Wiedergabe ab, nicht bloss den Untertitel. Das Füllen
läuft beim Anlegen der Sitzung schon an; beim nächsten Abspielen stehen
sie drin.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Spieler verweist bei einer gescheiterten Wiedergabe aufs
Tagesprotokoll. Dort stand nichts: `ILogger` landet in den
Container-Ausgaben, und das Tagebuch ist eine eigene Datei, in die nur
Scan, Wartung und unbehandelte Ausnahmen schreiben. Der Hinweis war eine
Sackgasse — gemessen an einem Protokoll, das einen ganzen Tag Betrieb mit
vier Zeilen Start und Ende wiedergab, während eine Wiedergabe mit 500
abgebrochen war.
Jetzt geht der ffmpeg-Fehler auch dorthin.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die App schickt keine Codec-Listen; dann nimmt der Server Apples Profil.
Das führt zu einer anderen Entscheidung als beim Browser — dort wird
umgerechnet, hier umgepackt — und damit auf einen Weg, den keine Prüfung
abging. Jetzt schon: bestellen, Hauptliste, Medienliste, erstes Stück.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`index.m3u8` ist eine Medienliste: eine Reihe von Schnipseln, sonst
nichts. Dem Browser reicht das, weil er die Untertitel als eigene Spur
ans Videoelement gehängt bekommt. AVPlayer kann das nicht — dort gibt es
keinen Weg, einer laufenden Wiedergabe eine Textspur von aussen
beizulegen. Was er findet, findet er in der Abspielliste oder gar nicht.
Also eine Hauptliste: dieselbe Medienliste als Bildspur, daneben je eine
Textspur als eigene Liste mit genau einem Stück — unsere WebVTT-Datei
über den ganzen Rest des Films. Der Vorlauf der Sitzung hängt daran, denn
die Zeitstempel in der Datei zählen ab Dateianfang und der Strom ab null.
Für den Browser ändert sich nichts; er holt weiterhin `index.m3u8`. Eine
Direktwiedergabe bekommt keine Hauptliste: die Datei bringt ihre Spuren
selbst mit, und eine Liste darum herum wäre eine Hülle um nichts.
Vorarbeit für die App, die bisher auf eine fertig umgepackte MP4 wartet —
dreissig Sekunden, wo das Web nach einer anfängt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
OMDb führt im Feld `Ratings` nur eine Zahl je Dienst, und bei Rotten
Tomatoes ist das die der Kritik. Die erweiterten Felder gibt es, sie
stehen aber seit Jahren auf `N/A`; Jellyfin fragt sie an und liest sie
nicht einmal aus — deren Antwortklasse kennt `tomatoUserMeter` gar nicht.
Dort hat den Publikumswert also niemand.
Auf der Seite selbst steht er. Gemessen an `/m/the_matrix`: im Quelltext
liegt `"audienceScore":{… "score":"85" …}` neben `"criticsScore"`, dazu
ein `application/ld+json` mit Titel und Erscheinungsdatum; bei Serien
unter `/tv/…` dasselbe, und ein unbekannter Weg antwortet mit 404. Eine
Prüfung mit `[Explicit]` geht einmal wirklich hinaus und bestätigt es:
`rottenTomatoes 83%, rottenTomatoesAudience 85%`.
Das ist Auslesen einer fremden Webseite, und es hat seinen Preis. Eine
offene Suche gibt es nicht — `/napi/search` antwortet mit 401 —, also
wird der Weg aus dem Titel geraten und hinterher am Jahr geprüft: auf der
Seite steht keine IMDb-Kennung, und `/m/the_lion_king` ist der von 1994.
Weicht das Jahr um mehr als eines ab, wird verworfen; lieber keine Zahl
als die eines fremden Films. Der Originaltitel kommt zuerst dran, sonst
fände ein deutscher Bestand dort fast nichts. Ohne Jahr zuerst, weil der
Normalfall den blanken Namen trägt — mit Jahr zuerst kostete jeder Titel
eine Anfrage zu viel.
Deshalb ein Schalter, und der steht aus, bis jemand ihn umlegt. Dazu eine
Bremse: höchstens eine Anfrage je Sekunde, prozessweit. Achthundert Titel
in einem Schwall von einer Adresse wären eine Zumutung und würden
gesperrt. Der Scan dauert dadurch länger; das ist der Preis fürs
Mitbenutzen.
Die Probe unter Einstellungen → Metadaten fragt die Quelle mit, und dort
ist sie mehr als eine Höflichkeit: wird die Seite umgebaut, merkt man es
genau da und sonst nirgends.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Eine neue Quelle wirkt erst beim nächsten Holen der Metadaten. Das ging
bisher nur je Mediathek: achthundert Titel, eine Viertelstunde — nur um
an einem Film zu sehen, ob ein Schlüssel etwas bringt. Wer auf einer
Detailseite bemerkt, dass IMDb und Rotten Tomatoes fehlen, hatte von dort
keinen Weg dorthin.
Der Server kann eine Auswahl längst; es fehlte bloss die Kennung der
Mediathek in der Antwort, damit die Seite weiss, wohin ihr Titel gehört.
Der runde Knopf holt diesen einen Titel, wartet den Lauf ab und zeichnet
die Seite neu.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Wer bei der Zuordnung kein Jahr einträgt, sagt damit, dass er keines
weiss. Der Server reichte trotzdem eines nach — das aus dem Dateinamen —,
und das leere Feld hatte überhaupt keine Wirkung. Gerade bei einem falsch
benannten Film ist der Dateiname aber die unzuverlässigste Quelle, die es
gibt: „Plan B (2010)" heisst bei TMDB „DivertiMento – Plan B" und ist von
2011. Mit dem nachgereichten 2010 kam nie ein Treffer, und es gab keinen
Weg daran vorbei.
Ohne Angabe wird jetzt ohne Jahr gesucht. Der Platzhalter sagt es auch.
Auf der Detailseite stand im Feld bisher das Jahr der bestehenden
Zuordnung — also genau der, die gerade korrigiert werden soll. „Doctor
Strange" war auf ein Making-of von 2022 gelaufen; mit dessen Jahr konnte
der richtige Film von 2016 gar nicht erscheinen. Das Feld bleibt jetzt
leer, und leer heisst überall dasselbe.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nebeneinander standen „7,2" und „12", beide ohne Beschriftung. Die 12
konnte alles heissen: zwölf Folgen, zwölf von hundert Punkten, ab zwölf
Jahren. Die Auskunft steckte im Tooltip, und den sieht nur, wer schon
vermutet, dass dort etwas steht. Jetzt trägt jeder Wert seine Quelle:
FSK 12, TMDB 7,2, IMDb 7,4, MC 62.
Bei Rotten Tomatoes ist „RT 87%" ebenfalls keine Auskunft, denn dort
stehen zwei Zahlen — die der Kritik und die des Publikums, oft weit
auseinander. Also „RT Kritik" und „RT Publikum", auch wenn nur eine
der beiden da ist.
Und das Publikum wird mitverlangt: `tomatoes=true` legt die Felder
derselben Antwort bei, kostet keine zweite Anfrage und nichts vom
Tageskontingent. Ob etwas darin steht, ist eine andere Frage — seit 2018
wird berichtet, dass dort `N/A` steht, und die Frage, ob die Felder je
wieder Daten führen, ist bei OMDb seit 2017 unbeantwortet offen. Darum
wird nichts erwartet und nichts erzwungen: steht eine Zahl da, erscheint
sie; steht `N/A`, entsteht keine Wertung. Die Probe unter Einstellungen →
Metadaten zeigt, was wirklich kam.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ein Film stand bei 1:32 und tauchte in „Fortsetzen" trotzdem nicht auf.
Die Reihe fragt nicht die Regel, sondern deren festgehaltenes Ergebnis:
die Spalte `InProgress`. Die Bruchrechnung „Stelle kleiner als Anteil der
Laufzeit" lässt sich nicht nach SQL übersetzen, also steht sie als
Wahrheitswert in der Zeile — und berechnet wird sie nur, wenn eine
Fortschrittsmeldung eintrifft. Die kaputte Meldung von vorgestern hatte
dort `false` hinterlassen; die Korrektur am Server verhinderte neuen
Schaden, räumte den alten aber nicht weg.
`Sehstandpflege` rechnet nach jedem Update einmal durch: erst die
Laufzeit aus der Datei nachtragen, dann neu bewerten — in dieser
Reihenfolge, denn eine Neubewertung auf Basis eines Stummels käme wieder
zu `false`. `UpdatedAt` bleibt, wo es war; danach ist die Reihe sortiert.
Und der Knopf heisst jetzt so wie die Reihe, über der er steht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
**Man konnte nicht sehen, ob ein Schlüssel wirkt.** Nach dem Eintragen
stand „Hinterlegt" da, und das war alles. Ob er stimmt, ob der Dienst
antwortet, ob zu einem Film überhaupt etwas zu holen ist — herausfinden
liess sich das nur, indem man die Metadaten über achthundert Titel
auffrischte und danach nachsah, ob sich etwas geändert hatte.
Jetzt gibt es „Verbindung prüfen". Es wird mit „The Matrix" gefragt, nicht
mit einem Film aus der Mediathek: der könnte schlicht unbekannt sein, und
dann wüsste man wieder nicht, ob es am Schlüssel liegt oder am Film. Die
Antwort nennt, was zurückkam — bei fanart.tv die Zahl der Plakate und
davon die in der eingestellten Sprache, denn genau dafür ist die Quelle da.
Gemessen am laufenden Server:
tmdb eingerichtet ok Erreichbar — Probeabruf ergab The Matrix (1999).
fanart — — Kein Schlüssel hinterlegt — die Quelle ist aus.
omdb — — Kein Schlüssel hinterlegt — die Quelle ist aus.
**Und die Übersicht war eine Ablage für alles geworden.** Zu Recht
angemerkt: es gibt eine Seite „Metadaten", und die Zugänge standen trotzdem
unter „Übersicht" — zwischen Servername, doppelten Einträgen, Fassung und
Speicherplatz. Die drei Quellen stehen jetzt dort, wo man sie sucht, jede
mit einem Satz dazu, was sie liefert und was sie dafür braucht.
Aufmacherhöhe und saisonale Reihen bekommen einen eigenen Bereich
„Darstellung". Eine Seite, die „Übersicht" heisst, ist nicht der Ort für
alles, was sonst nirgends passt.
**Am Telefon tat die Aufmacherhöhe gar nichts.** Gemeldet als „auf 100 %
ist es immer noch zu niedrig" — und das stimmte buchstäblich: unter 860
Pixeln Breite stand `height: min(52vh, 420px)` fest und überschrieb die
Einstellung vollständig. Ob „niedrig" oder „sehr hoch", es kamen immer
dieselben 52 vh heraus.
Jetzt hat jede Stufe zwei Höhen, eine fürs breite und eine fürs schmale
Bild; am Telefon fällt sie eine Spur flacher aus, weil der Bildschirm
hochkant ist und derselbe Anteil der ersten Kachelreihe dort den Platz
nähme. Dazu eine fünfte Stufe „Bildschirmhoch" — mehr ginge nicht
sinnvoll, darüber ist es kein Aufmacher mehr, sondern die ganze Seite.
Eine Prüfung hält ausserdem fest, dass ein Film ohne passendes Stichwort
und ohne passendes Genre nicht in der saisonalen Reihe steht. Anlass war
die Meldung, dort stünden harmlose Zeichentrickfilme — die Reihe arbeitet
richtig, sie macht nur sichtbar, wenn ein Titel auf den falschen Eintrag
zugeordnet ist.
765 Tests grün.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Zu Halloween, zur Weihnachtszeit, zum Jahreswechsel, im Sommer — und zu
allem, was sonst jemand feiert.
**Nach Stichwörtern, nicht nach Genres.** „Stirb langsam" ist kein
Familienfilm und trägt trotzdem „christmas". Eine Weihnachtsreihe nach
Genres enthielte jeden Zeichentrickfilm und diesen Film nicht. TMDB führt
die Stichwörter ohnehin; sie kommen jetzt über `append_to_response` mit
und landen in einer eigenen Tabelle, nach demselben Muster wie Genres.
Kleingeschrieben abgelegt — TMDB liefert „Christmas" und „christmas" je
nach Eintrag, und zwei Schreibweisen wären zwei Stichwörter.
**Einstellbar und nicht eingebaut.** Halloween und Weihnachten kennt
jeder; der 1. August ist Schweizer Nationalfeiertag, der 4. Juli
amerikanischer, und der Geburtstag des Betreibers steht in keiner
Bibliothek. Eine feste Liste im Quelltext hiesse: für jeden Anlass eine
neue Fassung. Vier sind vorangelegt — als Beispiel, nicht als Meinung
darüber, was jemand feiert.
**Über den Jahreswechsel hinweg.** „28.12. bis 02.01." ist kein Fehler,
sondern der Normalfall für Silvester; ein Vergleich, der nur „von ≤ heute
≤ bis" kennt, fände diesen Zeitraum nie — und die Reihe stünde genau in
den Tagen nicht da, für die sie gedacht ist. Geprüft, Tag für Tag, samt
der Zusicherung, dass sich die vorangelegten Anlässe an keinem Tag im Jahr
überschneiden.
**Leer heisst leer.** Gilt heute kein Anlass oder findet er nichts, kommt
eine Reihe ohne Namen zurück und der Client zeigt gar keine — statt einer
mit Überschrift und nichts darunter. Scheitert der Abruf ganz, fehlt eine
Reihe und nicht die Startseite.
Bedient wird sie als Text, eine Zeile je Anlass: Name, von, bis,
Stichwörter, Genres. Ein Listeneditor mit Zeilen zum Hinzufügen,
Verschieben und Löschen wäre viel Oberfläche für etwas, das man dreimal im
Jahr anfasst — und es ist dieselbe Form, in der es gespeichert wird.
Ein Anlass ohne Stichwort und ohne Genre wird abgewiesen statt
stillschweigend geschluckt: er fände nie etwas, und wer ihn eingetragen
hat, suchte den Fehler woanders.
**Die Stichwörter kommen erst beim nächsten Auffrischen der Metadaten.**
Vorhandene Titel tragen noch keine — bis dahin bleibt die Reihe leer.
764 Tests grün, davon 22 neue.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Urlaubsaufnahmen, Mitschnitte, Vorträge. Jedes Verfahren, das bei Filmen
richtig ist, wäre hier falsch: „Sommer 2019 Teil 2" ist kein Film von 2019,
zwei Aufnahmen desselben Tages sind nicht zwei Fassungen derselben Sache,
und in keinem Katalog steht, was da liegt.
Die neue Art `Videos` nimmt den Dateinamen als Titel, zerlegt nichts, legt
nichts zusammen und fragt niemanden. Der Pfad ist dabei der
Vergleichsschlüssel, nicht der Name — zwei Dateien „Ausflug.mp4" in zwei
Ordnern sind zwei Ausflüge.
**Der Unterordner wird zur Kategorie.** Statt eine eigene Gruppierung zu
bauen, bekommt jedes Video den Namen seines Ordners als Genre. Damit
greifen Filter, Kategorienansicht und Suche, ohne dass es in der Oberfläche
etwas Neues gibt. Eine zweite Gruppierung daneben hiesse: zwei Wege für
dasselbe, und einer davon wäre überall sonst ungepflegt.
Die Anreicherung übergeht jetzt alles, was `MatchLocked` trägt. Das galt
bisher nur für von Hand korrigierte Zuordnungen und gilt damit auch hier —
eine Suche nach „Abend am Feuer" fände irgendeinen fremden Film, und der
stünde danach mit Plakat und Beschreibung in der Urlaubsmediathek.
Geprüft: vier Dateien in zwei Ordnern ergeben vier Titel mit ihren
Dateinamen, keine Jahre, keinen fremden Treffer — und genau zwei
Kategorien, denn was direkt in der Wurzel liegt, bekommt keine.
742 Tests grün, davon zwei neue.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
IMDbs eigene Schnittstelle kostet Geld, Rotten Tomatoes vergibt Zugänge nur
an Partner, Letterboxd hat gar keine offene. OMDb liefert alle drei in
einer Antwort, über die IMDb-Kennung, die ohnehin schon an jedem Film steht.
**Eine Tabelle und kein Feld je Quelle.** `Movie.Rating` ist ein Skalar
ohne Quellenangabe, und das war richtig, solange es eine Quelle gab. Für
drei weitere wären es drei Spalten — und bei der vierten wieder eine
Migration. Jetzt ist jede Quelle eine Zeile, je Titel und Quelle eine, mit
eindeutigem Index darauf: ein zweiter Lauf schreibt um statt anzuhäufen.
**Zwei Werte je Bewertung, und beide gebraucht.** Der normierte Wert zum
Vergleichen und Sortieren, die Anzeige zum Hinschreiben. Rotten Tomatoes in
„8,7 von 10" umzurechnen wäre eine Lüge über die Quelle — dort steht 87 %,
und genau so kennt man es.
**Wieder keine zweite Metadatenquelle.** `IBewertungsquelle` liefert
Bewertungen, sonst nichts. Bei Titel und Beschreibung wären zwei Anbieter
zwei Wahrheiten; bei einer Bewertung ist jede Quelle eine eigene Zahl, und
da sind mehrere kein Widerspruch, sondern der Sinn der Sache.
**Ohne Kennung wird nicht gefragt.** OMDb kennt nur den Weg über die
IMDb-Kennung; nach einem Titel zu suchen hiesse, denselben Zuordnungsfehler
ein zweites Mal zu riskieren. Und auf Unbekanntes antwortet OMDb mit 200
und `Response: "False"` — das als Treffer durchzulassen legte eine leere
Bewertung ab, die danach als „keine Wertung" dastünde.
Angezeigt wird mit Kürzel statt Stern: vier gleich aussehende Zahlen
nebeneinander sagen nicht, woher sie kommen. Kein Logo — die Marken von
IMDb und Rotten Tomatoes mitzuliefern hiesse, fremde Zeichen auszuliefern.
Kein mitgelieferter Schlüssel, aus demselben Grund wie bei fanart.tv: OMDb
begrenzt auf tausend Abrufe am Tag je Schlüssel. Einzutragen unter
Einstellungen → Übersicht → Metadaten.
740 Tests grün, davon acht neue.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
„365 Tage" stand mit polnischem Plakat da. Nachgesehen: TMDB hat zu diesem
Film 26 Plakate und kein einziges deutsches. Die Auswahl von gestern nimmt
seither das sprachlose statt des polnischen — erfinden kann sie keines.
fanart.tv ist die Quelle, bei der jedes Plakat seine Sprache mitführt, und
die Gemeinschaft dort pflegt landessprachliche Fassungen. OMDb wäre keine
Hilfe gewesen: es liefert das Plakat von IMDb, und das ist in aller Regel
das amerikanische.
**Ausdrücklich keine zweite Metadatenquelle.** `IBildquelle` liefert
Bilder, sonst nichts. Titel, Beschreibung und Besetzung kommen weiterhin
von einer Stelle — zwei Anbieter für dieselbe Angabe wären zwei Wahrheiten.
**Gefragt wird nur, wenn es lohnt.** `MovieMetadata` trägt jetzt die
Sprache des gewählten Plakats mit. Hat TMDB schon eines in der
eingestellten Sprache, geht keine Anfrage hinaus — bei achthundert Titeln
ist das der Unterschied zwischen „läuft nebenher" und „hängt am fremden
Dienst". Und nur ein landessprachliches sticht: ein sprachloses von
fanart.tv ist nicht besser als ein sprachloses von TMDB.
**Die Rangfolge liegt jetzt im Kern**, nicht im TMDB-Client. Sie gilt für
alle Quellen, sonst kämen die Bilder zweier Anbieter in zwei Reihenfolgen
zusammen. Im Bilderdialog stehen beide Quellen gemeinsam geordnet: ein
deutsches Plakat von fanart.tv vor einem sprachlosen von TMDB.
**Nur Filme.** Der Serienweg von fanart.tv verlangt eine TVDB-Kennung, und
die führt Flaro nicht. Eine halbe Lösung, die bei Serien stillschweigend
nichts tut, wäre schlechter als eine, die sagt, worauf sie sich beschränkt.
**Kein mitgelieferter Schlüssel.** Bei TMDB gibt es einen als Rückfall;
fanart.tv verlangt einen persönlichen, und einen fremden mitzuliefern
hiesse, ihn fremd zu benutzen. Ohne eigenen Schlüssel geht keine Anfrage
hinaus — geprüft. Einzutragen unter Einstellungen → Übersicht → Metadaten.
Dass „00" dort „ohne Sprache" heisst, wird beim Lesen übersetzt: sonst
führte die gemeinsame Rangfolge ein sprachloses Plakat als Sprache „00"
und setzte es hinter Englisch statt davor.
732 Tests grün, davon sieben neue.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sichern, Datenbank aufräumen, verwaiste Bilder wegwerfen — alle drei Tage,
alle sieben Tage, von selbst.
**Der erste Hintergrunddienst in diesem Server, und das mit Bedacht.** An
drei Stellen steht sinngemäss, ein eigener Dienst wäre Aufwand ohne
Gegenwert, und für das Wegräumen abgelaufener Gerätecodes stimmt das:
solche Arbeit hängt an einem Aufruf, der ohnehin kommt. Eine Sicherung
hängt an nichts. Sie soll alle drei Tage geschehen, auch wenn niemand etwas
anklickt, und dafür gibt es eine Uhr. Alle fünfzehn Minuten eine Frage an
eine Tabelle mit einer Handvoll Zeilen; ist nichts fällig — der Normalfall
—, kostet es nichts.
Der letzte Lauf steht in der Datenbank, nicht im Arbeitsspeicher. Ein
Server, der jede Nacht neu startet, sicherte sonst jeden Morgen — oder,
bei einem Zähler, der bei null anfängt, nie. Auch ein Fehlschlag wird
vermerkt, sonst versuchte der Plan es alle fünfzehn Minuten wieder und
eine volle Platte erzeugte ein volles Protokoll.
**Die Sicherung ist `VACUUM INTO`, nicht Datei kopieren.** Die Datenbank
läuft im WAL-Modus: neben `flaro.db` stehen `-wal` und `-shm`, und wer nur
die erste Datei kopiert, bekommt einen Stand von vor Stunden oder einen
halben. `VACUUM INTO` schreibt eine vollständige, aufgeräumte Kopie im
laufenden Betrieb. Der Zielordner ist einstellbar und geht als gebundener
Wert in die Anweisung — eine Einstellung soll keine SQL-Anweisung werden.
Die Prüfung verlässt sich nicht darauf, dass eine Datei entstanden ist: sie
öffnet die Kopie als Datenbank und zählt die Zeilen. Eine Sicherung, die
sich nicht öffnen lässt, ist schlimmer als keine — man verlässt sich
darauf und merkt es erst, wenn es zu spät ist.
**Bilder nur, wenn verwaist.** Den ganzen Bildspeicher zu leeren gibt es
als Knopf und ist gefahrlos, kostet danach aber tausende Abrufe bei TMDB.
Hier fliegt nur, worauf kein Titel mehr zeigt — mit einer Stunde
Schonfrist, weil ein Bild abgelegt wird, bevor der Verweis darauf
gespeichert ist. Ohne diese Frist räumte ein Lauf mitten im Scan genau das
weg, was gerade geholt wurde.
**Eine eigene Seite „Wartung".** Die drei Kratzplatzzahlen standen unter
„Wiedergabe" — dort ist ihr Anlass, aber nicht ihr Thema. Sie ziehen mit
um: alles auf dieser Seite ist etwas, das der Server ohne Zutun erledigt,
und man sieht es an einem Ort nach. Je Aufgabe steht da, wann sie zuletzt
lief und was herauskam, dazu ein Knopf „Jetzt ausführen" — wer gleich etwas
Grosses vorhat, will die Kopie jetzt und nicht in drei Tagen.
725 Tests grün, davon elf neue.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
39,1 GB in FLARO_TRANSCODE, und das war kein Normalzustand, sondern ein
Leck: für jede Mitnahme baut der Server eine MP4 in Quellgrösse und löschte
sie nie. Die einzige Löschung, die es gab, räumte ältere Fassungen
derselben Datei weg. Stromordner verschwanden nur beim geordneten
Herunterfahren, Untertitel gar nicht.
**Zuerst: fertig heisst fertig.** ffmpeg schrieb direkt auf den Endnamen.
Nach einem harten Abbruch lag dort eine abgeschnittene Datei, `IsReady`
meldete „fertig", und sie ging so hinaus — und ein Aufräumer konnte
vollständig und abgeschnitten nicht unterscheiden. Gebaut wird jetzt unter
`.teil`, umbenannt erst nach Erfolg. Dabei muss das Format ausdrücklich
gesetzt werden: ffmpeg wählt den Muxer an der Endung und antwortet auf
`.teil` mit „Unable to choose an output format". Das hätte jedes Umpacken
zerlegt; die Prüfung hat es gefunden, bevor es jemand zu spüren bekam.
**Zwei Stufen für die Pakete.** Verwaiste gehen bedingungslos: unleserlicher
Name, fremde Bauart, kein Eintrag in der Mediathek, oder der Zeitstempel im
Namen passt nicht mehr. Der Rest fällt unter Obergrenze (20 GB) und Alter
(7 Tage), Ältestes zuerst.
Beides, und nicht eines: ein Alter allein löst den Fall nicht, denn die 39
GB waren gültige, aktuelle Dateien, jede für sich berechtigt. Eine
Obergrenze allein ist zu grob — ein Topf knapp darunter, in dem seit Wochen
nichts gebraucht wurde, besetzt ein Volume, das anderes braucht.
„Zuletzt gebraucht" ist die Änderungszeit der Paketdatei selbst, bei jeder
Auslieferung neu gestempelt. Die Zugriffszeit wäre das Naheliegende, nur
führt sie nicht jedes Dateisystem — mit `noatime` steht sie still, und der
Aufräumer hielte alles für uralt.
Ein laufender Download wird nie gelöscht: ein Benutzungszähler hält die
Datei fest, solange die Antwort läuft, dazu eine Schonfrist von 30 Minuten.
Der Zähler ist auch unter Linux nötig — der laufende Strom überlebt ein
Entfernen, die Fortsetzung einer Bereichsanfrage nicht, und der Client finge
von vorn an.
**Stromordner** werden beim Start bedingungslos geleert — nach einem Start
gibt es keine einzige Sitzung, also ist jeder Ordner verwaist. Im Betrieb
nur die ohne passende Sitzung, deren Änderungszeit älter ist als die
Geduld; das fängt auch den Fall, dass `Abraeumen` an einem IOException
scheitert und der Ordner für immer liegenbleibt.
**Untertitel** nur, wenn sie verwaist sind. Keine Obergrenze: eine .vtt ist
Kilobytes, und eine gültige wird wieder gebraucht — sie nach Platz zu
löschen hiesse, Minuten Lesearbeit gegen Megabytes zu tauschen. Eine leere
bleibt ausdrücklich liegen: sie ist das Ergebnis „diese Spur hat keinen
Text", und wer sie wegräumt, lässt bei jedem Start eine vierzig Gigabyte
grosse Datei neu durchlesen.
**Vier Anlässe, kein Dienst.** Start, vor jedem Umpacken, nach einem Scan,
auf Knopfdruck — wie alles Wiederkehrende in diesem Server. Einer zur Zeit;
wer nicht hineinkommt, geht weiter.
Gemessen am laufenden Server mit Attrappen:
vorher packages 17408 KB (4), untertitel 5 KB (3), 1 Stromordner
nachher 14 MB freigegeben, 4 Dateien
gültiges Paket blieb, verwaistes/veraltetes/Teilstück weg,
verwaiste .vtt weg, gültige blieben, Stromordner weg
Obergrenze 1 MB, Datei in der Schonfrist -> bleibt
Obergrenze 1 MB, Schonfrist vorbei -> 3072 KB freigegeben
In der Übersicht steht die Belegung je Unterordner mit Dateizahl und
daneben „Jetzt aufräumen"; die drei Zahlen stehen unter Wiedergabe, wo
Transcoder-Stufe und Schwellen schon stehen — der Kratzplatz ist, was die
Wiedergabe hinterlässt.
714 Tests grün, davon 36 neue.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
**Mehrfachfassungen lassen sich abhaken.** Zwei Dateien an einem Film sind
meist zwei Fassungen und völlig in Ordnung. In der Liste standen sie
trotzdem für immer: sie rechnet jedes Mal frisch, und es gab keinen Zustand.
Wer absichtlich zwei Fassungen hält, konnte die Gruppe nicht loswerden — und
unter den gewollten ging das Ungewollte unter.
Abgehakt wird eine Dateizahl, kein Zeitpunkt. Ein Zeitstempel müsste gegen
`MediaFile.ModifiedAt` verglichen werden, und eine alte Datei, frisch
hineinkopiert, behält ihre alte Zeit und käme nie wieder hoch. Die Anzahl
ist genau die Frage: kommt eine dritte Fassung dazu, meldet sich die Gruppe
von selbst zurück. Gerechnet wird sie auf dem Server — ein Client mit
veralteter Liste hakte sonst eine Gruppe mit zwei Dateien ab, die inzwischen
drei hat.
„Ignorierte zeigen" führt zurück, wie bei den erledigten Meldungen; ohne das
wäre das Abhaken unumkehrbar und niemand wüsste, was er weggeklickt hat.
**Die Startseite wartete dreimal nacheinander.** Erst die Serverauskunft,
dann Konto und Mediatheken, dann erst die Startseite — drei Rundreisen,
bevor das erste Bild in Auftrag ging. Liegt eine Sitzung im Speicher, laufen
die ersten beiden jetzt zugleich los. Nur dann: ohne Sitzung liefen
`/auth/me` und `/libraries` vor der Antwort auf `needsSetup` in ein 401, und
die Sitzungsprüfung kann daraufhin abmelden.
Gemessen am laufenden Server: `users/me`, `system/info` und `libraries`
starten alle bei 37 ms, die Startseitenabfragen folgen bei 73 ms. Vorher
lagen drei Runden dazwischen.
**Sieben Hintergrundbilder auf einmal waren sechs zu viel.** Jede Folie des
Aufmachers bekam sofort ihr `w1280`-Bild; sichtbar ist immer nur eine. Jetzt
lädt die sichtbare sofort und der Rest in der Leerlaufzeit — und wer
weiterschaltet, bevor das lief, bekommt sein Bild dann.
**Die Schrift kam von Google.** `display=swap` rettete den Text, nicht den
ersten Aufbau: ein Stylesheet blockiert das Zeichnen, und ein Heimserver
ohne Internet oder mit träger Namensauflösung wartete davor. Inter liegt
jetzt als zwei Dateien von zusammen sechzig Kilobyte im Projekt, variabel
für alle Schnitte, mit `unicode-range` getrennt nach latin und latin-ext.
Gemessen: null Anfragen an fonts.googleapis.com. Lizenztext liegt dabei.
**Und mein Untertitelvorrat bremste mit.** Er startete sofort beim
Hochfahren und las die ganze Mediathek — von derselben Platte, von der die
Startseite ihre Bilder holt. Jetzt beginnt er eine Minute später und stellt
zurück, solange eine Wiedergabe läuft. Die Datei bleibt dabei vorn in der
Reihe; verworfen wird nichts.
678 Tests grün, davon zwei neue.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
**Der Einstellungsknopf lag hinter der Abspielleiste.** Gemessen: `.pl`
steht auf Stufe 150, `#chrome` auf gar keiner, und die kleine Leiste spannte
über die ganze Breite — genau über die unteren 72 Bildpunkte der
Seitenleiste, wo „Einstellungen" klebt. Weil der Spieler bei jedem Wechsel
der Adresse minimiert, stand sie zuverlässig im Weg, sobald man dorthin
wollte.
Die Leiste beginnt jetzt hinter der Seitenleiste. Nicht über die Stufen
gelöst, und das ist der Punkt: `#chrome` höher zu setzen hiesse, die
Seitenleiste läge auch über dem grossen Spieler — es ist dieselbe Hülle.
Dass diese Leiste Platz wegnimmt statt sich darüberzulegen, stand für
Inhalt, Hinweise und Auswahlleiste ohnehin schon da; die Seitenleiste war
die vergessene vierte. Gemessen, 1400 Bildpunkte breit: Leiste ab 240,
Knopf bei 0, keine Überschneidung, und ein Treffertest in der Knopfmitte
trifft den Knopf.
**Ein Klick auf eine Kategorie kam nie an.** Dort sprang der Code in die
Mediathek und suchte danach `.filterzeile select`, um den Wert zu setzen.
Die Filterzeile hat kein `<select>` — `auswahl()` baut einen Knopf, mit
Absicht und begründet. Der Zweig lief also nie, und man landete in der
ungefilterten Mediathek. Der Kommentar daneben behauptete das Gegenteil.
Das Genre steht jetzt in der Adresse: `#/m/{id}/mediathek/Horror`. Das
löst zugleich das zweite Problem — die Mediathek wird unter ihrem Weg
gemerkt, und stünde das Genre nur im Kopf, zeigte ein zweiter Besuch die
Liste des ersten. Geprüft: Klick setzt den Weg und die Pille, Neuladen
behält beides, Zurück führt auf die Kategorien, Vorwärts wieder hinein.
**Zurückgezogene Einladungen blieben für immer stehen.** Der Knopfblock
steckte ganz in `if (!e.revokedAt)`, eine erledigte Zeile hatte also gar
keinen Knopf. Die Serverbegründung — eine Zeile wegzuwerfen hiesse,
hinterher nicht mehr zu wissen, dass es den Link je gab — bleibt richtig,
sie gilt nur nicht ewig: eine Liste, aus der nichts verschwindet, wird
unlesbar, und dann sieht man die eine Zeile nicht mehr, um die es ging.
Deshalb kein umgedeutetes DELETE, sondern `?purge=true`, und das greift nur
bei einem Link, der ohnehin keine Tür mehr öffnet. Ein gültiger wird mit
einem deutschen Satz abgewiesen.
**Poster in der falschen Sprache.** „365 Tage" stand mit polnischem Plakat
da. Kein fehlender Anbieter: für den Schriftzug gab es die sprachgewichtete
Wahl längst, fürs Plakat wurde das blanke `poster_path` der Hauptantwort
genommen — bei TMDB das Bild der Originalsprache, wenn in der gewünschten
keines eingetragen ist. Die sortierte Liste lag im selben Abruf schon vor
und wurde ausschliesslich für den Schriftzug benutzt.
Plakat und Hintergrund nehmen jetzt dieselbe Rangfolge: eingestellte
Sprache, dann sprachlos, dann Englisch. Sprachlos vor Englisch ist keine
Verlegenheit — ein Bild ohne Sprachkennung trägt keinen Text, und ein
Plakat ohne Titel ist für einen deutschen Bestand besser als dasselbe mit
englischem. Im Bilderdialog stehen die Kandidaten jetzt ebenso sortiert.
Bestehende Titel ändern sich beim nächsten Auffrischen; von Hand gesetzte
Plakate bleiben unangetastet.
676 Tests grün, davon zehn neue.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
„Auf der App höre ich einfach alle Audiospuren gleichzeitig."
MP4 ordnet Spuren derselben Art einer `alternate_group` zu, und ein
Abspieler nimmt daraus genau eine. ffmpeg leitet diese Gruppe aus der
Markierung der Quelle ab — sind dort zwei Tonspuren als Vorgabe markiert,
hält es sie für Verschiedenes und legt sie in getrennte Gruppen. Dann ist
jede für sich eingeschaltet, und man hört beide übereinander. Bei Rips aus
freier Wildbahn ist die doppelte Markierung eher Regel als Ausnahme; in
Matroska stört sie niemanden, in MP4 ist sie der Unterschied zwischen Wahl
und Chor.
Gemessen an einer Quelle mit zwei markierten Tonspuren, am Spurkopf der
fertigen MP4:
vorher Spur 2: an, Gruppe 1
Spur 3: an, Gruppe 2 <- beide laufen
nachher Spur 2: an, Gruppe 1
Spur 3: aus, Gruppe 1 <- eine zur Wahl
Das Umpacken schreibt jetzt genau eine Vorgabe je Spurart: die, die es in
der Quelle schon war, sonst die erste. Auch die Nullen gehören
hingeschrieben — ffmpeg übernähme sonst die Markierung der Quelle für alle
übrigen, und genau die ist ja das Problem. „Erzwungen" wird dabei
ausdrücklich erhalten, weil `-disposition` die ganze Markierung ersetzt
statt sie zu ergänzen.
Untertitel bekommen dieselbe Behandlung. War in der Quelle keiner
vorgesehen, bleibt es dabei — sonst erschienen auf einmal zwei übereinander.
Der Name im Zwischenspeicher trägt jetzt die Bauart mit. Ohne das bliebe
auf einem NAS, das schon umgepackt hat, die fehlerhafte Datei liegen und
würde weiter ausgeliefert, obwohl der Server es längst besser kann.
Geprüft an der echten Datei, am `tkhd` und nicht an der Disposition: was
ein Abspieler tut, hängt an „enabled" und `alternate_group`, und ffprobe
zeigt davon nur einen Teil. Ohne die Korrektur fällt die Prüfung um
(Gruppe 1 gegen 2).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Zwei Minuten „wird geholt", und noch immer kein Text. Die Ablage von vorhin
war richtig und reichte nicht: beim ersten Mal musste immer noch jemand
warten, und zwar der, der gerade einen Film anschaut.
Das Lesen selbst lässt sich nicht beschleunigen. Untertitelpakete liegen
über die ganze Datei verteilt; sie zu finden heisst, alles zu lesen. Bei
vierzig Gigabyte auf einem NAS sind das Minuten, und daran ändert kein
Schalter etwas. Also nicht schneller lesen, sondern früher.
Der Vorrat arbeitet die Mediathek jetzt von sich aus durch: beim Start des
Servers und nach jedem Scan, die zuletzt hinzugekommenen zuerst. Eine Datei
nach der anderen, mit zwei Sekunden Pause dazwischen — das Lesen ist von
der Platte begrenzt, und eine laufende Wiedergabe liest von derselben
Platte und ist wichtiger. Wer einen Film startet, drängelt sich vor: seine
Datei ist als Nächstes dran statt als Dreissigste.
Einmal je Datei, nie wieder: der Name der Ablage trägt den
Änderungszeitpunkt. Eine ersetzte Datei bekommt dadurch von selbst einen
neuen Namen, eine unveränderte wird nie zweimal gelesen.
Gemessen am Prüfserver mit leerer Ablage:
Start, ohne jeden Abruf -> „1 Datei(en) werden im Hintergrund
gelesen", danach „2 von 2 Spur(en)"
Klick auf „Deutsch" -> 153 ms bis zum Text, 10 Einblendungen,
die erste bei 0 bis 3 s
Landet jemand trotzdem im Wartefall, gibt der Spieler nicht mehr nach zwei
Minuten auf, sondern fragt zehn Minuten lang nach — und sagt dazu, dass es
einmalig ist.
Die Auswahl der Textspuren stand zweimal im Quelltext und steht jetzt
einmal: im Vorrat. Zwei Listen, von denen eine das Menü füllt und die
andere bestimmt, was geholt wird, laufen sonst irgendwann auseinander.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
„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>