Legt das Fundament für den Plex-Client an: - XcodeGen-Projekt nach dem Muster von Onyx (com.scarriffleservices, strict concurrency, Projektdatei nicht eingecheckt) - FlixyKit mit vier Modulen: PlexKit, FlixyDesign, FlixyPlayback, FlixyDownloads. macOS steht als Plattform mit drin, damit die Logik-Tests ohne Simulator laufen. - FlixyDesign: dunkle Farb-Tokens, Schriftskala, Poster- und Querformat-Kacheln, Aufmacher-Bild. Die Akzentfarbe fließt über die Umgebung und wird per WCAG-Rechnung lesbar gehalten. - PlexKit: Kopfzeilen-Identität, Transport-Protokoll, Fehlerarten und die Medienmodelle samt nachsichtigem Decoder. - FlixyPlayback: VideoPlayerEngine-Protokoll und PlaybackDecider — die eine Funktion, an der Phase 2 das Transcoding andockt. - Ausfahrbare Seitenleiste auf dem iPhone, Split-View auf dem iPad. - 45 Tests, alle grün. VLCKit 4.0.0-a24 ist vorab geprüft: baut über SPM unter Swift 6 mit strict concurrency. Die iOS-Slice ist mit 239 MB allerdings gewichtig, deshalb bekommt sie in Phase 1 ein eigenes Target. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
10 KiB
Flixy — nativer Plex-Client für iPhone & iPad
Kontext
Der Ordner /Users/scarriffle/Projects_Coding/Flixy ist leer. Es entsteht ein nativer Plex-Client, weil die aktuelle offizielle Plex-App gestalterisch nicht gefällt — gewünscht ist etwas, das näher an der alten Plex-Handy-App liegt, sich aber wertig anfühlt wie eine große Streaming-App: ein-/ausblendbare Seitenleiste für die Mediatheken, ein Hero-geführter Startbildschirm, durchgängig dunkel, mit einstellbarer Akzentfarbe.
Zwei Subsysteme tragen das Projekt: eine Wiedergabe-Schicht, die zunächst nur DirectPlay macht, aber so gebaut ist, dass Server-Transcoding und adaptive Bitrate reine Ergänzungen sind — und eine Download-Engine für Offline-Nutzung von einzelnen Folgen bis zu ganzen Serien.
Vorbild und Referenz im eigenen Bestand: Nook (../ABS-Client) — derselbe Aufbau für Audiobookshelf, inkl. Hintergrund-Downloads, Live Activities und DE/EN-Lokalisierung. Onyx (../Onyx) liefert das Muster für XcodeGen + lokale Packages.
Der Name „Flixy" ist ein Platzhalter und soll später leicht änderbar sein.
Entscheidungen (mit dem Nutzer abgestimmt)
| Thema | Entscheidung |
|---|---|
| Plattformen | iPhone + iPad (kein macOS, kein tvOS) |
| Mindest-iOS | 26.0 |
| Anmeldung | plex.tv PIN-Flow, ein Server, ein Benutzer |
| Player | Hybrid — AVPlayer wenn nativ möglich, sonst VLCKit |
| Medien | Filme, Serien, Live-TV mit EPG (kein Musik/Fotos) |
| Plex Pass | vorhanden |
| Struktur | XcodeGen + lokale Swift Packages |
| Persistenz | SwiftData |
| Tests | Swift Testing für Logik, UI manuell im Simulator |
| Sprachen | Deutsch + Englisch (String Catalogs) |
| Design | nur Dark Mode, Layout A (Hero + Querformat-Reihen), Overlay-Drawer-Seitenleiste |
| Akzentfarbe | kuratierte Palette + eigene Farbe mit Kontrastkorrektur |
Architektur
Vier lokale Swift Packages unter Packages/, das App-Target enthält nur noch SwiftUI-Views und Navigation. Abhängigkeiten fließen strikt in eine Richtung:
App ──► FlixyDesign (Farb-Tokens, Poster/Hero-Komponenten, Platzhalter — keine Abhängigkeiten)
│
├──────► PlexKit (HTTP, Auth, Modelle, Endpunkte — kein UI, kein SwiftUI)
│
├──────► FlixyPlayback ──► PlexKit
│
└──────► FlixyDownloads ─► PlexKit
PlexKit — PlexClient (Actor) kapselt Header (X-Plex-Token, X-Plex-Client-Identifier, Produkt/Version), Verbindungsauswahl und Dekodierung. Endpunkte als kleine, einzeln testbare Typen; Modelle (PlexItem, PlexMedia, PlexPart, PlexStream, PlexMarker) als Codable gegen echte Server-Antworten, die als JSON-Fixtures im Test-Bundle liegen.
FlixyPlayback — das Herzstück der Nachrüstbarkeit:
protocol VideoPlayerEngine: AnyObject {
func load(_ source: PlaybackSource) async throws
func play(); func pause(); func seek(to: Duration)
var state: AsyncStream<PlaybackState> { get }
var tracks: [MediaTrack] { get }
func select(_ track: MediaTrack)
var supportsPictureInPicture: Bool { get }
}
AVPlayerEngine und VLCPlayerEngine implementieren es. Darüber entscheidet PlaybackDecider anhand von Streams + Gerätefähigkeiten (+ ab Phase 2 Netzwerk) zwischen .directPlay(engine:) und .transcode(url:maxBitrate:). In Phase 1 gibt der Decider nur DirectPlay-Fälle zurück — Phase 2 erweitert genau diese eine Funktion, ohne die View-Schicht anzufassen. Das ist die konkrete Form von „Transcoding vorbereiten".
FlixyDownloads — URLSession mit Background-Konfiguration, ein DownloadCoordinator-Actor als Zustandsautomat (queued → downloading → paused → completed/failed), SwiftData-Modelle DownloadItem und OfflineMetadata (Titel, Beschreibung, Marker, Spuren, Cover-Pfad — alles, was die Detailseite und der Player offline brauchen).
Branding-Isolation — App-Name, Bundle-ID, Icon und Standard-Akzentfarbe liegen ausschließlich in project.yml und Branding.swift. Umbenennen später = zwei Dateien.
Phasen
Phase 0 — Fundament
project.yml(XcodeGen), Packages-Gerüst,.gitignore, Git-Repo initialisieren- Dieses Dokument als Spec nach
docs/superpowers/specs/2026-09-11-flixy-design.mdins Repo übernehmen und committen - App-Target:
.preferredColorScheme(.dark), String Catalogs DE/EN,Branding.swift FlixyDesign: Farb-Tokens (Surface-Stufen, Text, Akzent als austauschbarer Wert), Typo-Skala,PosterCard,LandscapeCard,HeroBanner, Shimmer-Platzhalter- Test-Setup mit Swift Testing in allen Packages
Phase 1 — Anmeldung, Bibliothek, DirectPlay
Ziel: App ist real benutzbar.
- Auth:
POST /api/v2/pins→ Browser-Auth → Polling → Token im Keychain. Server überGET /api/v2/resourcesauflisten; Verbindungen (lokal / plex.direct / Relay) parallel anpingen, schnellste erreichbare gewinnt und wird gemerkt. - Seitenleiste: Overlay-Drawer mit Abdunklung, per Wischgeste und Hamburger; auf dem iPad dauerhaft im Split-View, einklappbar. Einträge: Start, je Bibliothek, Downloads, Einstellungen.
- Startseite (Layout A): Hero-Karussell oben, darunter Reihen aus
/hubs— Weiterschauen, Als Nächstes, Zuletzt hinzugefügt je Bibliothek, Watchlist, Sammlungen. - Bibliothek: Grid und Liste umschaltbar, Filter (Genre, Jahr, Bewertung, Auflösung), Sortierung, „Nur Ungesehene"-Schalter, Paginierung über
X-Plex-Container-Start/Size. - Detailseiten: Film und Serie → Staffel → Folge, Besetzung mit Porträts (tippbar), Ähnliche Titel, aufklappbare technische Infos (Codec, Bitrate, HDR, Tonspuren, Größe), Versionsauswahl bei mehreren Media-Einträgen.
- Suche: Live-Ergebnisse über alle Bibliotheken, gruppiert, letzte Suchen gemerkt.
- Player:
PlaybackDecider+ beide Engines, DirectPlay über{Part.key}?X-Plex-Token=…, eigenes Chrome (Scrubber, ±10 s, Titel/Folgenanzeige), PiP wenn AVPlayer aktiv ist.
Schlüsseldateien: Packages/PlexKit/Sources/{PlexClient,Auth,Endpoints,Models}, Packages/FlixyPlayback/Sources/{PlaybackDecider,AVPlayerEngine,VLCPlayerEngine}, App/Views/{Sidebar,Home,Library,Detail,Player}
Phase 2 — Transcoding, adaptive Bitrate, Spuren, Sync
PlaybackDeciderum Transcode-Zweig erweitern:/video/:/transcode/universal/decisionfragen, bei Bedarfstart.m3u8mit Client-Profil undmaxVideoBitratebauen- Echtes ABR über den HLS-Stream, dazu Obergrenzen-Regler getrennt für WLAN und Mobilfunk
- Untertitel- und Tonspurwahl (sidecar bei DirectPlay, eingebrannt bei Transcode), Untertitel-Styling, bevorzugte Sprache merken
- Intro-/Abspann-Marker aus
includeMarkers=1: Skip-Button, optionales Auto-Skip, Autoplay der nächsten Folge - Fortschritt an den Server melden (
/:/timeline), gesehen/ungesehen markieren (/:/scrobble,/:/unscrobble), ungesendete Updates offline puffern und nachreichen
Phase 3 — Download-Engine & Offline
- Hintergrund-
URLSession, Zustandsautomat, Wiederaufnahme nach App-Neustart - Einzeldownload (Film/Folge) und Batch (Staffel/Serie) mit Gesamtfortschritt und Abbruch
- Download in Originalqualität über
?download=1 - Offline-Metadaten und Cover mit sichern, Offline-Wiedergabe aus der lokalen Datei
- Downloads-Ansicht in der Seitenleiste: laufend, offline verfügbar, Belegung, pausieren/fortsetzen/löschen
- Nur-WLAN-Schutz, Live Activity mit Fortschritt in Sperrbildschirm und Dynamic Island
Phase 4 — Download-Intelligenz
- Qualitätswahl pro Download (Original oder server-komprimiert) — vorab ein Spike: Plex-Sync-API (
/sync/items) gegen HLS-Segment-Download abwägen - Einstellbares Speicherlimit mit Warnung
- Automatisches Löschen nach dem Ansehen (global oder pro Serie)
- Regel-Engine „halte N ungesehene Folgen offline" — lädt beim Weiterschauen nach
Phase 5 — Feinschliff & Personalisierung
- Akzentfarben-Einstellung: kuratierte Kacheln plus eigene Farbe mit automatischer Helligkeits-/Kontrastkorrektur gegen die dunklen Flächen
- Startseiten-Reihen ein-/ausblenden und per Drag umsortieren
- Watchlist verwalten, Sterne-Bewertung, Bibliotheks-Scan anstoßen (hinter Bestätigung)
- Animationen, leere Zustände, Fehlerzustände, iPad-Feinschliff, Lokalisierungs-Durchgang
Phase 6 — Live-TV & EPG
- Spike zuerst: DVR-/EPG-Endpunkte sind bei Plex am schlechtesten dokumentiert —
/media/providers, Sendergitter,/livetv/sessions - Sender-Liste, EPG-Raster mit Zeitachse, Umschalten auf laufende Sendung (immer transcodiert, baut auf Phase 2 auf)
Später / bewusst nicht geplant
Mehrere Server und Plex-Home-Profile (Datenschicht ist dafür vorbereitet), macOS, tvOS, Musik- und Fotobibliotheken, Chromecast, Trailer/Extras, Löschen von Serverdateien, Home-Screen-Widget, Siri/Spotlight.
Risiken, die früh geprüft werden müssen
- VLCKit auf iOS 26 + Swift 6 — Verfügbarkeit über SPM, Concurrency-Verhalten, Binärgröße (~60–100 MB). Spike in Phase 0/1, bevor die Player-Abstraktion festgezurrt wird.
- Komprimierte Downloads — die Plex-Sync-API ist umständlich und schlecht dokumentiert. Spike vor Phase 4; Original-Download in Phase 3 ist der sichere Boden.
- Sehr große Dateien im Hintergrund — iOS drosselt Background-Transfers. Verhalten mit einem 40-GB-Remux real prüfen,
isDiscretionarybewusst setzen. - Live-TV — kann sich als deutlich größer herausstellen als geplant; deshalb bewusst am Ende.
Verifikation
- Automatisiert:
swift testje Package — PlexKit gegen aufgezeichnete Server-Antworten,PlaybackDecidergegen eine Matrix aus Codec/Container/Gerät (die wichtigste Testtabelle im Projekt), Download-Zustandsautomat inkl. Pausieren/Fortsetzen/Abbruch. - Manuell je Phase im Simulator und auf dem Gerät:
- P1: Anmelden, Server finden, Bibliothek durchblättern, MP4 mit AVPlayer und MKV mit VLCKit abspielen
- P2: Datei erzwungen transcodieren, Netzwerk drosseln (Network Link Conditioner) und Bitratenwechsel beobachten, Fortschritt auf einem zweiten Plex-Client gegenprüfen
- P3: Serie herunterladen, App killen, Flugmodus, offline abspielen; Live Activity auf einem echten Gerät prüfen
- P5: Akzentfarbe durchschalten und Kontraste gegenprüfen, DE/EN umschalten
- Am Ende jeder Phase: ein lauffähiger Build, keine offenen Compiler-Warnungen, Screenshot-Durchlauf der Kernbildschirme auf iPhone und iPad.