Im Web stand am Abspann „Nächste Folge", in der App nur „Abspann
überspringen". Der Server war es nicht: beide fragen denselben Endpunkt,
und der ordnet nach Staffel und dann nach Nummer — er findet also auch
die erste Folge der nächsten Staffel.
Es war `nachbar`. Die Stelle baute sich einen frischen `JSONDecoder()`,
und der kennt das Datumsformat des Servers nicht. Solange die
Nachbarfolge weder `publishAt` noch `lastPlayedAt` mitbrachte, ging es
gut — mit eingeschalteter Prüfung bringt sie ein `publishAt`. Das `try?`
schluckte den Fehler, und von aussen sah es aus, als gäbe es keine
nächste Folge: kein Knopf, kein Hinweis, nichts.
In `FlaroClient` steht seit je ein eingerichteter Dekoder, ausdrücklich
nicht privat, „damit die Prüfungen denselben Dekoder benutzen" — und
direkt darunter die Warnung, dass ein frischer an genau diesem Format
scheitert. Diese eine Stelle las sie nicht.
Vier Prüfungen dazu, darunter die mit Datumsangaben: mit dem alten
Dekoder ist sie rot, mit dem neuen grün. Gegengeprüft, nicht behauptet.
Die übrigen frischen Dekoder im Quelltext lesen nur, was dieselbe App
vorher selbst geschrieben hat — Zwischenspeicher, Abzüge, Offline-
Angaben. Die laufen rund und bleiben.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
„Intro überspringen" lag im Web in der Bedienleiste und verschwand mit
ihr. Am Fernseher sieht man die Leiste gar nicht — der Knopf war also
da und trotzdem unsichtbar. Jetzt liegt er als eigene Schicht über dem
Bild und bleibt stehen, solange die Marke läuft.
**Im Abspann ist „überspringen" ohnehin die falsche Frage.** Was man
dort will, ist die nächste Folge; ans Ende dieser zu springen bringt
niemanden weiter. Gibt es eine, heisst der Knopf jetzt so — im Web wie
in der App, und in der App nicht mehr erst, wenn alles durchgelaufen
ist. Bei einem Film gibt es nichts danach: dort heisst „Abspann
überspringen" jetzt wirklich Schluss, statt auf ein stehendes Bild zu
spulen und den Spieler offen zu lassen.
Dazu der Titel über dem Spieler. Dort stand bei einer Folge „S1 E6" und
bei einer aus der Detailseite gestarteten sogar nur „Wiedergabe". Auf
einer Kachel ist die Kurzform richtig, dort zählt jeder Buchstabe; über
dem Spieler steht eine Zeile in einer leeren Fläche. Jetzt steht da
„Küstenwache · Staffel 1 · Folge 6 · Der Nebel kommt". In der App hiess
es „Episode" statt „Folge" — das eine Wort, das sonst nirgends steht.
Und drei Prüfungen für die Marken auf dem Weg in die App: aus einer
Serverantwort mit Vorspann und Abspann werden zwei Marken an der
richtigen Stelle. Die Übersetzung war nicht der Grund, warum der Knopf
dort fehlte.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
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>
„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>
`FlaroServerKit` steht neben `JellyfinKit`, nicht darin — genau auf dem
Steckplatz, den Plex geräumt hat. Dass `MediaBackend` zwei Server zugleich
trägt, war schon einmal bewiesen; die Naht hat gehalten, und keine einzige
Ansicht musste dafür neu geschrieben werden.
`ServerKind` ist zurück. Es wurde beim Entfernen von Plex gestrichen, weil
ein Aufzählungstyp mit einem Fall ein umständliches Bool ist. Jetzt gibt es
wieder zwei, und es ist wieder eine Unterscheidung — einer zur Zeit, nicht
beide nebeneinander.
**Flaro steht oben auf dem Anmeldebildschirm.** Nicht aus Eitelkeit: dort
bringt ein Download alle Tonspuren mit und der Fortschritt wird ab der
ersten Sekunde gemerkt. Wer Jellyfin benutzt, sucht gezielt danach — wer neu
anfängt, soll nicht in der schwächeren Hälfte landen, weil ihr Knopf
auffälliger war. Am Fernseher steht bei beiden der sechsstellige Code voran.
---
**Der Zugang geht als Cookie, nicht in der Adresse.** Jellyfin hängt sein
Token an jede Bildadresse, weil eine Bildansicht keine Kopfzeilen schickt;
es steht damit in jedem Zugriffsprotokoll und jedem weitergegebenen Link.
Flaro tut das nicht, und deshalb legt der Client sein Token selbst im
gemeinsamen Keksspeicher ab — auch beim Wiederaufnehmen aus dem
Schlüsselbund, wo gar keine Anmeldung stattfindet und der Server es nie
setzen könnte.
`URLSession` schickt es von allein mit; **AVFoundation nicht**. Der
Systemplayer bekommt die Kekse jetzt ausdrücklich. Serverneutral: wo keine
liegen, ist die Liste leer.
**`BackendCapability.allTracksDownload`** statt einer Spurwahl, die ins
Leere führt. Die Frage „welche Tonspur?" gibt es nur, weil Jellyfins
Transcoder eine je Sitzung abbildet. Bei Flaro führen beide Antworten zum
selben Ergebnis — also wird nicht gefragt, und das Umpacken mit einer
einzigen Spur entfällt gleich mit.
**Die Adressraterei wandert nach `MediaKit`.** Sechzig Zeilen sorgfältig
begründeter Reihenfolge — IP-Adressen sprechen selten HTTPS, Domänennamen
fast immer — unterscheiden sich zwischen beiden Servern nur im Standardport.
Zweimal hingeschrieben wäre die eine Fassung beim nächsten Sonderfall
nachgezogen worden und die andere nicht. Jellyfins Prüfungen laufen
unverändert gegen die verschobene Fassung; das ist der Beweis.
**Nebenbei zwei Altlasten:** `player.jellyfinPending` war unerreichbar — der
Player zeigt diese Meldung nur, wenn gar kein Server da ist, dann aber nie
die mit Jellyfin. Und `signIn.jellyfin.userName` hiess „Benutzername"; der
Schlüssel heisst jetzt so neutral wie der Text.
---
**Der Fehler, den erst der Simulator zeigte:** „Stelle wird
wiederhergestellt", 0:00, für immer.
Muss der Server umpacken, antwortet er auf den Download mit 202 und fängt
an. AVPlayer bekommt darauf kein Video, sondern eine leere Antwort — und
wartet, unbegrenzt, ohne Fehler. Die Weboberfläche fragt in einer Schleife
nach; `preparePlayback` tat es nicht. Jetzt wartet es, mit Obergrenze und
einer Begründung, wenn sie reisst. Mit Prüfung, die zwei Runden erzwingt.
413 Tests grün. Im Simulator gegen den echten Server nachgewiesen: Adresse
eintippen, Server gefunden, angemeldet, Mediathek mit Plakaten, Neustart
ohne erneute Anmeldung — und eine MKV abgespielt, die der Server dafür erst
umpacken musste. Danach stand auf dem Server: Matrix, 2,0 s von 2,0 s,
gesehen, ein Durchlauf.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Upload zu App Store Connect scheiterte am Namen. „Halo" ist eine
eingetragene Marke von Microsoft; im App Store heisst zwar keine App exakt
so, aber reservierte und markenrechtlich belegte Namen tauchen in keiner
Suche auf. Der einzige verlässliche Test war die Eingabe dort, und die ging
daneben.
**Der Name wurde diesmal vorher geprüft.** Zwanzig Kandidaten gegen den
deutschen und amerikanischen App Store, auf exakte Treffer und auf ähnliche
Namen. „Kavo" wäre dieselbe Wand gewesen — exakt belegt in beiden Stores, in
Deutschland von KaVo Dental GmbH. „Flare" ist mit 42 ähnlichen Apps je Store
der am dichtesten besetzte Name der ganzen Runde. **Flaro** ist in beiden
frei und liest sich als eigenes Wort, nicht als Flare mit Tippfehler.
Das bleibt eine Store-Suche und keine Markenrecherche — reservierte Namen
ohne veröffentlichte App sieht sie nicht. Aber es ist mehr Prüfung, als beim
letzten Mal stattfand.
---
2160 Textvorkommen in 273 Dateien, 49 Pfade. Betroffen ist diesmal auch der
Server: `Flaro.Api`, `Flaro.Core`, `Flaro.Data`, `Flaro.Media`,
`Flaro.Metadata`, `FlaroContext`, `FlaroPaths`, `flaro.db`, die Variablen
`FLARO_DATA`, `FLARO_CACHE`, `FLARO_TRANSCODE`, `FLARO_WRITE_SIDECARS`, und
das Image.
**Vor dem Ersetzen geprüft**, ob „halo" irgendwo als Teil eines anderen
Wortes steckt — und ob es im TMDB-Token vorkommt. Tat es nicht; ein blindes
Ersetzen hätte den sonst zerlegt.
`docs/superpowers/specs/` bleibt wie beim letzten Mal unangetastet: datierte
Entwurfsdokumente sind ein Protokoll getroffener Entscheidungen. Das
Server-Dokument hat stattdessen einen Nachtrag bekommen, damit es niemanden
in die Irre führt.
**Die Bundle-ID wandert mit**, zum zweiten Mal an einem Tag:
`com.scarriffleservices.flaro`. Wer die vorige Fassung installiert hat, hat
jetzt zwei Karteileichen auf dem Gerät — beide löschen, dann ist Ruhe.
308 Server-Tests grün, 368 im Swift-Paket, null Warnungen. iOS und tvOS
bauen, das Image baut, und ein frischer Container läuft durch Einrichtung
und Anmeldung.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>