Commit Graph
430 Commits
Author SHA1 Message Date
ScarriffleandClaude Opus 5 9bcdd239d7 Entwurf: den Abspann eines Films im Bild finden
Der Tonabgleich kann bei Filmen grundsätzlich nichts finden — er erkennt
einen Abspann daran, dass er sich über die Folgen einer Staffel
wiederholt, und ein Film hat nichts, womit er sich vergleichen liesse.

Der Entwurf steht auf zwei Messungen statt auf Vermutungen: an einem
erzeugten Film liegt die mittlere Helligkeit in der Handlung bei 125 und
im Abspann bei 18, mit einer scharfen Kante genau am Übergang. Und das
Dekodieren der letzten Minuten kostet 2,89 Sekunden, wenn man jedes
zweite Bild nimmt, aber 0,24, wenn man sich auf Schlüsselbilder
beschränkt.

Dazu steht drin, wo das Verfahren danebenliegt: ein Abspann über
laufendem Bild hat keine dunkle Strecke und bekommt deshalb keinen Knopf.
Das ist die richtige Antwort — ein geratener Knopf schnitte Handlung weg.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-12 22:12:15 +02:00
ScarriffleandClaude Opus 5 fd79fb4022 „No such file or directory" über Dateien, die da waren
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>
2026-10-12 22:09:56 +02:00
ScarriffleandClaude Opus 5 2c535e6f47 Drei Wege, die über ein Neuladen führten
**In „Fortsetzen" startet ein Tipp.** Dort steht ein Titel, *weil* man ihn
angefangen hat, und die Stelle steht daneben — der Umweg über die
Detailseite stellte eine Frage, die schon beantwortet war. Überall sonst
bleibt es beim Weg auf die Seite: wer nachsehen will, wie viele Staffeln
eine Serie hat, soll nicht den Film bekommen.

**Ein Haken nimmt die Kachel mit.** In „Fortsetzen" und „Als Nächstes"
steht, was offen ist. Eine abgehakte Folge ist nicht mehr offen — sie mit
einem Haken stehen zu lassen sah aus, als hätte das Abhaken nicht
gewirkt. Dasselbe für „Aus Fortsetzen entfernen": in der Reihe selbst
heisst entfernen genau das.

**Und ein getauschtes Bild erreicht den Aufmacher.** Wer auf der
Startseite ein Plakat wechselt, sieht denselben Titel gleich darüber in
voller Breite — dort stand bis zum nächsten Neuladen das alte. Die
Kachel frischt sich selbst auf; der Aufmacher steht ganz woanders und
erfuhr nichts davon. Jetzt meldet der Bilderdialog es der ganzen Seite,
und wer es angeht, hört zu.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-12 21:54:50 +02:00
ScarriffleandClaude Opus 5 adca700c4a Marken suchen, ohne dass jemand danach fragt
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>
2026-10-12 21:52:24 +02:00
ScarriffleandClaude Opus 5 1d70be0f99 Fünf Aufmacher je Titel, und der Abspann zählt als Schluss
**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>
2026-10-12 21:44:43 +02:00
ScarriffleandClaude Opus 5 8cf2118723 Vorspann und Abspann — erkannt, abgelegt, überspringbar
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>
2026-10-12 21:28:36 +02:00
ScarriffleandClaude Opus 5 acba683c94 Eine Folge weiter, eine zurück — ohne den Spieler zu verlassen
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>
2026-10-12 21:11:09 +02:00
ScarriffleandClaude Opus 5 bcbe5557d7 Der Kern des Vorspann-Abgleichs — ohne Anschluss, aber geprüft
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>
2026-10-12 20:56:37 +02:00
ScarriffleandClaude Opus 5 bfe263d495 Vierundzwanzig Bilder, und der Weg dorthin fehlte sowieso
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>
2026-10-12 20:49:37 +02:00
ScarriffleandClaude Opus 5 78e347c58d Zwei Zeilen Untertitel, die zweite halb unter dem Bildrand
Gemessen am Bildschirmfoto: zwischen den Zeilen lagen 71 Pixel bei 18
Pixel Texthöhe — fast das Vierfache dessen, was `line-height: 1.25`
ergibt. Und die zweite Zeile war mitten im Buchstaben abgeschnitten.

Beides derselbe Grund. Der bisherige Weg ging über die Norm:
`lineAlign = "end"` macht `line: 90` zur **Unter**kante, und dann steht
auch ein zweizeiliger Untertitel vollständig über dem Rand. Chrome und
Firefox halten sich daran — nachgestellt und abgelichtet. WebKit nicht:
dort ist es die Oberkante, der Kasten wächst nach unten aus dem Bild,
und `line-height` aus `::cue` bleibt ebenfalls wirkungslos.

Jetzt wird dort gar nichts mehr an den Einblendungen verstellt.
Stattdessen rückt der Kasten ab, in den der Browser sie setzt — ein
Element, das WebKit selbst erfunden hat und das es dort also gibt. Eine
Verschiebung des ganzen Kastens kann nichts abschneiden, egal wie viele
Zeilen eine Einblendung hat.

Wer den Kasten nicht kennt — Firefox —, bekommt weiter den Weg über die
Norm. Erkannt wird das daran, ob die Regel im Stilblatt stehen bleibt,
nicht an der Browserkennung.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-12 20:42:16 +02:00
ScarriffleandClaude Opus 5 5a0f2bf07f Eine Prüfung dafür, dass eine Datei auch ein Bild sein darf
Der Lader gab bei jeder `file:`-Adresse `nil` zurück, und nichts hätte
das gemeldet. Drei Zeilen Prüfung: ein PNG auf der Platte kommt an, eine
fehlende Datei nicht, und Text ist kein Bild.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-12 20:28:14 +02:00
ScarriffleandClaude Opus 5 79d7d04583 Ein Download sah aus wie eine Videodatei, nicht wie ein Film
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>
2026-10-12 20:20:27 +02:00
ScarriffleandClaude Opus 5 8f0568cb6c Ohne Server stand man vor der Tür, mit dem Film in der Tasche
Fiel der Server aus, endete der Start bei einem Fehlerbildschirm mit
zwei Knöpfen: „Erneut versuchen" und „Abmelden". Beide nützen im
Flugzeug nichts — und dahinter lagen die Dateien, die man genau für
diesen Fall heruntergeladen hatte.

Jetzt geht die App trotzdem auf: Token und Konto liegen auf dem Gerät,
die Mediathekenliste auch, und die Startseite kommt neuerdings aus
demselben Speicher. Oben die Downloads, darunter der Stand vom letzten
Mal, ausgegraut, mit einem Satz, der sagt warum. Die Detailseite nimmt
den Titel vom Gerät, wenn der Server schweigt, und der Abspielknopf
bleibt an, weil die Datei ja da ist.

Und was man offline geschaut hat, zählt: Fortschritt, den der Server
nicht annimmt, wird zurückgelegt und nachgereicht, sobald er wieder
antwortet. Dafür sagt `reportProgress` jetzt, ob die Meldung ankam.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-12 19:41:22 +02:00
ScarriffleandClaude Opus 5 77593105de Heruntergeladenes lag auf dem Gerät und war nirgends zu sehen
Downloads gab es nur im Downloads-Bereich. Wer wissen wollte, was im
Flugzeug läuft, musste zwei Listen vergleichen: die Mediathek und die
Downloads.

Jetzt sind sie eine Reihe auf der Startseite, tragen in jeder Kachel
eine Marke, und in der Mediathek gibt es „Nur Heruntergeladene" — der
Filter fragt den Server gar nicht erst, weil der nicht weiss, was auf
diesem Gerät liegt.

Dabei kam heraus, dass Downloads nie ein Bild mitbrachten: der
Dateiname dafür stand im Modell, gesetzt hat ihn niemand. Neue holen es
mit, alten wird es beim nächsten Start nachgereicht.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-12 19:28:45 +02:00
ScarriffleandClaude Opus 5 fb8ad1c831 Der Stand lag daneben und wurde trotzdem weggeworfen
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>
2026-10-12 19:13:09 +02:00
ScarriffleandClaude Opus 5 fa8d1a97e2 Vom Aufmacher war vor allem Grau zu sehen
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>
2026-10-12 19:03:45 +02:00
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