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>
186 lines
16 KiB
Markdown
186 lines
16 KiB
Markdown
# 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.
|