Commit Graph
414 Commits
Author SHA1 Message Date
ScarriffleandClaude Opus 5 0a394b9b3b „Wartet" für immer, obwohl der Server längst fertig war
Der Eintrag ging nach einer 202 zurück auf „wartet" und rief danach
`resume`. Das nimmt aber nur auf, was **angehalten oder gescheitert**
ist — ein wartender lief in die Schutzklausel und kam nie wieder heraus.
Im Protokoll packte ffmpeg um, in der App stand „wartet", und dazwischen
lag nichts.

Jetzt wird die Warteschlange abgearbeitet statt ein einzelner Eintrag
aufgenommen; „wartet" steht darin.

Dazu zwei Dinge, um die du gebeten hast:

**Das Protokoll liest sich wie eine Kommandozeile.** Älteste Zeile oben,
neueste unten, die Ansicht steht am Ende. Mitgeschoben wird nur, wenn man
unten steht — wer hochgescrollt hat, liest etwas, und ihn bei jeder neuen
Zeile ans Ende zu reissen wäre, als nähme man ihm die Seite aus der Hand.

**Ein Tipp auf eine Kachel führt auf die Detailseite**, am Telefon wie am
iPad wie am Mac. Nur in „Weiterschauen" spielt er ab: dort steht der
Titel, *weil* man ihn angefangen hat. Vorher war es umgekehrt, mit dem
Argument „wer eine Kachel antippt, weiss schon, was er sehen will". Im
Gebrauch stimmte das nicht — wer nachsehen wollte, wie viele Staffeln
eine Serie hat, bekam den Film. Ein Umweg von einem Schritt ist
ärgerlich; eine Wiedergabe, die man nicht wollte, ist schlimmer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-11 20:56:39 +02:00
ScarriffleandClaude Opus 5 ce5dc66047 Eine 500, die der Benutzer sieht und das Protokoll nicht
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>
2026-10-11 20:41:47 +02:00
ScarriffleandClaude Opus 5 a44d55e71c 163 Byte als Film verbucht, in einer Sekunde heruntergeladen
Muss der Server eine Datei umpacken, antwortet er mit **202** und einem
kurzen Text: noch nicht, komm wieder. Die Prüfung im Download verwarf
alles ausserhalb von 200 bis 299 — und 202 liegt mittendrin. Der
Antworttext lag danach als Film auf dem Gerät.

Jetzt ist 202 ein eigener Fall: der Eintrag geht zurück in die
Warteschlange und fragt alle fünfzehn Sekunden erneut, bis zu fünf
Minuten lang. Es ist nichts schiefgegangen, es ist nur noch nicht fertig
— und im Sekundentakt nachzufragen hiesse, dieselbe Datei hundertmal
anzufangen und wegzuwerfen.

Gezählt wird im Arbeitsspeicher, nicht im Eintrag: nach einem Neustart
beginnt das Zählen von vorn, und das ist richtig — der Server hat
inzwischen vermutlich fertig gepackt.

Dazu, aus derselben Meldung: bei 21∶9 auf einem Telefon reichte das Bild
hinter die Dynamic Island. Im Vollbild bleibt das so — wer es wählt, hat
sich fürs Beschneiden entschieden. Im normalen Modus nicht mehr: dort ist
der Anspruch gerade, dass nichts fehlt, und dann darf auch nichts
verdeckt sein.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-11 20:33:32 +02:00
ScarriffleandClaude Opus 5 c05004f586 Untertitel kamen an, der Ton nicht — sie sahen woanders nach
Die Vorwahl auf der Filmseite legt beides unter der Kennung des Films ab.
Beim Untertitel habe ich das beim Lesen berücksichtigt, beim Ton nicht:
dort stand `item.seriesID`, und die ist bei einem Film nichts. Die
Einstellung wurde gespeichert und nie wieder gelesen.

Jetzt gibt es eine Stelle, die sagt, wo die Vorwahl eines Titels liegt:
bei einer Folge die Serie, bei einem Film er selbst. Beides ist dieselbe
Frage auf der Ebene, die der Benutzer vor sich hatte, als er sie
beantwortet hat.

Dasselbe betraf das Merken einer von Hand gewählten Spur und die Anzeige
im Spurenblatt: bei einem Film merkte sich der Spieler nichts, weil die
Kennung fehlte. Jetzt lesen alle vier Stellen dieselbe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-11 13:58:44 +02:00
ScarriffleandClaude Opus 5 94458ef41c Fünf Knöpfe in einer Reihe wurden auf dem Telefon zu Säulen
In einem `HStack` teilen sich die Knöpfe die Breite. Abspielen, Von vorn,
Herunterladen und zwei Sprachknöpfe passten auf einem Telefon nicht
nebeneinander — heraus kamen Säulen aus einzelnen Buchstaben, senkrecht
untereinander. Lesbar war nichts mehr; angetippt hätte man trotzdem das
Richtige.

`Flusszeile` bricht um, statt zu quetschen: was nicht mehr hinpasst,
kommt in die nächste Zeile. Zwei Zeilen sind kein Mangel, sie sind, was
dort hinpasst — und ein Knopf, der auf schmalen Geräten verschwindet,
wäre eine zweite Oberfläche, die gepflegt werden will.

Kein `ViewThatFits`: das wählt zwischen fertigen Entwürfen, und die
müsste man für jede Zahl von Knöpfen von Hand hinschreiben. Hier wird
gerechnet.

Dazu bleibt die Beschriftung eines Sprachknopfs einzeilig. Umbrochen wird
die Reihe, nicht das Wort darin.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-10 23:20:57 +02:00
ScarriffleandClaude Opus 5 f85891aba4 Die Sprachwahl gehört neben den Knopf, der sie anwendet
Auf der Filmseite standen „Tonsprache für diesen Titel" und „Untertitel
für diesen Titel" als zwei breite Zeilen unter der Beschreibung — weit
weg von „Abspielen", und mit so viel Platz dazwischen, dass sie wie eine
Einstellung aussahen und nicht wie eine Wahl für diesen Film.

Jetzt stehen sie als „Ton: Deutsch" und „Untertitel: Keine" in der Reihe
mit Abspielen, Von vorn und Herunterladen.

Der Auswahlknopf hat dafür eine zweite Form bekommen statt eines zweiten
Typs: neben „Abspielen" ist kein Platz für eine Zeile mit Spalten, und
Beschriftung plus Wert in einem sagt dasselbe in einem Wort mehr. Auf der
Serienseite bleibt es bei der Zeile — dort stehen sie in einer Liste.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-10 22:16:12 +02:00
ScarriffleandClaude Opus 5 84612335ec Sprachwahl in der App — bei Serien und bei Filmen
Auf der Serienseite stand ein Menü für den Ton, gefüllt mit zwanzig
Sprachen, von denen die Serie zwei hatte. Für Untertitel gab es keines,
und auf der Filmseite überhaupt nichts.

Jetzt beides an beiden Orten, und zwar nur mit dem, was es wirklich gibt:

Bei einer **Serie** über den neuen Endpunkt — die Schnittmenge über alle
Folgen. Was dadurch wegfällt, steht als Satz darunter, sonst sähe eine
gekürzte Liste aus wie die ganze Wahrheit.

Bei einem **Film** aus den Spuren der Datei. Kein Serverweg nötig: dort
ist die Datei der Titel, und ihre Spuren stehen in der Antwort, die die
Seite ohnehin geholt hat.

Ton und Untertitel teilen sich einen Typ. Sie unterscheiden sich in genau
zwei Punkten — woher der Wert kommt und ob es „Keine" gibt; ein zweiter
Typ wäre dieselbe Datei mit zwei geänderten Zeilen. „Keine" gibt es nur
beim Untertitel: ein Film ohne Tonspur ist kein Wunsch, sondern ein
Fehler.

Die Untertitelsprache liegt je Titel, in einem Topf für Serien und Filme
— eine Filmkennung kollidiert mit keiner Serie, und zwei Wörterbücher für
dieselbe Frage liefen auseinander. Der Spieler liest sie jetzt: vorher
kannte er nur die allgemeine, und eine Serie im Original mit deutschen
Untertiteln liess sich nicht einstellen, ohne sie überall einzuschalten.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-10 21:56:15 +02:00
ScarriffleandClaude Opus 5 95d7eee8c3 Die Staffelwahl klebte am Untertitelknopf
Die Sprachwahl einer Serie steht zwischen dem Kopf und den Staffeln und
hatte als einziger Abschnitt dort keinen Abstand nach unten. Jetzt
denselben wie jeder andere.

Dazu: der Hinweissatz darunter verschwindet, wenn er leer ist. Ein leeres
Feld nimmt sonst Platz für einen Satz, den es nicht gibt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-10 21:45:33 +02:00
ScarriffleandClaude Opus 5 fc36bb7ddb Eine Serie, eine Sprachwahl — nicht zwanzig
Auf der Serienseite im Web stand bei jeder einzelnen Folge ein Knopf für
Ton und einer für Untertitel. Zwanzig Folgen hiessen zwanzigmal dieselbe
Frage, und am Ende wusste niemand, welche Antwort gilt. „Dieser Anime
läuft auf Japanisch mit deutschen Untertiteln" ist eine Aussage über die
Serie, nicht über Folge 7.

Die Wahl steht jetzt einmal oben bei den Angaben zur Serie und an den
Folgen gar nicht mehr. Beim Film bleibt sie, wo sie war — dort ist die
Datei der Titel.

Angeboten werden nur Sprachen, die in **jeder** Folge vorkommen; was
dadurch wegfällt, steht als Satz darunter. Gemerkt wird die Sprache, nicht
die Spurnummer: die unterscheidet sich von Folge zu Folge, und beim Start
wird sie für diese Datei nachgeschlagen.

Dazu die Staffeln. Sie standen alle untereinander — nach Folge 8 kam
wieder eine Folge 1, und wer zur dritten Staffel wollte, scrollte an
zwanzig Folgen vorbei. Die Überschrift ist jetzt das Menü. Bei einer
einzigen Staffel bleibt es bei der Überschrift: ein Menü, das nur sich
selbst anbietet, ist keine Wahl.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-10 21:43:17 +02:00
ScarriffleandClaude Opus 5 22be288fc3 Welche Sprachen eine Serie durchgehend führt
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>
2026-10-10 21:38:19 +02:00
ScarriffleandClaude Opus 5 2c51f639be 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>
2026-10-10 21:33:35 +02:00
ScarriffleandClaude Opus 5 ab43656f71 Nach Folge 8 kam wieder Folge 1, und nichts sagte warum
Der Server liefert die Folgen einer Serie am Stück. Auf der Seite stand
unter „Staffeln" eine durchgehende Liste, in der nach der achten Folge
wieder eine erste kam — die der zweiten Staffel, aber das stand nirgends.

Jetzt wählt ein Menü die Staffel, dort wo vorher nur „Folgen" stand. Bei
einer einzigen Staffel bleibt es beim Wort: ein Menü mit einem Eintrag
ist keine Wahl.

Kein `Picker`: diese Seite ist eine `ScrollView`, kein `Form`, und dort
hat ein `Picker` kein Aussehen, auf das man sich verlassen kann —
dieselbe Begründung wie beim Sprachmenü darüber.

„Alle auswählen" meint jetzt die gezeigte Staffel. Vorher hätte ein Griff
sechzehn Folgen heruntergeladen statt acht. Beim Wechsel der Staffel
fällt eine bestehende Auswahl weg: stehen zu lassen, was man nicht mehr
sieht, lädt unsichtbare Folgen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-10 21:27:58 +02:00
ScarriffleandClaude Opus 5 fa31adf3d1 Deutsch gewählt, Englisch gesehen
„Die erste ist die richtige" stand hier, und es stimmte — solange der
Server genau eine Textspur auslieferte, nämlich die bestellte. Seit die
Wiedergabe über einen Strom läuft, stehen alle Spuren in der
Abspielliste, und dann ist die erste schlicht die erste.

Gesucht wird jetzt die Spur, die zur Bestellung passt: erst über das
Sprachkürzel — `ger`, `deu` und `de` meinen dasselbe, dafür gibt es
`LanguageCode` längst —, bei gleicher Sprache über das Merkmal
„erzwungen", und zuletzt über den Namen. Findet sich keine, bleibt es bei
der ersten: bei einem umgerechneten Strom gibt es nur sie, und gar kein
Untertitel wäre schlechter als der eine.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-10 21:13:52 +02:00
ScarriffleandClaude Opus 5 5657cd9e4b Der Sprung ging ins Leere, und die Prüfung dagegen griff nie
Nach vorn ziehen, Regler springt zurück. Im Pausieren nicht — dort wuchs
die Abspielliste weiter und holte die Stelle irgendwann ein.

Der Spieler sollte einen Sprung ausserhalb des Stroms daran erkennen,
dass er hinter dessen Ende liegt. Das Ende stand in `streamEnd`, und das
wird gesetzt, wenn der Player eine Dauer meldet — was er bei einer
wachsenden Liste nie tut. `streamEnd` blieb also nichts, jeder Sprung
galt als innerhalb, AVPlayer bekam ein Ziel ohne Schnipsel und verwarf
es.

Gefragt wird jetzt der Vorrat: so weit, wie Daten da sind, springt der
Spieler selbst; darüber hinaus wird der Strom neu gebaut. Weiss niemand,
wie weit es reicht, wird ebenfalls neu gebaut — ein neuer Strom dauert
ein paar Sekunden, ein Sprung, der nichts tut, ist kaputt.

Entschieden wird vor dem Aufräumen: `seek` verwirft den Vorrat, und
genau an dem hängt die Antwort.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-10 21:01:35 +02:00
ScarriffleandClaude Opus 5 064f8f61af Eine wachsende Abspielliste hat keine Länge, also kam nie eine
Mein letzter Versuch griff an der falschen Stelle: er verbesserte, was
`durationResolved` einträgt — nur kommt dieses Ereignis bei einem Strom
aus Flaro gar nicht. Die Abspielliste wächst und trägt kein
`EXT-X-ENDLIST`; für AVPlayer ist die Dauer damit unbestimmt.

`duration` blieb also auf nichts, und die Ansicht rechnet dann mit einer
Sekunde (`model.duration ?? .seconds(1)`). Darum stand der Regler bei
1:50:07 am Anschlag, egal was noch kam.

Jetzt wird die Länge gesetzt, sobald der Strom steht — aus der Datei, die
der Spieler ohnehin in der Hand hat. Auf den Player zu warten hiesse, auf
etwas zu warten, das er nicht wissen kann.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-10 20:50:44 +02:00
ScarriffleandClaude Opus 5 693a6cc867 Cookie heisst Cookie
„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>
2026-10-10 20:42:29 +02:00
ScarriffleandClaude Opus 5 f789c40594 Der Vorrat holte unangemeldet, und das stand im Protokoll
20:32:40  Umpacken … Vorbereitung 3203 ms
    20:32:40  FlaroToken was challenged
    20:32:52  Umkodieren … 0 ms
    20:32:55  erstes Stück nach 2750 ms

Einmal je Wiedergabe eine abgewiesene Anfrage, gefolgt von zwölf
Sekunden Stille und einer zweiten Bestellung. Die zwölf Sekunden sind
AVPlayer, der auf etwas wartet, das nie kommt.

Der Vorrat, der dem Spieler die Schnipsel voraushält, benutzt eine
flüchtige URLSession — und die bringt ihren eigenen, leeren Keksspeicher
mit. Jede Anfrage von dort ging unangemeldet hinaus. Bei Jellyfin fiel
das nie auf, weil der Schlüssel dort in der Adresse steht; Flaro meldet
über ein Keks an. Und bisher kam der Vorrat bei Flaro nie zum Zug: er
springt nur bei einer Abspielliste an, und die App hat bis gestern eine
fertige Datei abgespielt.

Dass die zweite Bestellung lief, passt dazu: nach dem Fehlschlag gibt der
Vorrat auf, und der Spieler holt unmittelbar vom Server — mit dem Keks,
den er selbst hat.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-10 20:35:37 +02:00
ScarriffleandClaude Opus 5 347ddfea01 Das Protokoll kannte nur, was jemand ausdrücklich hineinschrieb
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>
2026-10-10 20:30:23 +02:00
ScarriffleandClaude Opus 5 19ec17da19 Zwölf Sekunden für eine Hülle um nichts
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>
2026-10-10 20:20:16 +02:00
ScarriffleandClaude Opus 5 a6b3217d6e Das Protokoll steht jetzt da, statt sich holen zu lassen
Vorher: eine Liste von Tagen, je Tag drei Knöpfe, und erst nach „Ansehen"
stand irgendwo Text. Wer wissen wollte, was gerade schiefgeht, brauchte
drei Handgriffe für eine Antwort, die auf der ersten Seite hätte stehen
können.

Jetzt ist „Protokolle" eine Liste von Ereignissen, wie man sie aus Home
Assistant kennt: der heutige Tag sofort und offen, neueste Zeile oben —
damit man zum Mitlesen nicht scrollen muss —, und alle vier Sekunden
kommt dazu, was dazugekommen ist. Fehler und Warnungen tragen die Farbe
am Rand statt im Text; eine ganze Zeile in Rot liest sich schlechter als
eine in Grau mit rotem Strich.

Die früheren Tage stehen darunter als Kacheln zum Herunterladen. Der
heutige nicht: er steht schon oben, und eine zweite Tür zum selben Raum
ist keine Hilfe.

Mehrzeiliges — ffmpeg schreibt gern vier Zeilen — hat keinen eigenen
Zeitstempel und gehört zur Zeile darüber. Es wird eingerückt gezeigt
statt verworfen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-10 20:15:50 +02:00
ScarriffleandClaude Opus 5 4db2a17e64 „Es dauert fünfzehn Sekunden" liess sich nicht beantworten
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>
2026-10-10 20:11:02 +02:00
ScarriffleandClaude Opus 5 855cfddf03 Der Regler stand am Ende, obwohl noch zwanzig Minuten kamen
Als Filmlänge nahm der Spieler `item.duration` und fiel sonst auf die
Länge des Stroms zurück. Beides ging daneben: `item.duration` kommt aus
der Liste, und dort stehen keine Dateien — bei einem Titel, der aus einer
Reihe geöffnet wurde, ist es nichts. Übrig blieb die Länge des Stroms,
und eine wachsende Abspielliste ist am Anfang ein paar Sekunden lang.

Die Datei weiss es genau. Sie steht im Spieler ohnehin, weil er aus ihr
abspielt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-10 20:04:47 +02:00
ScarriffleandClaude Opus 5 b88e58d5eb Das Gerät war da, die VA-Anzeige dahinter nicht
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>
2026-10-10 19:59:49 +02:00
ScarriffleandClaude Opus 5 34ade18ed6 „Die Einzelheiten stehen im Protokoll von heute" — standen sie nicht
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>
2026-10-10 19:51:41 +02:00
ScarriffleandClaude Opus 5 6ef84b47cf Den Weg der App einmal ganz abgehen
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>
2026-10-10 17:01:50 +02:00
ScarriffleandClaude Opus 5 6e503d63de „noch 35:37" wird zu „−35:37"
Das Wort stand da, weil die Zahl ganz rechts hinter den Menüs allein
nicht zu deuten war. Neben der verstrichenen Zeit tut das Minuszeichen
dasselbe mit einem Zeichen statt vier.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-10 14:41:41 +02:00
ScarriffleandClaude Opus 5 1bcf6f12e5 Die App wartete auf eine fertige Datei, statt zu streamen
Gleichzeitig im Web und in der App auf Abspielen getippt: das Web lief,
die App lud dreissig Sekunden. Das war keine Ladezeit, sondern die Zeit,
die der Server braucht, um den ganzen Film einmal durchzuschreiben.

`preparePlayback` kannte nur `/files/{id}/download`. Muss die Datei
umgepackt werden, fragte es im Sekundentakt nach, bis die vollständige
MP4 auf der Platte lag — Grenze zehn Minuten. Der Endpunkt, den das Web
benutzt, ist am Server längst da; die App wusste nur nichts davon.

Jetzt zuerst `/files/{id}/stream`. Das erste Stück ist beim Umpacken in
deutlich unter einer Sekunde da. Der alte Weg bleibt als Rückfall: er ist
langsam, kommt aber ohne Sitzung aus, und eine späte Wiedergabe ist
besser als keine.

**Der Strom beginnt dort, wo man weiterschauen wollte**, und zählt
trotzdem ab null. Ohne das mitzurechnen stünde bei „Fortsetzen ab 1:32"
eine Null auf der Uhr — und genau die ginge als Fortschritt an den
Server. Also: die Zeit der Engine plus Vorlauf ist die Zeit des Films,
und der Spieler springt nicht noch einmal dorthin, wo der Server schon
steht.

**Ein Sprung aus dem Strom heraus baut ihn neu auf.** Die Abspielliste
wächst von der Einstiegsstelle vorwärts; davor und dahinter gibt es
nichts, und ein Sprung ins Leere liesse den Spieler mit einem Kreisel
stehen. Innerhalb des Erzeugten springt er weiterhin selbst — dafür eine
neue Sitzung zu starten hiesse, Sekunden zu warten, um fünf Sekunden
zurückzugehen.

**Und die Sitzung wird geschlossen.** `endPlayback` war eine leere
Methode; das passte, solange es keine Sitzung gab. Mit dem Strom läuft
sonst ein ffmpeg weiter und schreibt den Film zu Ende, während längst
niemand mehr zusieht.

Die Laufzeit kommt weiter aus den Metadaten und nicht aus der Engine:
eine wachsende Liste meldet anfangs ein paar Sekunden, und als Filmlänge
genommen stünde der Regler sofort am Ende.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-10 14:22:18 +02:00
ScarriffleandClaude Opus 5 4a5576f939 Eine Hauptliste, damit auch AVPlayer die Untertitel findet
`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>
2026-10-10 14:17:47 +02:00
ScarriffleandClaude Opus 5 912969d829 Abspielen in die Mitte, die beiden Zeiten daneben
Die verstrichene Zeit stand hinter dem Lautstärkeregler — „1:51:01"
direkt neben einem Schieber liest sich wie dessen Beschriftung — und die
verbleibende ganz rechts hinter den Menüs. Zwei Angaben über dieselbe
Sache, so weit auseinander wie möglich.

Jetzt stehen sie dort, wo man ohnehin hinsieht: links und rechts vom
Knopf, den man drückt.

Drei Spalten statt drei Flex-Kinder, denn die Mitte soll die Mitte des
Bildes sein und nicht die Mitte dessen, was links und rechts übrig
bleibt. Mit `space-between` wanderte der Knopf, sobald rechts ein Menü
dazukam oder links die Lautstärke wegfiel.

Schmal weicht, was man auch anders bekommt: die Lautstärke liegt am
Telefon auf einer Taste, und die verbleibende Zeit lässt sich an der
Leiste ablesen. Der Knopf in der Mitte weicht nicht — dort sucht ihn der
Daumen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-10 14:12:50 +02:00
ScarriffleandClaude Opus 5 3dba736def Zwei Angaben über dieselbe Sache, so weit auseinander wie möglich
Die verstrichene Zeit stand hinter dem Lautstärkeregler, die verbleibende
ganz rechts hinter den Menüs. „1:48:20" direkt neben einem Schieber für
Lautstärke liest sich wie dessen Beschriftung — und die zweite Zahl, die
dazugehört, war einen halben Bildschirm entfernt.

Beide stehen jetzt links und rechts von dem Balken, über den sie Auskunft
geben. Schmal weicht die verbleibende Zeit; dort ist der Balken wichtiger
als die zweite Zahl.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-10 14:09:57 +02:00
ScarriffleandClaude Opus 5 dbd66592a6 Untertitel klebten am Bildrand, und ihr Zeilenabstand blieb gross
Zwei Fehler, die sich gegenseitig verstärkt haben.

**Der Zeilenabstand kam nicht mit.** `::cue { font-size }` ändert die
Schrift, nicht die Zeilenhöhe — die rechnet der Browser weiter aus seiner
eigenen Grösse. Bei 50 Prozent stand deshalb winziger Text mit dem
Abstand von grossem darunter; zwischen zwei Zeilen klaffte eine dritte.
`line-height` als Vielfaches der eigenen Grösse hält das Verhältnis.

**Und sie sassen ganz unten.** Ohne Angabe setzt der Browser jede
Einblendung an den unteren Rand des Videos. Dort liegt bei uns die
Bedienleiste — bei zwei Zeilen verschwand die untere darunter. Die
Unterkante sitzt jetzt bei neunzig Prozent der Videohöhe.

Gestellt wird das am `VTTCue`, nicht über CSS: `::cue` gestaltet den
Text, aber nicht seinen Platz. `lineAlign: "end"` ist dabei der Punkt —
damit meint `line` die Unterkante. Ohne ihn stünde ein zweizeiliger
Untertitel wieder tiefer als ein einzeiliger, also genau der Fall, um den
es ging.

Eine Einblendung, die ihre Stelle selbst mitbringt, behält sie:
Schilder und Aufschriften stehen dort, wo sie im Bild stehen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-10 14:08:07 +02:00
ScarriffleandClaude Opus 5 c5ab200846 Die Startseite holte ihre Reihen im Gänsemarsch
Die Weboberfläche holt alles, was auf der Startseite steht, in einem
einzigen `Promise.all` — und sie holt sogar mehr davon: Vorschlag und
saisonale Reihe kennt die App gar nicht. Trotzdem war sie spürbar
langsamer. Der Grund stand in einer Schleife:

    for mediathek in mediatheken {
        ... await client.send(...)
    }

Ein `await` im Schleifenrumpf heisst: Rundreise, warten, nächste
Rundreise. Bei vier Mediatheken viermal hintereinander, nachdem
„Fortsetzen" und „Als Nächstes" längst da waren. Dieselbe Schleife stand
in beiden Hintergründen, für Flaro wie für Jellyfin.

Jetzt laufen sie nebeneinander, in einer `TaskGroup`. Die Reihenfolge der
Mediatheken bleibt erhalten — nach den Antworten zu ordnen hiesse, dass
die Reihen nach jedem Start woanders stünden. Ein Actor steht dem nicht
im Weg: jede Anfrage gibt ihn beim Warten frei, genau wie die beiden
`async let` darüber es schon tun.

Nebenbei: ein Fehler bei einer Mediathek nimmt die anderen nicht mehr
mit. Eine Startseite mit drei von vier Reihen ist besser als keine.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-10 13:55:24 +02:00
ScarriffleandClaude Opus 5 8397e042b8 Der Publikumswert steht auf der Seite, also wird er von dort gelesen
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>
2026-10-09 16:51:22 +02:00
ScarriffleandClaude Opus 5 e05bbfc2cd Einen einzelnen Titel neu holen, statt achthundert
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>
2026-10-09 16:02:39 +02:00
ScarriffleandClaude Opus 5 18db89399d Ein leeres Jahrfeld ist eine Angabe, kein Versehen
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>
2026-10-09 16:02:29 +02:00
ScarriffleandClaude Opus 5 ac64b20a5b Jede Zahl sagt jetzt, was sie ist — und woher sie kommt
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>
2026-10-08 22:41:41 +02:00
ScarriffleandClaude Opus 5 a609b2d087 Ein Haken, der stehen blieb, als die Rechnung sich änderte
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>
2026-10-08 22:41:22 +02:00
ScarriffleandClaude Opus 5 5dfc22bdce „Hinterlegt" war keine Auskunft, und die Übersicht keine Ablage
**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>
2026-10-08 22:11:55 +02:00
ScarriffleandClaude Opus 5 e066f5fe87 Eine Reihe, die nur zu ihrer Zeit dasteht
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>
2026-10-08 21:45:09 +02:00
ScarriffleandClaude Opus 5 c13d1e78d2 Eine Mediathek für alles, was in keinem Katalog steht
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>
2026-10-08 21:36:06 +02:00
ScarriffleandClaude Opus 5 af2b844d65 IMDb, Rotten Tomatoes und Metacritic neben der TMDB-Wertung
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>
2026-10-08 21:26:58 +02:00
ScarriffleandClaude Opus 5 97edb49564 Eine zweite Bildquelle, weil TMDB nicht alles hat
„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>
2026-10-08 21:18:27 +02:00
ScarriffleandClaude Opus 5 2bd13f0f19 Breitbild sass oben statt mittig
Ein 21:9-Film war in der Breite richtig und in der Höhe nicht: der schwarze
Rand lag ganz unten statt gleichmässig oben und unten. Im Fenster fällt das
kaum auf, im Vollbild sofort.

Gemessen an einem 2560×1080-Film in einem 1024×768-Fenster:

    Hülle        768 hoch
    Videokasten  459 hoch, bei oben 0

Woran es lag: die Hülle ist ein Raster mit `place-items: center`. Damit
wird das Videoelement nicht gedehnt, und sein `height: 100%` löst sich
gegen eine Zeile auf, die ihre Höhe wiederum aus dem Element nimmt — die
Prozentangabe hatte nichts, woran sie sich halten konnte. Herausgekommen
ist ein Kasten, der weder die volle Höhe hatte noch mittig sass.

Jetzt liegt das Videoelement im grossen Spieler absolut über der ganzen
Hülle. `object-fit: contain` rechnet damit gegen den vollen Bildschirm und
legt den Rand von selbst zu gleichen Teilen oben und unten — in beiden
Richtungen, nicht nur in der einen, die vorher stimmte.

Nachher dieselbe Messung: Kasten 1024 × 768, oben 0.

Die kleine Leiste bleibt unberührt — sie positioniert dasselbe Element
ohnehin absolut und mit eigenen Massen; deshalb das `:not(.pl-klein)`.
Nachgemessen: 128 × 70, `object-fit: cover`, unverändert.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-08 21:07:31 +02:00
ScarriffleandClaude Opus 5 144470a413 Im Browser gibt es keine Datei mehr, nur den Strom
Auf der Detailseite stand ein Knopf „Herunterladen", der die Datei als MP4
herausgab. Das ist etwas anderes, als einen Strom zu zeigen: wer eine Datei
bekommt, hat sie und kann sie weitergeben — und das soll dieser Server
nicht anbieten.

Weg sind der Knopf, das Bereitstellen davor und die beiden Stellen in der
Schnittstelle, die nur dafür da waren.

**Der Endpunkt bleibt.** Die Apps holen sich eine Mitnahme über
`/api/v1/files/{id}/download`, und dort ist sie an ein Gerät gebunden statt
an einen Ordner. Das heisst aber auch: wer angemeldet ist und die Adresse
kennt, kommt weiterhin an die Datei. Soll das zu — etwa als Recht je
Benutzer oder als Schalter für den ganzen Server —, ist das ein eigener
Schritt und keine Zeile in dieser Änderung.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-08 21:07:20 +02:00
ScarriffleandClaude Opus 5 532473b1f9 Ein Plan für das, was an keinem Aufruf hängt
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>
2026-10-08 21:02:42 +02:00
ScarriffleandClaude Opus 5 c4af182d31 Der Kratzplatz räumt sich auf
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>
2026-10-08 19:07:52 +02:00
ScarriffleandClaude Opus 5 1b4784f1db Zwei Fassungen dürfen gewollt sein, und die Startseite darf nicht warten
**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>
2026-10-08 17:56:41 +02:00
ScarriffleandClaude Opus 5 2809821c9b Vier Fehler, und bei zweien log der Kommentar daneben
**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>
2026-10-08 17:46:46 +02:00
ScarriffleandClaude Opus 5 c6df9fdab0 Die Schriftgrösse war am falschen Ende grosszügig
60 bis 300 Prozent waren es, und gebraucht wird der Weg nach unten: schon
die Vorgabe des Browsers ist auf einem grossen Bildschirm eher zu gross als
zu klein, und 300 Prozent füllt das halbe Bild. Jetzt 30 bis 120 Prozent,
mit unveränderter 100-Prozent-Marke.

Dazu Fünferschritte statt Zehner. Auf einer Spanne von 90 statt 240 Prozent
wiegt ein Zehnerschritt doppelt so schwer; bei 35 Prozent ist der
Unterschied zu 45 Prozent kein Feinschliff mehr, sondern ein Sprung.

Ein gemerkter Wert von früher wird beim Lesen gekappt — geprüft: 190 im
Speicher ergibt Regler 120 und `font-size: 120%`. Und 30 Prozent ist am
Bild nachgesehen klein, aber lesbar; als Untergrenze taugt es.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-08 06:59:15 +02:00
ScarriffleandClaude Opus 5 963e5a74ae Untertitel so gross und so bunt, wie man sie braucht
Die Voreinstellung des Browsers ist weisse Schrift auf schwarzem Kasten, in
einer Grösse, die er aus der Höhe des Videos rechnet. Auf einem grossen
Bildschirm aus drei Metern Abstand ist das zu klein, und wer schlecht sieht,
braucht es anders.

Einstellbar sind Grösse (60 bis 300 Prozent in Zehnerschritten),
Schriftfarbe (Weiss, Hellgelb, Hellblau, Schwarz) und Hintergrund
(Halbdunkel, Schwarz, Grau, Ohne).

Gestaltet wird mit `::cue`, nicht mit eigenen Kästen. Der Browser zeichnet
die Einblendungen selbst — Position, Umbruch, Zeilenabstand und die
Platzangaben aus der Datei. Das nachzubauen hiesse, all das noch einmal zu
schreiben und dabei schlechter zu werden.

Gemessen an einem Video mit echter Spur, Bild für Bild: 200 Prozent in
Hellgelb ohne Kasten und 140 Prozent in Hellblau auf Grau kommen beide so
heraus, wie sie gemeint sind.

Ohne Kasten setzt es von selbst einen Schatten. Weisse Schrift in einer
hellen Szene ist sonst weg, und das wäre eine Einstellung, die sich selbst
unbrauchbar macht. Mit Kasten wäre derselbe Schatten nur Unschärfe.

Die Einstellung sitzt im Untertitelmenü, abgetrennt unter den Spuren — wie
der Tonversatz im Tonmenü. Ob die Schrift gross genug ist, sieht man nur an
einem laufenden Film, in diesem Licht, aus diesem Abstand; wer dafür die
Wiedergabe verlassen muss, stellt zweimal falsch ein, bevor er einmal
richtig liegt.

Gemerkt wird im Browser, nicht am Konto: wie gross Schrift sein muss, hängt
am Bildschirm und am Abstand davor. Dasselbe Konto am Telefon und am
Fernseher will zwei verschiedene Grössen.

Farbfelder statt einer Liste mit Farbnamen — „Hellblau" muss man lesen und
sich vorstellen, ein Feld sieht man. Das gewählte trägt einen Ring aussen
herum und keinen Haken darin: ein Haken wäre auf Weiss unsichtbar und auf
Schwarz erst recht. „Ohne Hintergrund" zeigt ein Karomuster, weil ein
leeres Feld nach Weiss aussähe.

Gelesen wird nur, was bekannt ist. Ein fremder Eintrag im Speicher darf
nicht zu beliebigem CSS in der Seite werden.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-08 06:43:26 +02:00