Files
Flaro/docs/superpowers/specs/2026-09-14-offene-punkte-und-weg-nach-vorn.md
T
ScarriffleandClaude Opus 5 ecfa454136 Das Web spielt jetzt ab — und sagt, wenn es nicht geht
Es fehlte der erste Schritt. Die Stream-Adresse wurde selbst
zusammengebaut, und damit fehlten PlaySessionId, die ausgehandelte
MediaSourceId und die Adresse, die der Server eigentlich liefert. Im
Swift-Code steht seit jeher, warum das nicht geht: "eine selbst
zusammengebaute Adresse wäre eine Vermutung darüber."

Jetzt vier Schritte wie in der App: PlaybackInfo mit Geräteprofil, die
Quelle aus der Antwort wählen, die Adresse des Servers nehmen, die
Sitzungskennung mitführen.

Vor allem aber sind Fehler nicht mehr unsichtbar. Vorher endete jeder
Fehlschlag in einem stummen schwarzen Kasten — ein 401 sah aus wie ein
404 und wie ein Netzproblem. Geprüft: Ablehnung, fehlende Quelle und
nicht abspielbares Format nennen jetzt alle drei Ross und Reiter.

Serien und Staffeln haben keinen Abspielknopf mehr. Sie sind Ordner;
/Videos/{serienId} zeigt auf nichts. Wer über eine Serienkachel getestet
hat, konnte gar nichts starten.

Dazu Fortschrittsmeldung an /Sessions/Playing — ohne die erscheint der
Titel auf dem Server nie als "läuft gerade", und "Weiterschauen" bliebe
im Web für immer leer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 17:29:17 +02:00

16 KiB

Flixy — offene Punkte und der Weg nach vorn

Kontext

Flixy gibt es als App für iPhone, iPad und Mac und seit heute als Weboberfläche für Rechner ohne App — Windows vor allem. In den letzten Stunden sind Live-TV für Jellyfin, ein randloser Aufmacher, ein Webfrontend und der Abgleich der Einstellungen entstanden.

Vieles davon ist gebaut, aber nicht belegt. Der Anlass für diesen Plan ist doppelt: erstens eine ehrliche Liste dessen, was offen ist, zweitens die vier Stücke, die als Nächstes anstehen — die Weboberfläche brauchbar machen, eine Apple-TV-App, eine Admin-Oberfläche, und das Gefühl der Bedienung angleichen.

Der Maßstab für den Funktionsumfang ist JellyTV (App Store), ausdrücklich als Anregung, nicht als Vorlage. Flixy kann dabei etwas, das JellyTV nicht kann: Plex als Server.

Ausdrücklich nicht gewollt: iCloud-Sync der Einstellungen, Vision Pro, Push bei neuen Inhalten, Jellyseerr.


Teil A — Was offen ist

A1. Gebaut, aber nie an echten Daten geprüft

Alles hiervon kompiliert und ist committet; keines davon hat je einen echten Server gesehen.

Was Wo Warum ungeprüft
Live-TV für Jellyfin — Sender, Programmführer, Einschalten JellyfinBackend+LiveTV.swift Kein Simulator-Zugang zu einem angemeldeten Server
Aufnahmen und Timer JellyfinBackend+Recordings.swift dito
Plex-Live-TV nach dem Umbau PlexBackend+LiveTV.swift Das größte Risiko: hier hat vorher etwas funktioniert, und die Schicht darunter wurde ausgetauscht
Cover-Nachladen (Wiederholung bei Zeitablauf) ArtworkLoader.swift Trat nur auf großen Fenstern mit vielen Kacheln auf
Karussell-Auswahl alle 6 Stunden HomeView.swift Wechselt erst nach Ablauf eines Fensters
Kachelgröße und Aufmacherhöhe HomeLayoutSettings.swift Neu, nie in der laufenden App gesehen
Randloser Aufmacher, Insel blendet aus RootView.swift, TopChrome.swift Vom Nutzer als „sieht ok aus" bestätigt, nicht im Einzelnen geprüft

A2. Bewusst liegen gelassen

  • Programmführer ohne Scroll-Melder (ProgramGuide.swift): Die Insel bleibt dort beim Scrollen stehen, während sie überall sonst ausblendet. Verschachtelte Scroll-Struktur mit eigener onScrollGeometryChange-Logik — das Risiko war zu hoch für einen blinden Eingriff.
  • Doppelte Chrome-Zeile auf Detailseiten: „Staffel 2" steht klein in der Leiste und groß im Bild. Entscheidung war, das zu bewerten, wenn der schwarze Balken weg ist. Er ist weg.

A3. Halbfertig im Arbeitsverzeichnis

  • Phase 4 des Einstellungs-Abgleichs (web/frontend/index.html, uncommittet): Wiedergabe (Tempo, Spulweite, Qualität) und Sprachen als Einstellungen, dazu Spulknöpfe und Tempo-Wahl im Player. Geschrieben, nicht getestet.

A4. Vom Nutzer angekündigt, noch nicht da

  • Screenshots von JellyTV für den Gefühls-Abgleich.
  • Die eine Sache, die am iPhone-Bildschirmfoto aufgefallen war — nie genannt.

Teil B — Was gebaut werden soll

Reihenfolge: Web brauchbar → Admin → Apple TV. Eine Oberfläche, die nichts kann, nützt niemandem.


B1. Das Web muss abspielen können

Befund. Das Web ruft /Items/{id}/PlaybackInfo kein einziges Mal auf. Es baut die Stream-Adresse in web/frontend/index.html:1395-1406 selbst zusammen und springt damit direkt zu einem geratenen Schritt. Im Swift-Code steht wörtlich, warum das nicht geht (JellyfinBackend+Playback.swift:47-49):

„Die Adresse kommt vom Server, nicht von uns. Er weiß, welche Spuren er kopieren kann und welche er neu rechnen muss; eine selbst zusammengebaute Adresse wäre eine Vermutung darüber."

Es fehlen dadurch: PlaySessionId, die ausgehandelte MediaSourceId, die TranscodingUrl des Servers und die DeviceId am Stream. Der Stream-Abruf geht an api() vorbei und trägt keine einzige Kopfzeile — der Server sieht eine Anfrage ohne Gerät und ohne Sitzung.

Die Reihenfolge, die die App einhält und das Web nachziehen muss:

  1. POST /Items/{id}/PlaybackInfo mit DeviceProfile → Vorbild: playbackInfo in JellyfinBackend+Playback.swift:116-177
  2. Quelle aus MediaSources wählen (nicht item.Id annehmen — bei mehreren Fassungen falsch)
  3. TranscodingUrl des Servers relativ zur Basisadresse auflösen
  4. PlaySessionId mitführen — für Fortschritt und für DELETE /Videos/ActiveEncodings am Ende

Weitere Funde, die mitbehoben werden:

  • Fehler sind unsichtbar. Kein error-Ereignis am <video>, kein Hls.Events.ERROR. Ein 401, ein CORS-Block oder gemischtes HTTP/HTTPS enden in einem stummen schwarzen Player. Das kommt zuerst dran — ohne Fehleranzeige ist jede weitere Suche blind.
  • Serien und Staffeln bekommen einen Abspielknopf (index.html:1250, 1337-1356). /Videos/{seriesId}/master.m3u8 zeigt auf einen Ordner. Wer über eine Serienkachel getestet hat, konnte gar nichts starten.
  • Meine Phase-4-Arbeit ist an dieser Stelle falsch: AudioLanguage/SubtitleLanguage sind keine Parameter dieses Endpunkts. Jellyfin erwartet AudioStreamIndex/SubtitleStreamIndex — Indizes, die erst PlaybackInfo liefert. Ebenso gehört MaxStreamingBitrate ins DeviceProfile, nicht an die URL. Das wird beim Umbau berichtigt, nicht übernommen.
  • Kein /Sessions/Playing. Der Titel erscheint auf dem Server nie als „läuft gerade", Fortschritt wird nie gespeichert. Damit ist „Weiterschauen" im Web wertlos.

B2. Das Web wird ein richtiges Projekt

Entschieden: Frontend-Projekt plus schlanker eigener Server.

Der Server ist nicht Zierde, sondern löst zwei Dinge, an denen die Einzeldatei scheitert: Er reicht die Jellyfin-Anfragen weiter (damit ist CORS ein für alle Mal erledigt, auch beim Streamen), und der Token bleibt bei ihm statt im Browser-Speicher zu liegen.

  • Vite + TypeScript, Quelltext in Module zerlegt statt 1500 Zeilen in einer Datei
  • Node-Server davor, der /api/* an den Medienserver weiterreicht
  • Zum Betreiben auf einem Container; Node ist auf dem Entwicklungsrechner vorhanden (v26), Docker nicht — das Bauen läuft lokal, das Ausliefern als fertige Dateien
  • Der bestehende Entwurf wandert hinüber, er wird nicht weggeworfen: Gestaltung, Aufmacher, Insel-Umzug und Einstellungen sind bereits erprobt

B3. Admin-Oberfläche

Befund. BackendCapability.administration existiert seit Langem, wird in JellyfinBackend.swift:21-29 gesetzt — und nirgends abgefragt. Eine tote Fähigkeit: berechnet, gespeichert, überlebt Neustarts, niemand sieht sie an. Es gibt keine Admin-View, keinen Admin-Endpunkt, keinen admin.*-Übersetzungsschlüssel.

Nur Jellyfin. Plex hat kein Gegenstück zu JellyfinAccount — PlexAuthenticator liefert einen Token-String, kein Konto, keine Rechte. Verwaltung wäre über plex.tv grundsätzlich denkbar, ist aber nirgends angelegt. Genau dafür ist BackendCapability da: bei Plex erscheint der Bereich schlicht nicht, so wie der dritte Schalter im Live-TV dort fehlt.

Aufbau, nach dem Vorbild von Live-TV. Ein eigenes AdministrationBackend-Protokoll in MediaKit, außerhalb von MediaBackend — die Begründung steht wörtlich in LiveTV.swift:320-325 und gilt hier noch stärker, weil Plex es nie erfüllen wird. Neue Modelltypen: ServerUser, ServerTask, ActivityEntry, ActiveSession.

Zu ergänzen:

  • JellyfinBackend+Administration.swift — neue Datei, Extension wie bei Live-TV. Die Endpunkte fehlen alle: /Users, /ScheduledTasks, /Sessions, /System/ActivityLog/Entries, /Library/VirtualFolders.
  • JellyfinAuthenticator.swift:177-183 — die Policy dekodiert heute nur IsAdministrator; alles andere wird verworfen. Achtung: JellyfinAccount.init hat vier Parameter und wird an mehreren Stellen gerufen (FlixySession.swift:521, 588).
  • FlixySession — supportsAdministration und administration, exakt nach dem Muster der beiden Live-TV-Zeilen (Z. 168-176).
  • SettingsView.swift — bedingte Section zwischen Server und „Darstellung": derselbe Gegenstand wie Server (nicht das Gerät), aber ein anderer Umgang damit. Nicht in ServerSettingsView hinein — dort steht heute ausschließlich Beobachtetes.
  • SettingsChrome.swift:58-66 — SettingsPalette fehlt ein admin-Eintrag.

Web: index.html:502-506 wirft User.Policy beim Anmelden weg. Ohne diese eine Zeile ist dort gar keine Entscheidung möglich, ob jemand Admin ist.

Vorbild für schreibende Server-Aktionen ist LibraryRow in ServerSettingsView.swift:72-133 — heute die einzige der ganzen App, mit ihrer Zustandsmaschine und dem ehrlichen Kommentar, warum dort kein Fortschrittsbalken steht.

Umfang, in dieser Reihenfolge — nach Risiko, nicht nach Aufwand:

  1. Wer schaut gerade (GET /Sessions). Nur lesend, nichts kann kaputtgehen, und im Alltag das, was man am häufigsten wissen will. Gut geeignet, um Protokoll und Oberfläche einmal durchzuspielen, bevor etwas Schreibendes dazukommt.
  2. Server-Aufgaben (GET /ScheduledTasks, POST /ScheduledTasks/Running/{id}). Schreibend, aber folgenlos — eine angestoßene Aufgabe kann man laufen lassen. Schließt unmittelbar an das an, was „Mediathek aktualisieren" schon tut.
  3. Nutzer verwalten (/Users, /Users/{id}/Policy, /Users/{id}/Password). Der größte Brocken: Jellyfins Rechteobjekt hat gut dreißig Felder. Nicht alle davon gehören in eine Oberfläche — die Auswahl wird beim Bauen begründet, nicht stillschweigend getroffen.
  4. Mediatheken verwalten (/Library/VirtualFolders). Zuletzt, weil hier ein Fehlgriff eine Mediathek löscht. Jede zerstörerische Aktion bekommt eine Rückfrage, die den Namen nennt — wie bei den Aufnahmen: „Wirklich löschen?" allein beantwortet nicht, was.

B4. Apple TV

Die Trennung zahlt sich aus. Rund die Hälfte des Codes trägt sich hinüber, die andere Hälfte ist die Oberfläche:

Schicht Zeilen Zustand auf tvOS
MediaKit, PlexKit, JellyfinKit, FlixyPlayback 9.972 ~95 % unverändert, drei kleine Korrekturen
Flixy/Session/ 2.050 größtenteils übernehmbar
FlixyDesign 1.479 Farben und Rechnerei ja, Bauteile brauchen Fokuszustände
FlixyVLC 262 ja — VLCKit hat echte tvOS-Slices
FlixyDownloads + Widget 1.384 entfällt
Flixy/Views/ 9.133 praktisch komplett neu

Die erfreuliche Überraschung: VLCKit liefert tvos-arm64 mit, das Upstream-Paket deklariert tvOS, und VLCPlayerEngine.swift benutzt nichts, was dort fehlt. Eine Zeile im Manifest. Damit könnte die TV-App MKV, DTS und TrueHD direkt abspielen — besser als die Mac-App, wo der Server rechnen muss.

Zwei stille Fehler, die heute niemand bemerkt (und die auch iOS betreffen, sobald man sie sieht):

  • AVPlayerEngine.swift:320,330 — AudioSession.activate() steht unter #if os(iOS), obwohl AVAudioSession auf tvOS existiert. Ohne Korrektur würde die Kategorie dort stillschweigend nie gesetzt.
  • Sechs Abfragen von horizontalSizeClass liefern auf tvOS immer .regular. Nichts bricht — es läuft einfach immer der iPad-Pfad, obwohl das nicht mehr die gemeinte Frage ist.

Was echte Arbeit ist:

  1. Die Oberfläche, wegen des Eingabemodells. Nicht fehlende APIs sind das Problem, sondern Fokus statt Berührung. Die ehrlichste Kennzahl: @FocusState kommt im ganzen Projekt genau einmal vor. Jede Kachel braucht auf tvOS einen Fokuszustand — das ist dort keine Zutat, sondern die Grundmechanik.
  2. Der Player zerfällt in zwei Pfade. AVPlayerViewController bringt auf tvOS Transport, Spurwahl und Info-Tafel fertig mit — aber nur für AVPlayer. VLC braucht eine eigene fokusgesteuerte Bedienung. Das ist der unangenehmste Einzelposten.
  3. PlatformShims.swift ist binär gedacht („iOS oder macOS"). UIPasteboard ist auf tvOS ein harter Übersetzungsfehler, die übrigen sieben Helfer meinen im #else-Zweig stillschweigend das Falsche. Muss auf drei Plattformen umgestellt werden, bevor irgendeine Ansicht sie benutzt.
  4. Eigenes Target mit eigenem Quellverzeichnis. Der Kniff, mit dem FlixyMac sich Flixy/ teilt, funktioniert nur, weil Catalyst buchstäblich der iOS-Bau ist. tvOS ist eine eigene Plattform.

Was entfällt: Downloads (der Speicherort ist auf tvOS genau falsch herum, und ein Apple TV hat 64 GB), Mini-Player (kein Ziehen, kein Pinch), AirPlay-Knopf (das Gerät ist das Ziel), Bildschirmdrehung.

Was schon vorbereitet ist: Plex braucht dort den PIN-Fluss statt ASWebAuthenticationSession — PlexPin.swift und session.showsManualCode existieren bereits, nur der Bildschirm fehlt.


Teil C — Wie geprüft wird

Der wunde Punkt dieser ganzen Sitzung: Vieles ist gebaut und nichts davon an echten Daten belegt. Das soll sich nicht fortsetzen.

Was ich selbst prüfen kann:

  • Übersetzt es? (xcodebuild für iOS und Mac Catalyst, swift test für die Pakete — 37 Tests)
  • Web: Oberfläche und Verhalten im Browser, mit abgefangenen Server-Antworten. So sind Karussellpunkte, Insel-Umzug, Reihenfolge der Reihen und die Kontrastrechnung belegt worden.
  • iOS-Simulator: Bildschirmfotos, Tippen, Wischen — aber nur bis zum Anmeldebildschirm, solange dort kein Konto hinterlegt ist.

Was ich nicht selbst prüfen kann:

  • Alles, was einen angemeldeten Server braucht. Das ist der Grund, warum Teil A1 so lang ist.
  • macOS-Fenster (keine Werkzeuge dafür), JellyTV (App Store, nicht quelloffen).

Der Weg, der das auflöst: Eine einmalige Anmeldung im iPad-Simulator — bei Plex oder Jellyfin. Danach kann ich Startseite, Live-TV, Aufnahmen, Kachelgröße und Aufmacher selbst ansehen und nachbessern, statt jede Runde an den Nutzer zurückzugeben. Das würde Teil A1 in einem Zug abräumen.

Für das Web: Sobald es einen eigenen Server hat, lässt sich dieser gegen einen erfundenen Jellyfin richten — dann ist auch die Wiedergabekette prüfbar, ohne dass ein echter Medienserver nötig ist.


Teil D — Reihenfolge

Vor allem anderen: die Fehleranzeige im Web-Player. Ein halber Tag Suche nach „warum geht das nicht", wenn ein 401 schon die Antwort gewesen wäre, ist verlorener Tag. Das kommt zuerst, weil alles Weitere daran hängt.

  1. Web spielt ab — Fehleranzeige, dann PlaybackInfo-Kette, dann Fortschrittsmeldung. Kein Abspielknopf mehr auf Serien und Staffeln.
  2. Web wird ein Projekt — Vite plus Server, der bestehende Entwurf wandert hinüber. Phase 4 wird dabei berichtigt, nicht übernommen.
  3. Admin — in vier Schritten nach Risiko: wer schaut gerade → Server-Aufgaben → Nutzer → Mediatheken. In App und Web.
  4. Apple TV — Pakete tvOS-fähig machen (inklusive der zwei stillen Fehler und PlatformShims), dann die Oberfläche.

Das Gefühl der Bedienung (JellyTV als Anregung) läuft nebenher, sobald die Screenshots da sind — es ist Feinschliff an Vorhandenem, kein eigener Bauabschnitt.


Was ich anders sehe als der Auftrag

Zwei Anmerkungen, die ich nicht unterschlagen will:

Teil A1 wächst schneller, als er abgetragen wird. Sieben Dinge sind gebaut und keines davon belegt — darunter der Umbau von Plex-Live-TV, wo vorher etwas funktionierte. Vier weitere Bauabschnitte davorzusetzen vergrößert den Stapel. Mein Vorschlag wäre, eine einzige Anmeldung im iPad-Simulator dazwischenzuschieben: danach kann ich A1 in einem Zug selbst abräumen, statt jede Runde zurückzugeben. Das kostet dich fünf Minuten und mich eine Sitzung.

Apple TV ist kein viertes Stück, sondern ein eigenes Projekt. Rund 9.100 Zeilen Oberfläche neu, ein Player in zwei Pfaden, und ein Eingabemodell, mit dem dieses Projekt bisher einmal in Berührung kam. Das ist machbar und lohnt sich — aber es sollte nicht in einem Zug mit „Admin-Oberfläche" geplant werden, sondern eine eigene Spec bekommen, wenn es soweit ist.