ScarriffleandClaude Opus 5 903bfe5f63 Der Player weiß jetzt, wie es ihm geht
Bisher gab es über eine laufende Wiedergabe zwei Zahlen — gemessene
Bitrate und verworfene Bilder —, und die standen nur zur Anzeige da. Die
Frage „trägt die Leitung das gerade" war nicht zu beantworten, weder für
den Nutzer noch für die App.

**Die wichtigste neue Zahl ist `variantCount`.** Sie kommt aus
`AVAsset.variants`, also vom Parser des Players, und beantwortet die
Frage, ob es überhaupt etwas zum Umschalten gibt. Steht dort 1, greift
jedes Nachregeln ins Leere — und genau das ist heute der Fall, weil die
App bei beiden Servern eine Playlist mit einer Stufe bestellt.

Dazu der Pufferstand aus `loadedTimeRanges`: der einzige echte
Frühwarner. Schrumpft er schneller, als die Zeit vergeht, kommt weniger
herein als hinausgeht — zehn bis zwanzig Sekunden, bevor das Bild steht.
Alles andere meldet sich erst danach.

**Verworfene Bilder werden jetzt über alle Protokolleinträge summiert.**
Das Zugriffsprotokoll legt bei jedem Stufenwechsel einen neuen an, dessen
Zähler bei null beginnt; wer nur `last` liest, hält eine Wiedergabe für
makellos, die schon zwanzigmal Bilder verworfen hat.

**Der Wachhund war blind für den häufigsten Fall.** Vierzig Sekunden am
Stück ohne Daten brechen ab — aber er setzte sich bei jedem kurzen
Erholen zurück, und zehn Rückler à dreißig Sekunden lösten ihn nie aus.
`StallLedger` zählt daneben mit, wie viel Zeit im gleitenden Fenster
fürs Warten draufging. Es bricht nichts ab, es stellt nur fest — und ist
ein reiner Typ ohne Uhr und ohne AVFoundation, damit sich eine Folge von
Rücklern als Zahlenreihe hinschreiben und das Urteil prüfen lässt.
Sieben Tests, darunter genau der Fall, an dem der alte Wachhund
vorbeisah.

Gezählt wird nur der Weg vom Spielen ins Warten. Das anfängliche Puffern
ist der Start, kein Rückler; würde es mitgezählt, hätte jede Wiedergabe
eine Vorstrafe.

Und zwei Kommentare sind berichtigt. Sie behaupteten, AVPlayer wechsle
die Bitrate von selbst und das sei „die zugesagte automatische
Qualitätsanpassung". Umschalten kann er nur zwischen Varianten, die in
der Liste stehen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-27 14:59:29 +02:00
2026-09-15 20:33:24 +02:00
2026-09-15 21:00:44 +02:00

Flixy

Ein nativer Plex-Client für iPhone und iPad — dunkel, mit ausfahrbarer Seitenleiste und Offline-Downloads.

Der Name ist ein Platzhalter. Siehe Flixy/Branding.swift.

Stand

Alle sechs Phasen sind gebaut. Anmeldung, Blättern und Wiedergabe laufen am echten Server; der Rest ist unterschiedlich weit geprüft.

Der Fahrplan steht in docs/superpowers/specs/2026-09-11-flixy-design.md.

Phase Inhalt Stand
0 Projektgerüst, Design-Tokens, Navigation ✅
1 Anmeldung, Bibliothek, DirectPlay ✅ am Server geprüft, iPhone und iPad
2 Transcoding, adaptive Bitrate, Spuren, Sync ✅ Wiedergabe geprüft; Transcoding selbst noch nicht erzwungen
3 Download-Engine und Offline-Wiedergabe ⚠️ Downloads starten, Offline-Wiedergabe noch nicht bestätigt
4 Download-Intelligenz ⚠️ komprimierte Downloads ungeprüft
5 Feinschliff und Personalisierung ✅ gebaut, teilweise geprüft
6 Live-TV mit EPG ⚠️ Empfänger werden gefunden, Sender noch nicht bestätigt

„Ungeprüft" heißt: die Logik ist von Tests abgedeckt, an einem echten Bestand aber noch nicht gesehen.

Was der Praxistest gelehrt hat

Drei Fehler, die kein Test gefunden hätte, weil sie in Annahmen über fremde Bibliotheken steckten — sie stehen als Warnung im jeweiligen Quelltext:

  • VLCKit ruft seinen Delegaten vom eigenen Thread, nicht über die Hauptwarteschlange. MainActor.assumeIsolated brach die App beim Drücken auf Play hart ab.
  • app.plex.tv/auth verknüpft den Code nicht, wenn man im Browser schon bei Plex angemeldet ist — es springt auf watch.plex.tv weiter. plex.tv/link tut es zuverlässig.
  • URLSessionDownloadTask gilt auch bei 404 als erfolgreich und legt die Fehlerseite als Datei ab.

Aufbau

Die Xcode-Projektdatei wird aus project.yml erzeugt und ist nicht eingecheckt — das erspart Konflikte in der pbxproj.

xcodegen generate
open Flixy.xcodeproj

Wichtig: Xcode vorher beenden.

xcodegen generate schreibt project.pbxproj neu. Ein geöffnetes Xcode liest das nicht nach — es hält seinen eigenen Stand im Speicher und überschreibt die frisch erzeugte Datei beim nächsten Sichern wieder. Neue Dateien fehlen dann im Build, ohne dass irgendwo ein Fehler auftaucht.

Das ist der Preis dafür, die Projektdatei aus Git herauszuhalten. Immer wenn Dateien dazukommen oder project.yml sich ändert: Xcode mit ⌘Q beenden, xcodegen generate, neu öffnen.

Ob der Build aktuell ist, steht in der App unter Einstellungen → Diagnose als Build-Datum.

Der Code liegt zum größten Teil in lokalen Packages unter Packages/FlixyKit. Das App-Target enthält nur Oberfläche und Navigation.

Modul Aufgabe Hängt ab von
PlexKit Anmeldung, Endpunkte, Modelle. Kein SwiftUI. —
FlixyDesign Farben, Typografie, wiederkehrende Bausteine. —
FlixyPlayback Wiedergabe-Abstraktion und die Entscheidung DirectPlay/Transcode. PlexKit
FlixyDownloads Warteschlange, Hintergrundübertragung, Offline-Bestand. PlexKit

Warum zwei Player

iOS spielt weder Matroska-Dateien noch DTS- oder TrueHD-Ton — und genau das liegt in typischen Plex-Beständen. PlaybackDecider prüft deshalb pro Datei, ob AVPlayer sie nativ öffnen kann (dann gibt es Bild-in-Bild und AirPlay gratis), und schickt sie sonst an VLCKit.

Ab Phase 2 kommt der dritte Weg dazu: Server-Transcoding. Dafür ist genau eine Funktion zu erweitern — PlaybackDecider.decide(media:part:policy:). Die Oberfläche bleibt unberührt.

Tests

Die Logik-Tests laufen ohne Simulator, deshalb in Sekunden:

cd Packages/FlixyKit && swift test

Die wichtigste Testdatei ist PlaybackDeciderTests — sie hält fest, welche Datei auf welchem Weg landet, und schlägt an, sobald jemand DeviceCapabilities anfasst.

Die Antwort-Vorlagen unter Tests/PlexKitTests/Fixtures sind von Hand geschrieben und gehören durch echte Mitschnitte ersetzt, sobald ein Server erreichbar ist — siehe das README dort.

Die Oberfläche wird von Hand im Simulator geprüft.

Was noch am echten Server zu prüfen ist

  1. Komprimierte Downloads. Sie laufen über den Universal-Transcoder mit protocol=http statt über die umständliche Sync-API. Offen: ob Plex eine Content-Length mitschickt (ohne sie gibt es keinen Fortschrittsbalken und kein Fortsetzen) und ob die Datei durchsuchbar ist. Original bleibt solange Voreinstellung. Siehe transcodedDownloadURL in PlexServer+Playback.swift. Ein Fehlschlag erscheint immerhin als solcher — die Plausibilitätsprüfung in DownloadCoordinator.implausibility verwirft Fehlerseiten und abgebrochene Übertragungen, statt sie als fertig zu verbuchen.
  2. Live-TV: die Senderliste. Dass die Empfänger unter Dvr gefunden werden, ist geprüft. Ob /livetv/dvrs/{id}/channels die Sender liefert, nicht. Die Ansicht unterscheidet beide Fälle: „Kein Empfänger eingerichtet" gegen „Keine Sender" — daran ist ablesbar, wo es hakt.
  3. App-Größe. Die iOS-Slice von VLCKit ist unstripped 239 MB. Was davon im fertigen Archiv übrig bleibt, ist noch nicht gemessen.
  4. Große Dateien im Hintergrund. Das Verhalten mit einem 40-GB-Remux — vor allem Pausieren und Fortsetzen — ist nur gegen Testdaten geprüft.

Voraussetzungen

  • Xcode 26 oder neuer
  • iOS 26.0 auf dem Zielgerät
  • XcodeGen (brew install xcodegen)

Live-TV untersuchen, ohne zu bauen

Die Live-TV-Schnittstelle von Plex ist nirgends beschrieben, und jede Vermutung darüber ging bisher als TestFlight-Build hinaus und kam als Screenshot zurück. Irgendwann war das Upload-Kontingent des Tages aufgebraucht — für eine Frage, die keine Oberfläche braucht.

cd Packages/FlixyKit && swift run FlixyProbe

Beim ersten Lauf zeigt das Werkzeug einen Code für plex.tv/link; danach liegt der Token im Schlüsselbund dieses Rechners. Es sucht den Server, liest Senderliste und Programm und probiert das Einschalten durch — mit derselben Logik wie die App, nur in Sekunden statt in einem halben Tag.

Ausgegeben werden Pfade, Kennungen und die Antworten des Servers. Keine vollständigen Adressen, also auch kein Token.

S
Description
No description provided
Readme GPL-2.0
13 MiB
Languages
C# 43.1%
Swift 38.4%
TypeScript 12.5%
CSS 2.5%
HTML 2.2%
Other 1.3%