Kein Video lief jemals an
AVPlayerEngine.load() wartete auf asset.load(.duration) und die
Spurenliste, BEVOR es zurückkehrte — und erst danach rief das Modell
play(). AVFoundation muss für die Dauer aber so viel von der Datei
holen, bis es den Index findet, und der sitzt bei MP4 wie MKV oft am
Ende. Über eine langsame Leitung sind das Minuten, in denen nur ein
Ladekreis zu sehen war, obwohl der Player längst hätte anfangen können.
Jetzt kehrt load() zurück, sobald das Item hängt; Dauer und Spuren
kommen nach und melden sich über den Ereignisstrom.
Dazu AVURLAssetPreferPreciseDurationAndTimingKey: false — sonst
durchsucht AVFoundation bei manchen Formaten die ganze Datei.
Zweiter Fund an derselben Stelle: VLCMedia(url:) ist nullable. Ging es
schief, blieb media still nil, play() tat nichts und es gab keinen
Fehler — nur einen Ladekreis ohne Ende. Wird jetzt gemeldet.
Kein Querformat im Player
requestGeometryUpdate allein dreht nicht: das System dreht sofort
zurück, solange die erlaubten Ausrichtungen es nicht hergeben. Die
Erlaubnis kommt nur über den App-Delegaten, deshalb OrientationLock
als Brücke. Player dreht ins Querformat, beim Verlassen zurück. iPad
bleibt frei — dort ist Querformat beim Blättern der Normalfall.
Uhrzeit über dem Film
Die Statusleiste war nur ausgeblendet, solange die Bedienelemente weg
waren. Jetzt immer.
Sehr langer Start
Zwei Ursachen. Erstens legte der Initialisierer der App die
Hintergrund-URLSession an — die spricht über XPC mit nsurlsessiond und
verzögerte den allerersten Bildaufbau. Jetzt erst bei Bedarf, und wer
keine Downloads hat, braucht sie nie. Zweitens fragte jeder Start erst
plex.tv nach der Serverliste und suchte die Verbindung neu. Die zuletzt
funktionierende wird jetzt gemerkt (Server-Token im Schlüsselbund) und
mit kurzer Zeitgrenze zuerst probiert; die vollständige Suche greift
nur noch, wenn sie nicht mehr antwortet — etwa wenn man von zuhause
weggeht.
Dazu ein Hinweis im Player, wenn die Verbindung über die gedrosselte
Plex-Vermittlung läuft. Sonst sucht man den Fehler in der App.
132 Tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Live-TV
- An keinem echten Server geprüft: die DVR- und EPG-Endpunkte sind der am
schlechtesten dokumentierte Teil der Plex-API. Die Modelle sind deshalb
durchgehend nachsichtig — fehlende Felder ergeben leere Listen, nicht
Fehler, und die Senderkennung wird als Zahl, Zeichenkette oder uuid
akzeptiert.
- Ohne eingerichteten Empfänger erklärt die App das, statt einen Fehler
zu zeigen. Das ist der häufigste Fall.
- Senderliste mit laufender Sendung, Fortschrittsbalken und „danach".
Der Programmführer wird getrennt geholt: fällt er aus, bleibt das
Umschalten möglich.
- Die Anbieterkennung wird aus /media/providers gelesen statt fest
verdrahtet — sie wechselt je nach Server.
Live Activity
- Eigene Widget-Erweiterung; iOS zeichnet den Sperrbildschirm in einem
getrennten Prozess, während die App meist gar nicht läuft.
- Der Presenter drosselt auf 1,5 Sekunden. ActivityKit verwirft zu
häufige Aktualisierungen kommentarlos, und der Fortschritt kommt
mehrmals je Sekunde — ungebremst bliebe die Anzeige stehen.
- @preconcurrency import: Activity und ActivityContent tragen keine
Sendable-Zusage.
Zwei Stolpersteine unterwegs: canImport(ActivityKit) ist auf macOS wahr,
die Typen darin aber nicht verfügbar — es muss os(iOS) sein. Und eine
von Hand geschriebene Info.plist ohne CFBundleIdentifier führt zu einer
Erweiterung ganz ohne Kennung; der Fehler lautet dann „not prefixed with
the parent app's bundle identifier" und zeigt auf die falsche Stelle.
126 Tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Download-Regeln
- Automatisches Löschen nach dem Ansehen, mit Frist statt sofort: wer
die letzte Szene noch einmal sucht, soll sie nicht schon verloren
haben. „Nie" ist die Voreinstellung.
- „Halte N ungesehene Folgen offline": wartende und laufende Downloads
zählen mit, sonst würde bei jedem Durchlauf nachgelegt, bis die ganze
Serie auf dem Gerät liegt.
- Beides als reine Rechnung ohne Zugriff auf Datenbank oder Dateien —
bei einer Regel, die Filme löscht, ist Prüfbarkeit keine Formsache.
13 Tests.
Qualitätswahl beim Download
- transcodedDownloadURL über den Universal-Transcoder mit protocol=http
statt über die umständliche Sync-API. NOCH NICHT AM SERVER GEPRÜFT:
offen ist, ob Plex eine Content-Length mitschickt (sonst kein
Fortschritt und kein Fortsetzen) und ob die Datei durchsuchbar ist.
Steht so im Quelltext; Original bleibt Voreinstellung.
Erscheinungsbild
- Akzentfarbe: zehn kuratierte Kacheln plus freie Hex-Eingabe. Bei einer
eigenen Farbe greift die Kontrastkorrektur und sagt es auch — sonst
wirkt sie wie ein Fehler.
- Vorschaufläche mit Knopf, Symbol und Fortschrittsbalken.
Serveraktionen
- Gesehen-Status umschalten und Sterne setzen über ein Menü auf der
Detailseite.
- Suchlauf der Mediathek aus der Werkzeugleiste.
115 Tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Engine
- Hintergrund-URLSession: läuft weiter, wenn die App zu ist. Der ganze
Zustand liegt in SwiftData, weil es nach einem Neustart kein anderes
Gedächtnis gibt.
- Pausieren über cancel(byProducingResumeData:) — nur so lässt sich
weiterladen statt neu zu beginnen. Bei 40 GB der Unterschied
zwischen brauchbar und unbrauchbar.
- Zwei Übertragungen gleichzeitig, Folgen in Staffel-/Folgenreihenfolge.
Alles parallel zu laden hieße: keine Folge wird fertig.
- Speicherlimit und Geräteplatz werden vor dem Annehmen geprüft, ein
Gigabyte bleibt als Puffer frei.
- Verwaiste Dateien werden beim Start entfernt; sie entstehen, wenn die
App genau beim Abschluss beendet wird.
- Downloads liegen in Application Support, nicht in Caches (iOS räumt
den eigenmächtig leer) und sind von der iCloud-Sicherung ausgenommen.
Oberfläche
- Download-Knopf auf Detailseiten mit vier Zuständen in einem Knopf.
- „Alle Folgen laden" für Staffel und Serie; ein abgelehnter Titel
stoppt den Stapel nicht.
- Downloads-Ansicht mit Belegung, WLAN-Schalter, Pausieren, Fortsetzen,
Löschen — nach Serie gruppiert.
Offline
- Der Player bevorzugt die lokale Datei, auch ohne erreichbaren Server.
- Beim Herunterladen wird ein Abzug der Serverangaben mitgesichert:
offline lässt sich nichts nachfragen, und der PlaybackDecider braucht
Container und Codecs.
Ein Fehler, den die Tests aufgedeckt haben: der Koordinator hielt nur
den ModelContext, nicht den ModelContainer. Ein ModelContext hält seinen
Container nicht am Leben — wird der anderswo freigegeben, stürzt jeder
Zugriff auf einen Eintrag ab, und die Meldung zeigt auf ModelContext.reset
statt auf die Ursache. Jetzt liegt die Zuständigkeit beim Koordinator.
102 Tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>