Im Spurenblatt stand eine einzige Zeile: „Unbekannt". Das ist der Name, den
AVFoundation einer namenlosen Spur gibt — und bei einem umgerechneten
Stream liefert der Server genau eine. Der Player hatte nichts zur Auswahl,
weil die Wahl längst gefallen war, bevor er gefragt wurde.
Gewählt wird jetzt aus den Spuren, die **in der Datei** stehen, und zwar
bevor der Stream ausgehandelt wird. Die Nummer geht als `audioStreamIndex`
mit hinaus; wer mitten im Film umstellt, bekommt den Stream neu — ausser
bei unveränderter Wiedergabe, wo der Player ohne Unterbrechung umschaltet.
Die Abwägung selbst macht `AudioTrackChooser`: gemerkte Spur, dann
Wunschsprache, dann Rangliste, dann Standardspur — und Audiodeskription
mit deutlichem Abzug statt Ausschluss, damit eine Sprache, die es nur so
gibt, immer noch besser ist als Stille.
Drei Ebenen in `LanguagePreferences`: Serie, Mediathek, allgemein. Gefragt
wird von unten nach oben. Die Mediathek wird nur nachgeschlagen, wenn
überhaupt jemand eine Sprache je Mediathek gesetzt hat — sonst kostete jede
Wiedergabe eine Anfrage für eine Ebene, die niemand benutzt.
Wer von Hand umstellt, dessen Wahl wird für die Serie gemerkt, ohne
Nachfrage. Der Dialog „für diese Serie merken?" ist genau die Art, die man
nach dem dritten Mal wegklickt. Sichtbar ist es trotzdem: das Spurenblatt
zeigt es und lässt es löschen.
Dazu die Rangliste in den Einstellungen, ziehbar, und der Schalter
„Audiodeskription meiden".
Und die Player-Oberfläche blendet endlich als Ganzes über. Die Kopfzeile
hatte gar keine Überblendung — sie sprang herein, während die Bedienung
darunter verblasste. Jetzt steht die Animation an einer Stelle statt an
sechs, und sie läuft über Deckkraft statt über Einfügen und Entfernen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Jeder Aufruf einer Mediathek war ein Serverabruf mit Ladekreis davor. Die
Plakate lagen längst auf der Platte; was fehlte, war die Liste, die sagt,
welche davon hierhergehören.
Jetzt liegt die erste Seite als Abzug daneben: beim Aufrufen steht sie
sofort da, der Server wird still nachgefragt, und man sieht in aller Regel
nichts davon. Antwortet er nicht, bleibt der Abzug stehen — eine Liste von
gestern ist im Zug brauchbarer als ein Fehlerbildschirm.
Bewusst nur die erste Seite. Stünden 240 gespeicherte Titel da und der
Server antwortet mit 60, müsste entweder die Liste vor den Augen schrumpfen
oder der Versatz fürs Nachladen wäre falsch.
Gespeichert wird ausserdem nur, was eine Kachel zeigt. Ein Titel aus einer
Listenabfrage bringt jede Spur jeder Datei mit — im Raster steht davon
nichts, und ausgepackt werden müsste es bei jedem Aufrufen, also genau da,
wo der Abzug Zeit sparen soll.
Die Mediathekenliste selbst wird nur als Rettungsanker gemerkt, nicht als
Beschleuniger: sie sofort zu zeigen hiesse, eine Sitzung für fertig zu
erklären, bevor feststeht, dass der Token noch gilt.
Abmelden räumt alles weg. Titel, Zusammenfassungen und Sehfortschritt sind
persönlich.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Er hat weiterhin angehalten statt ein- und auszublenden, und der Grund
steckte in der Rechnung: der schwarze Rand wurde aus der Fenstergrösse
ausgerechnet, das Bild liegt aber in einer Fläche mit `ignoresSafeArea()`.
Auf dem Mac ist der Einzug der Fensterleiste genau der Unterschied
zwischen beiden — die Rechnung lag um diesen Betrag daneben.
Jetzt wird die Bildfläche gemessen statt angenommen, und beide Zahlen
stehen in einem gemeinsamen, benannten Koordinatenraum. Vor allem aber
wird zuerst `AVPlayerLayer.videoRect` gefragt: der Player weiss selbst,
wo Bild steht. Die eigene Rechnung bleibt für den Moment vor dem ersten
Bild und für VLC, das keine solche Ebene hat.
Dazu zwei Worte: „Nachschub" heisst jetzt „Nachladen", und „Blatt" heisst
wieder „Sheet" — das ist ein Fachbegriff und bleibt englisch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Auf der Detailseite stand „1080" — die Zeichenkette des Servers in
Grossbuchstaben. Weder eine Zahl mit Bedeutung noch ein Name, den jemand
benutzt. Jetzt steht dort „Full HD", „4K", „HD", „SD".
Die Einstufung geht neu über die **Breite**, nicht über die Höhe. Ein
Kinofilm in 2,39:1 misst 1920 × 804; nach der Höhe war das bisher „720p",
und zwar ausgerechnet bei den grossen Filmen. Die Höhe bleibt als Notnagel
für Server, die keine Breite melden, und der Name des Servers als letzter.
Nebenbei fällt damit der 4K-Filter bei Jellyfin nicht mehr durch, wenn ein
breitwandiges 4K nur 1608 Zeilen hat, und die doppelte Einstufung in
PlexMedia verschwindet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Befund, der die ganze Umsetzung bestimmt: `AudioStreamIndex` kam in
der PlaybackInfo-Anfrage **nicht vor**. Der Server wählte, wir nahmen was
kam — und bei einem umgerechneten Stream kommt danach nur noch diese eine
Tonspur an. `selectAudioTrack` schob seither ins Leere, weil im Player
eine Liste mit einem Eintrag stand.
Damit ist auch erklärt, warum die Spracheinstellung beim ZDF-Fall nichts
ausrichten konnte: sie lief auf einer Auswahl, die es gar nicht gab.
`PlaybackIntent` trägt die Spurnummer jetzt mit, und die Aushandlung
nennt sie ausdrücklich. Zwei Tests halten fest, dass sie ankommt — und
dass sie ohne Wunsch weggelassen wird, statt eine Null zu schicken.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
**`LanguageCode`** bringt Kürzel auf eine vergleichbare Form. Der Anlass
ist ein stiller Fehler: verglichen wurden die ersten zwei Zeichen, also
`ge` gegen `de` — und `ger` ist die ISO-639-2/B-Form von Deutsch, die
ffmpeg in viele Dateien schreibt. Bei solchen Titeln wirkte die
Spracheinstellung überhaupt nicht.
`gsw` bleibt `gsw`. Schweizerdeutsch ist keine Schreibweise von Deutsch,
und es gleichzusetzen hiesse, jemandem Schweizerdeutsch unterzuschieben,
der Hochdeutsch wollte. Es gehört in die Rangliste, nicht in eine
Gleichsetzung.
**`AudioTrackChooser`** wägt ab, statt zu filtern: jede Spur bekommt
Punkte, die höchste gewinnt. Damit gibt es immer eine Antwort, und
Audiodeskription wird gemieden, ohne unmöglich zu werden — gibt es eine
Sprache nur mit Audiodeskription, ist sie besser als Stille.
Gemerkt wird nicht eine Nummer, sondern eine Beschreibung der Spur:
Folgen derselben Serie haben unterschiedlich sortierte Spuren, und
„Spur 2" bedeutet in der nächsten Folge etwas anderes.
Siebzehn Tests, darunter der ZDF-Fall wörtlich — deutsche Spur mit
Audiodeskription als Standard, englisch betitelte deutsche Spur daneben
— und zwar in beide Richtungen: mit Meiden gewinnt die richtige, ohne
Meiden gewinnt das Kürzel. Sonst stünde der Schalter nur dabei.
Dazu trägt `MediaStream` jetzt `title`, `isDefault`, `isHearingImpaired`
und `isOriginal`. Der Name aus der Datei ist das zweite Signal neben dem
Kürzel und manchmal das einzige richtige.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Fehlstart im Zug hat den eigentlichen Mangel gezeigt: die ganze neue
Regelung misst **ab dem ersten Bild**. Kommt nie eines, misst sie nichts,
greift nichts — und „Auto" hat sich als das erwiesen, was es wörtlich
heisst: „nimm, was in den Einstellungen steht". Steht dort „Original",
bittet die App über eine Zugverbindung um eine 4K-Datei und wartet
vierzig Sekunden auf etwas, das nie kommen kann.
Statt aufzugeben geht der Watchdog jetzt eine Sprosse tiefer und baut
neu auf — bei Filmen über `restart()`, bei Sendern über den Tuner-Abbau.
Gezählt gegen dasselbe Budget wie die Sicherheitsleine, sonst schickt
eine tote Leitung die App endlos im Kreis.
Scheitert auch das, nennt die Meldung jetzt den Grund und den Weg statt
nur „keine Daten mehr": wer ohne Grenze angefragt hat, soll erfahren,
dass es daran liegt und wo es sich ändern lässt.
Und „Strom" heisst Stream. Zweiunddreissig Stellen, dazu das
Gedächtnis ergänzt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Kern des Ruckelproblems: die App bestellte bei beiden Servern eine
HLS-Liste mit genau einer Variante. AVPlayer kann nur zwischen Stufen
wechseln, die in der Liste stehen — bei einer gibt es nichts zu wechseln,
und wenn die Leitung einbricht, kann nichts nachgeben.
**Jellyfin.** `enableAdaptiveBitrateStreaming` wird gesetzt, und zwar
ersetzend: die Voreinstellung im Server ist `false`, und bei zwei
gleichnamigen Werten gilt beim Server der erste — dieselbe Falle wie bei
`maxHeight` eine Funktion weiter oben. Dazu wird `main.m3u8` zu
`master.m3u8`, falls der Server die Schnipselliste nennt; nur auf der
Stufenliste kann der Parameter wirken.
Am Geräteprofil ändert sich **nichts**. Mehrere `TranscodingProfiles`
wären der naheliegende Fehlgriff — die beschreiben Formate, nicht
Bitraten. `MaxStreamingBitrate` bleibt die oberste Sprosse; was darunter
liegt, erfindet der Server.
**Plex.** `autoAdjustQuality=1`, als Eigenschaft der Anfrage statt als
feste Zeile: `queryItems` speist auch die Adresse zum Herunterladen, und
dort ergäbe eine Leiter keinen Sinn. Bewusst **nicht** am Entscheider —
der antwortet auf Unbekanntes mit nackten 400ern, wofür es hier eigens
`transcoderBisect` gibt. Er beantwortet ohnehin nur „darf das gespielt
werden". Stünde der Wert dort und nähme der Server ihn übel, wäre nicht
die Leiter kaputt, sondern die Wiedergabe.
Beides nur bei „automatisch". Wer im Blatt ausdrücklich „720p · 3 Mbps"
antippt, bekommt das und nicht „höchstens 3, meistens weniger" unter
unverändertem Etikett.
**Was das nicht kann, und das gehört dazugesagt:** Jellyfin verweigert
die Leiter im lokalen Netz grundsätzlich — „Within the local network this
will likely do more harm than good." Ein schwaches WLAN zählt dort mit,
und vom Client aus ist das nicht zu überstimmen. Ebenso fällt sie weg,
wenn Bild oder Ton nur kopiert statt gerechnet werden. Ob am Ende
wirklich mehrere Stufen ankommen, steht deshalb nicht im Code, sondern in
`PlaybackStatistics.variantCount`.
Elf neue Tests, darunter der doppelte Parameter, die andere
Schreibweise, und dass die Sitzungskennung beim Umbau dieselbe bleibt —
sonst bliebe beim Aufräumen ein Transcoder stehen.
Die Probe zeigt jetzt außerdem, ob kopiert statt gerechnet wird. Das ist
die zweite Hürde, und sie ist von zuhause aus nicht sichtbar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`isSupported` war `movie || show`. Alles Übrige fiel heraus — auch eine
Mediathek mit gemischtem Inhalt, die Jellyfin ohne `CollectionType`
meldet. Wer Filme und Serien nicht getrennt hat, sah sie nirgends:
nicht in der Seitenleiste, nicht auf der Startseite, und nirgends eine
Meldung. Eine unbekannte Art ist kein Grund, etwas zu verschweigen.
Musik, Fotos und Live-TV bleiben draußen, und das aus jeweils eigenem
Grund — jetzt aufgezählt statt stillschweigend.
Die Attrappe blättert außerdem richtig: vorher kam immer alles auf
einmal und `TotalRecordCount` war die Länge der Antwort, womit eine App,
die richtig blättert, genauso aussah wie eine, die es gar nicht
versucht. Damit ist belegt, dass die Mediathek alle 252 Titel holt —
in fünf Seiten, bis StartIndex 240.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Keine Datei unter Views/LiveTV importiert noch PlexKit. Sender, Programm
und Einschalten laufen über den neutralen Vertrag, und supportsLiveTV
fragt endlich die Fähigkeit ab statt "server != nil" — womit die Zusage
in JellyfinBackend.capabilities zum ersten Mal stimmt.
Der Player holt die laufende Sendung jetzt für genau einen Sender nach.
Vorher ging dafür das Raster über alle Sender hinaus, weil der Plex-Weg
keinen schmaleren kannte; im neutralen Vertrag ist die Senderliste ein
Parameter.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Zwei Protokolle statt eines: LiveTVBackend für Sender, Programm und
Einschalten, LiveTVRecordingBackend für Aufnahmen. Getrennt, weil Plex
das eine kann und das andere nicht — ein as? sagt die Wahrheit, eine
Methode, die bei Plex nur wirft, verspricht etwas und bricht es dann.
Plex zieht um, ohne umgebaut zu werden. Der Suchlauf, der die
Senderliste findet, bleibt Zeile für Zeile, wo er ist; davor liegt jetzt
ein Actor, der sich Empfänger und Sender merkt. Nötig, weil PlexBackend
ein struct ist und tune() den echten PlexChannel braucht — ohne
Gedächtnis liefe der Suchlauf bei jedem Antippen neu.
Jellyfin ist neu und deutlich kürzer, weil es dort nichts zu suchen
gibt. Die Endpunkte stammen aus der OpenAPI-Spec des Servers. Beim
Vormerken wird die Vorlage des Servers roh weitergereicht statt
entschlüsselt und neu zusammengesetzt: welche Felder Pflicht sind, sagt
die Beschreibung nicht, und ein verlorenes fiele erst an einer Aufnahme
auf, die nicht startet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bis hierher konnte man sich bei Jellyfin anmelden und stöbern, aber nichts
abspielen. Der Player war die letzte große Stelle, die Plex unmittelbar
kannte.
Drei Mitglieder kommen dafür in den Vertrag: eine Wiedergabe vorbereiten,
sie beenden, den Fortschritt melden. Die Entscheidung **ob** umgerechnet
wird, fällt weiterhin vorher im `PlaybackDecider` — der Server führt sie nur
aus. Genau diese Trennung fehlte: der Player hat Plex' Transcoder
angesprochen und konnte deshalb mit nichts anderem reden.
`MediaBackendError` bekommt einen siebten Fall: `refused(reason:)`. Der
Server hat verstanden und sagt nein, in eigenen Worten. Bei Plex war genau
dieser Satz die einzige brauchbare Spur, und er ging tagelang verloren, weil
es dafür keinen Fall gab.
Bei Jellyfin kommt die Adresse einer umgerechneten Fassung vom Server selbst
(`PlaybackInfo` → `TranscodingUrl`), nicht von uns. Er weiß, welche Spuren er
kopieren kann und welche er neu rechnen muss.
Drei Dinge, die beim Hinsehen auffielen:
- **`startTimeTicks` gehört nicht in die Anfrage.** Sonst entscheiden zwei
Stellen über den Startpunkt: der Server ließe den Strom dort beginnen, und
der Player spränge noch einmal um dieselbe Strecke vor. Bei einundzwanzig
Minuten Fortschritt landet man bei zweiundvierzig.
- **Der erste Bericht braucht einen eigenen Endpunkt.** Ohne ihn erscheint
der Titel bei Jellyfin nirgends als „läuft gerade". Er fiel zunächst aus,
weil er die Laufzeit verlangte — und die steht erst Sekunden nach dem
Öffnen fest. Jetzt tut es für die erste Meldung die des Servers.
- **In der Download-Adresse standen Titel und Fassung vertauscht.** Bei einem
Film mit einer Fassung sind beide Kennungen dieselbe und nichts fällt auf;
bei zweien schon. `MediaFile` sagt jetzt, zu welchem Titel sie gehört.
Live-TV bleibt bei Plex und wird als Einziges noch unmittelbar durchgereicht.
Ein Tuner ist ein Stück Hardware, und die fünfhundert Zeilen, die es gekostet
hat, ihn zu bändigen, gehören in keinen gemeinsamen Vertrag.
Nachgesehen, nicht nur gebaut: gegen die Attrappe läuft ein echtes Video, der
Beginn wird gemeldet, der Fortschritt kommt in Ticks mit Sitzung und Fassung
an. Was die Attrappe **nicht** beantworten kann, ist, ob ein echter Jellyfin
das Geräteprofil annimmt und eine Umrechnung anbietet — das entscheidet erst
ein richtiger Server.
257 Tests grün.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Anmeldebildschirm kannte nur Plex. JellyfinKit war fertig, aber von der
App aus nicht erreichbar — „ich sehe nix von Jellyfin".
Quick Connect steht vorn: der Server nennt einen sechsstelligen Code, den man
in einem bereits angemeldeten Client bestätigt. Das Passwort erreicht Flixy
dabei nie, dieselbe Zusage wie bei Plex. Benutzername und Passwort sind der
Rückfall für Server, auf denen Quick Connect abgeschaltet ist; das Passwort
wird dann für die Dauer einer Anfrage gehalten und danach weggeworfen.
Die Adresse wird nicht erraten, sondern durchprobiert. „192.168.1.50" kann
alles Mögliche heißen, und die Reihenfolge ist der Kern von JellyfinAddress:
eine IP-Adresse im eigenen Netz spricht fast nie HTTPS, ein Domänenname fast
immer. Andersherum wartet man bei jedem Server ohne Zertifikat erst auf eine
Zeitüberschreitung.
Die Sitzung hält von hier an einen Server hinter dem neutralen Vertrag, nicht
mehr einen Plex-Server. Die Ansichten haben das nicht gemerkt — genau dafür
war MediaKit da. Dabei fällt ab, was Jellyfin kann und Plex nicht: Favoriten
stehen im Aktionsmenü, sobald der Server sie kennt, Sterne nur dort, wo es
sie gibt.
Was noch nicht geht und deshalb auch nicht behauptet wird: Wiedergabe und
Live-TV sprechen weiter unmittelbar mit Plex. Bei Jellyfin verschwindet der
Live-TV-Eintrag aus der Seitenleiste, und der Player sagt, dass die
Wiedergabe der nächste Schritt ist, statt „Server nicht erreichbar" zu
melden — er ist ja erreichbar.
Nebenbei zwei Stellen, die der Übersetzer aus Plex hinterlassen hat:
`catch is CancellationError` griff seit den getippten Fehlern nie mehr, weil
eine abgebrochene Netzanfrage als Übertragungsfehler zurückkommt; und das
Ersatz-Hintergrundbild einer Folge war toter Code, weil die linke Seite des
`??` gar nicht optional war.
TokenStore wandert nach MediaKit — Tokens sind nichts Plex-Eigenes. Die
Konten sind nach Server getrennt, damit ein späteres Umschalten nicht jedes
Mal eine neue Anmeldung kostet; „Abmelden" räumt trotzdem beide ab.
248 Tests grün.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Start, Suche, Mediathek, Detailseiten und Downloads arbeiten jetzt mit
`MediaItem` und dem `MediaBackend`-Vertrag. Damit sind sie fertig für
einen zweiten Server, ohne dass an ihnen noch etwas zu tun wäre.
Dabei sind ein paar Namen ehrlicher geworden: aus `grandparentTitle`
wurde `seriesTitle`, aus `viewedLeafCount` `playedEpisodeCount`. Und
`TagStrip` nahm bisher Plex-Marken mit Kennung und Rolle entgegen, um
davon nur den Namen anzuzeigen — Genres sind jetzt schlicht Zeichenketten.
`Arrangement` ist aus PlexKit heraus und generisch: dieselbe Rechnung
gilt für jede Liste, die jemand selbst zusammenstellt. Phase 3 baut die
konfigurierbare Seitenleiste darauf, statt sie ein zweites Mal — und ein
zweites Mal falsch — zu schreiben.
Die Fehlerübersetzung gibt es nur noch einmal. Es waren zwei, und sie
waren bereits auseinandergelaufen: derselbe Fehler bekam je nach
Bildschirm eine andere Erklärung.
Das Nachladen der Mediathek bleibt unangetastet: eine lange Liste, die
fünf Einträge vor dem Ende nachlädt. `MediaPage` heißt zwar Seite, ist
aber eine technische Portion — Blättern mit „weiter zu Seite 2" gehört
nicht in eine Mediathek, in der man stöbert. Der Kommentar dort sagt das
jetzt ausdrücklich, damit es niemand später „aufräumt".
Player, Live-TV und die Download-Engine sprechen noch unmittelbar mit
Plex. Sie folgen als Nächstes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ein Protokoll für das, was jeder Server können muss — blättern, Bilder
liefern, Zustand melden. Bewusst schmal: alles, was nur einer kann,
steht nicht darin, sondern als abfragbare Fähigkeit. Ein gemeinsamer
Nenner, der alles umfasst, wäre keiner mehr.
Deshalb `BackendCapability`. Plex kennt keine Favoriten, Jellyfin keine
Plex-Empfänger — und eine Oberfläche, die Knöpfe für Dinge zeigt, die
der Server nicht kann, ist schlimmer als eine, die sie weglässt. Plex
meldet hier ausdrücklich keine Favoriten und wirft, wenn jemand es
trotzdem versucht; stillschweigend nichts zu tun wäre die schlechtere
Antwort.
`MediaQuery` ersetzt `LibraryQuery` nach oben hin. Deren Sortierwerte
waren Plex' Drahtformat („titleSort:asc"), was hieß: entweder lernt
Jellyfin Plex' Schreibweise, oder jede Ansicht muss wissen, mit wem sie
spricht.
`PlexBackend` ist eine Hülle, keine Umschreibung — die Endpunkte bleiben,
wo sie sind. Der Fehlerübersetzer nimmt den ungetypten Wurf, weil Swift
getippte Fehler durch eine Closure hindurch nicht ableiten kann; der
zweite `catch` ist dabei keine Zierde, sondern verhindert, dass ein
unerwarteter Fehler durchfällt und die Signatur zur Lüge macht.
Die App benutzt noch nichts davon.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`MediaItem` und die Typen darunter — die Form, in der die Oberfläche
Titel sieht, unabhängig davon, welcher Server sie geliefert hat.
Die Namen sind dort geblieben, wo sie ohnehin neutral waren: `title`,
`summary`, `year`, `index`. Umbenannt wurde, was Plex-Jargon ist.
`ratingKey` heißt `id`, `viewOffset` heißt `resumeMilliseconds`, und aus
dem „Großelternteil" ist die Serie geworden, die von Anfang an gemeint
war — ein Name, den man in jeder Ansicht im Kopf übersetzen musste.
Zwei Dinge, die dabei ehrlicher werden:
`isFavourite` gibt es, aber Plex meldet immer `false`. Ein Feld, das ein
Server nicht füllt, ist besser als eine Fähigkeit, die es nur auf einem
gibt und die sich nirgends setzen ließe.
Dateigrößen sind jetzt `Int64`. Ein Remux überschreitet vier Gigabyte
mühelos.
Die Übersetzung steht in **einer** Datei und geht in **eine** Richtung.
Die Drahtmodelle bleiben, wo sie sind — sie tragen Eigenheiten, die nur
Plex hat, und die gehören nicht nach oben.
Geprüft wird gegen echte Server-Antworten, nicht gegen erfundene Werte:
genau dort stehen die Eigenheiten, an denen eine Übersetzung scheitert.
Ein Test vergleicht außerdem beide Wege zum Wiedergabe-Profil — sagten
sie Unterschiedliches, spielte ein Titel nach dem Umbau plötzlich über
einen anderen Player.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Erster Schritt zu Jellyfin, und er besteht vor allem aus Wegnehmen.
`PlaybackDecider` nahm bisher Plex-Modelle entgegen und las daraus sechs
Felder. Das band die Entscheidung an einen bestimmten Server, obwohl an
der Frage „kann iOS das öffnen?" nichts serverspezifisch ist. Jetzt nimmt
er ein `MediaProfile` — Container, Codecs, Bitrate, ob die Datei
untersucht ist.
Wie ernst die Kopplung war, zeigte der Player selbst: für die
Offline-Wiedergabe baute er sich **erfundene** Plex-Werte zusammen — eine
Media mit Kennung 0, ein Part mit dem Dateinamen als Schlüssel,
zusammengesetzte Spurlisten —, nur damit die Signatur passte. Für eine
Datei, die längst auf dem Gerät lag und mit Plex nichts mehr zu tun
hatte. Dieser Hack ist weg.
Neu ist das Target `MediaKit`: Typen und Rechnungen, die für jeden Server
gelten, ohne eine einzige Abhängigkeit. Darin `MediaProfile` und die
`Duration`-Brücke, die bisher in PlexKit stand — „weil es die unterste
Schicht ist". Das stimmte, solange es nur einen Server gab.
Damit kennen Wiedergabe und Downloads Plex nicht mehr. Zwei der
Abhängigkeiten waren ohnehin tot; die dritte trug nur diese Brücke.
Die wichtigste Testtabelle des Projekts ist mitgewandert und dabei
kürzer geworden: dort steht jetzt, worauf es ankommt, statt vollständiger
Modelle mit Kennungen und Dateipfaden, von denen niemand etwas las.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>