9a5412dc5dacd7f9b6f9ead3f4518d2badc128bb
25
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d54954b9c2 |
macOS: die App läuft jetzt auf dem Mac
Und dein Argument dafür war das beste: auf dem Mac kann ich selbst hinsehen. Kein Simulator-Tunnel, der mitten im Prüfen abstürzt — `screencapture` auf das Fenster der App, und zwar nur auf ihres, nicht auf den Bildschirm. **Ein eigenes Ziel, nicht ein gemeinsames.** Die Info.plist der iOS-App ist voller iOS-Angaben: Startbildschirm, erlaubte Ausrichtungen, Live Activities. Die alle bedingt zu machen hieße, dass jede spätere Änderung an der iOS-Seite über eine Stelle geht, die auch den Mac betrifft. Zwei Ziele sind ein paar Zeilen mehr und eine Sorge weniger. **Was den Unterschied ausmacht, steht an einer Stelle.** Ein neuer `PlatformShims` in FlixyDesign übersetzt, was auf beiden Seiten verschieden heißt: schmale Titelzeile, halbhohes Blatt, Eingabefelder ohne Großschreibung, die Plätze in der Werkzeugleiste, die Zwischenablage. Zwanzig Ansichten benutzen ihn, statt zwanzigmal `#if os(iOS)` zu tragen — sonst wandert die Fallunterscheidung in jede neue Datei und wird beim übernächsten Bildschirm vergessen. Wirklich verschieden sind nur fünf Stellen: die Bildfläche (`UIView` gegen `NSView`), der AirPlay-Knopf, die Ausrichtungssperre (auf dem Mac gibt es nichts zu sperren), der App-Delegat für Hintergrund-Downloads und das Anmeldefenster, das ein anderes Fenster als Anker braucht. **Das Karussell blättert jetzt mit einem einrastenden Streifen** statt mit `TabView(.page)` — den Seitenstil gibt es auf dem Mac nicht. Das ist nebenbei die bessere Lösung: das verschachtelte TabView hatte die Eigenschaft, beim Wechsel kurz zwei Seiten gleichzeitig zu zeigen. **Was auf dem Mac fehlt und warum:** VLC. Das Binärpaket ist auf iOS festgelegt; sein xcframework enthält zwar Mac-Teile, aber das ist eine eigene Runde. Solange fällt die Wiedergabe dort auf den Systemplayer zurück — MKV und DTS gehen also noch nicht. Live Activities gibt es auf dem Mac nicht, Bild-in- Bild startet von Hand statt beim Verlassen der App. Gesehen: die App startet, der Anmeldebildschirm steht, beide Wege sind da. Angemeldet habe ich mich auf dem Mac **nicht** — das ginge nur, indem ich einen Token in deinen Schlüsselbund schreibe, und dann fragt macOS dich beim ersten Zugriff nach dem Passwort. Das machst du mit einem Klick selbst. iOS baut unverändert, 263 Tests grün. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
21c5a8b46b |
Player: vier Knöpfe, die vorher fehlten oder nichts sagten
**Eine Taste zum Vergrößern** unten rechts auf der Kachel. Das Antippen der Fläche vergrößert weiterhin, aber auf derselben Fläche liegen schon das Ziehen und das Aufziehen — und was dabei danebengeht, sieht aus wie „reagiert nicht". Ein Knopf geht nicht daneben. **Ton und Untertitel getrennt.** Es sind zwei verschiedene Fragen: die Tonspur stellt man einmal am Anfang um, die Untertitel mitten im Film. In einem gemeinsamen Blatt scrollte man jedes Mal an der anderen Liste vorbei. Das Untertitel-Symbol ist gefüllt, solange welche laufen — so sieht man es, ohne das Blatt zu öffnen. Und eine Auswahl schließt das Blatt jetzt selbst. **AirPlay.** Nicht nachgebaut, sondern die Systemstelle: sie kennt Geräte, die eine App nie aufzählen könnte. Der Knopf erscheint nur beim Systemplayer — VLC zeichnet selbst und kann kein AirPlay, und ein Knopf, der dort ins Leere führte, wäre schlimmer als keiner. **Die Qualität steht als Text da**, nicht als drei Schieberegler: „1080p", „720p", „Auto". Bei „automatisch" steht dort, was gerade tatsächlich ankommt — das weiß nur der Player, und ihn zu fragen ist ehrlicher, als „Auto" hinzuschreiben und die Antwort schuldig zu bleiben. Dazu die Einstellungen mit farbigen Symbolen und einer Kopfkarte je Bereich. Das ist nicht nur Zierde: in einer Liste aus zehn gleich aussehenden Zeilen findet das Auge nichts wieder, und „das mit dem blauen Zahnrad" ist eine brauchbare Beschreibung. Die Farben sind fest und ausdrücklich **nicht** die Akzentfarbe — trüge alles dieselbe, unterschiede sie nichts mehr. Und der Aufmacher läuft jetzt unter der Seitenleiste weiter (`backgroundExtensionEffect`): die Ränder werden gespiegelt und weich fortgeschrieben, während Titel und Knöpfe stehen bleiben. Vorher endete das Bild an der Kante der Leiste, und beim Weiterwandern schob sich das nächste an einer harten Linie ins Bild. 263 Tests grün. Player-Knöpfe und Einstellungen angesehen; den Aufmacher hinter der Leiste konnte ich an den einfarbigen Flächen der Attrappe nicht sicher beurteilen — der Simulator ist dabei erneut abgestürzt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
dace5d1543 |
Einstellungen: sechs Bildschirme statt einer Liste — und was sie steuern
Deine Einstellungs-Bilder sind eine lange Liste. Ich habe sie durchgegangen
und getrennt: was etwas steuert, ist gebaut; was eine Einstellung für eine
Sache wäre, die es nicht gibt, ist nicht gebaut. Ein Schalter, der nichts
bewirkt, ist schlimmer als kein Schalter.
**Die Einstellungen sind jetzt eine Übersicht.** Vorher stand alles
untereinander auf einem Bildschirm — Serverangaben, Qualität, Wiedergabe, drei
Verweise, Downloads, Version, Abmelden. Das war beim Bauen bequem und beim
Suchen anstrengend. Jetzt führt jede Zeile in einen Bildschirm, der zu einer
Frage gehört: Server, Erscheinungsbild, Startseite, Wiedergabe, Ton &
Untertitel, Downloads, Diagnose.
**Neu, und jedes davon greift irgendwo:**
- Zurück- und Vorspulen getrennt einstellbar. Die Knöpfe im Player und die
Doppeltipp-Flächen tragen danach auch das passende Symbol.
- Startgeschwindigkeit — die Engine kann es längst, gefragt wurde nie.
- „Im Querformat sperren" abschaltbar. Für die allermeisten Filme richtig, für
den, der im Bett auf der Seite liegt, das Gegenteil.
- „Gewählte Qualität merken": die Stufe gilt dann auch für den nächsten Titel.
Aufbewahrt wird eine **Zahl**, keine Sprosse — welche Sprossen es gibt, hängt
von der Quelle ab, und eine 720p-Datei kennt keine Zwanzig-Megabit-Stufe.
Dann kommt die nächstgelegene, nicht „automatisch".
- „Bitrate im Qualitätsmenü zeigen".
- Gleichzeitige Downloads. Der Wert gab es schon in der Engine, nur ohne
Bedienung und ohne dass er einen Neustart überlebte.
- Aufmacher: wie viele Titel, und ob sie aus Angefangenem oder nur aus
Neuzugängen kommen.
- „Weiterschauen und Als Nächstes zusammenlegen" und „Gesehenes aus
Neuzugängen ausblenden" — beides in der Ansicht, nicht im Server: das sind
Fragen der Darstellung, und so gelten sie für Plex und Jellyfin gleich.
**Verkleinert herunterladen geht jetzt auch bei Jellyfin.** Da hatte ich zu
früh aufgegeben: ich hatte nur nach der Adresse zum *Abspielen* gesucht, und
die ist eine Abspielliste mit hunderten Schnipseln — als Datei unbrauchbar.
`/Videos/{id}/stream.mp4` gibt dieselbe Umrechnung als eine Datei heraus. Die
Größe bleibt eine Schätzung; eine Umrechnung, die erst beim Abruf beginnt,
kennt ihre eigene Länge nicht.
**Die Kontozeile ist jetzt eine Karte, die nach oben aufklappt**, kein
Systemmenü mehr. Ein Menü legt sich als eigene Ebene über alles und springt
dorthin, wo Platz ist; die Karte bleibt, wo sie ist, und lässt die Zeile mit
dem Namen stehen — man sieht also weiter, um wessen Konto es geht.
**Nicht gebaut, mit Grund:** Trailer im Karussell, Poster- und Sammlungsstil,
Untertitel-Aussehen, sekundäre Untertitel, lokale Intros, Modular Home,
App-Symbol. Für all das fehlt das, was die Einstellung steuern würde — es gibt
in Flixy weder Trailer noch Sammlungen, und der Player zeichnet Untertitel
nicht selbst.
263 Tests grün. **Nicht angesehen**: der Simulator-Tunnel ist weiterhin
abgestürzt und ließ sich nicht wieder aufbauen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
fb418c842d |
Wiedergabe: der Player spricht nicht mehr mit Plex, sondern mit dem Vertrag
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> |
||
|
|
5696254034 |
Jellyfin: der Anmeldebildschirm bietet jetzt beides an
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> |
||
|
|
fe2d730c00 |
Die Ansichten sprechen nicht mehr mit Plex
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> |
||
|
|
5b2b86b66b |
MediaKit: die Grundschicht, auf der Plex und Jellyfin dasselbe meinen
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> |
||
|
|
33dfac352a |
Player: beim Umstellen sah man den Filmanfang statt seiner Stelle
Ein frisch angeforderter Stream beginnt vorn, puffert und springt erst danach an die Stelle, an der man war. Zehn Sekunden lang lief sichtbar der Filmanfang — und der Eindruck, die Stelle sei weg und man müsse sie wiederfinden. Der Umstieg wird jetzt abgedeckt, solange der Player noch nicht dort ist, und die Abdeckung nennt die Stelle ausdrücklich. Genau darum geht es: nicht, dass es schneller wird, sondern dass niemand denkt, er habe etwas verloren. Aufgehoben wird sie, sobald der Player innerhalb von zwei Sekunden um die Stelle liegt. Dazu eine Notbremse nach dreißig Sekunden — springt er nie dorthin, bliebe sonst für immer eine schwarze Fläche über dem Bild. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
171ba99908 |
Player: nach einem Fehlschlag war dieselbe Stufe eine Sackgasse
`select(quality:)` stieg aus, wenn die Stufe bereits gewählt war. Nach einem Fehlschlag stand sie aber schon fest — ein erneutes Antippen galt als „keine Änderung", und es passierte nichts mehr. Man kam nur wieder heraus, indem man eine andere Stufe nahm. Steht die Wiedergabe auf „gescheitert", ist ein erneuter Versuch jetzt erlaubt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
f628819765 |
Player: eine gewählte Stufe wurde gar nicht umgerechnet
Der Decider rechnet nur um, wenn die Datei die Grenze überschreitet. Für den Normalfall ist das richtig — für eine ausdrückliche Wahl im Player nicht: wer für eine 8-Mbit-Datei „12 Mbit" wählt, bekam DirectPlay, weil die Datei ja darunter liegt. Gemeint ist aber „rechne es mir auf diese Stufe um", nicht „gib mir die Datei, falls sie so vorliegt". Die Richtlinie kann eine Umrechnung jetzt erzwingen, und die Wahl im Player tut das. Dazu verschluckte `select(quality:)` den eigentlichen Grund: lehnte der Server ab und sagte im Klartext warum, wurde diese Meldung danach durch das allgemeine „keine abspielbare Datei" ersetzt — der eine brauchbare Hinweis war damit weg. Jetzt bleibt stehen, was der Server gesagt hat. Und das Menü war zu viel. Vorn steht die kurze Liste — automatisch, Original, je eine brauchbare Stufe pro Auflösung, nur Ton. Wer eine bestimmte Bitrate erzwingen muss, klappt „Alle Stufen" auf; im Alltag will man „eine Stufe kleiner" und nicht die Wahl zwischen zehn und zwölf Megabit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
12bb49615b |
Player: die Qualitätsleiter des Servers, Bitrate im Blick, Doppeltipp
Die vier erfundenen Stufen weichen der Leiter, die der Server selbst meldet: 64, 96, 208, 320, 720, 1500, 2000, 3000, 4000, 8000, 10000, 12000, 20000 kbps mit den Auflösungen, die Plex dafür vorsieht. Eine eigene Leiter hieße, Stufen anzubieten, die der Transcoder nicht kennt — er rundet dann still auf die nächste, und die Anzeige log. Angeboten wird nur, was zur Quelle passt. Eine 720p-Datei auf 1080p hochzurechnen bringt kein Bild dazu, und die Stufen sähen alle gleich aus. Oben im Blatt steht, was tatsächlich über die Leitung kommt — gemessene Bitrate, Auflösung, verworfene Bilder. Der Unterschied zur gewählten Stufe ist keine Haarspalterei: die eine sagt, was erlaubt ist, die andere, was ankommt. Ruckelt es trotz hoher Stufe, steht die Antwort dort. „Nur Ton" gibt es, aber ehrlich beschriftet: Plex kennt für Videos keinen reinen Tonstrom, also wird die unterste Sprosse geholt und das Bild ausgeblendet. Dazu Doppeltipp links und rechts für zehn Sekunden zurück und vor. Die Reihenfolge der Gesten ist entscheidend — das einfache Tippen verschluckt sonst das doppelte. Die Kachel hängt nicht mehr in vier Ecken fest, sondern klebt links oder rechts und ist in der Höhe frei. Vier Ecken sind schnell gebaut, aber im Weg: steht sie über einem Knopf, bleiben drei Orte, die ebenso stören. Größer wird sie durch Zusammen- und Auseinanderziehen; der Griff ist weg, er saß zwangsläufig auf dem Bild und stritt sich mit dem Verschieben. Und die Begriffe: „Motor" für engine sagt kein deutscher Entwickler. Fachwörter, die im Deutschen englisch bleiben, bleiben jetzt englisch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c2ddc10871 |
Wiedergabe: der alte Motor lief weiter, während der neue startete
Der Ton, der nach dem Schließen der App weiterlief, und die neue Wiedergabe, die stumm blieb, sind derselbe Fehler — und er ist meiner. `stop()` machte drei Netzwerkaufrufe, bevor es den Motor abschaltete: Fortschritt melden, Transcoder anhalten, Tuner freigeben. Beim Wechsel habe ich das in den Hintergrund gelegt, damit man nach dem Antippen nicht wartet. Damit lief der alte Motor die ganze Zeit weiter, und wenn er endlich abräumte, nahm er der bereits laufenden neuen Wiedergabe die Audiositzung weg. Bei Live-TV kam dazu, dass der Motor weiterspielte, während die App längst im Hintergrund war — Hintergrundton ist erlaubt, und niemand hatte ihn abgeschaltet. Jetzt ist beides getrennt. `silence()` legt den Motor sofort still, ohne Netzwerk und ohne Warten; `releaseOnServer()` meldet danach in Ruhe, dass Schluss ist. Beim Wechsel wird zuerst stillgelegt, dann gebaut — nie laufen zwei Motoren gleichzeitig. Dazu eine Grenze für ewiges Laden: ein Live-Strom kann sterben, ohne dass der Motor es merkt, und der Kreisel drehte sich dann bis zum nächsten Antippen. Nach vierzig Sekunden ohne Daten wird abgebrochen, der Motor stillgelegt und gesagt, was los ist — sonst bliebe bei Live-TV obendrein der Tuner belegt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c348ab0755 |
Kachel größer ziehen, Senderlogos vollständig, Sendung im Player
Drei Sachen aus dem Feldtest. Der Griff zum Größerziehen war wirkungslos, und zwar durch meinen Fehler: die Drehung lag auf dem ganzen Überzug statt auf dem Symbol. Bei zwei der vier Ecken kippte damit die gesamte Trefferfläche weg, und man griff ins Leere. Jetzt sitzt der Griff über `overlay(alignment:)`, nur der Pfeil dreht sich, und das Ziehen hat Vorrang vor dem Verschieben der Kachel — sonst gewinnt das Verschieben, das am selben Ort hängt. Senderlogos sind Schriftzüge und fast nie quadratisch. Formatfüllend in einem Quadrat fehlten links und rechts genau die Teile, an denen man den Sender erkennt. Sie bekommen jetzt eine breitere Fläche und werden eingepasst statt beschnitten — `FlixyArtwork` kann beides, und welches gilt, entscheidet der Aufrufer. Und im Player steht bei Live-TV, was gerade läuft. Der Name reist mit dem Einschalten mit, denn nachzuschlagen ist er nicht — ein Sender hat keine Metadaten in der Mediathek. Beim Aufdecken der Bedienelemente wird er nachgeholt, aber nur, wenn die alte Sendung wirklich abgelaufen ist: sonst stünde derselbe Name den ganzen Abend, und das wäre derselbe Fehler, der eben erst im Programm behoben wurde. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
26c909a2e2 |
Mini-Player: die Wiedergabe überlebt das Zurückgehen
Wer im Fernsehen etwas sieht und nebenher nachsehen will, was sonst läuft, musste bisher abbrechen. Plex' eigene Handy-Apps können das auch nicht — kein Grund, es hier genauso zu machen. Der eigentliche Umbau ist nicht die Kachel, sondern der Besitz. Die Wiedergabe hing an der Sichtbarkeit der Ansicht, und zwar dreifach: das Modell war `@State` im Player, `onDisappear` hat den Motor abgeräumt, und die Bildebene entstand in `makeUIView` neu. Zurückgehen hieß deshalb: Motor tot, Transcoder aus, beim Live-TV der Tuner freigegeben. Jetzt gehört das Modell der Wurzelansicht, die immer da ist. Verkleinern und Vergrößern wechseln nur noch die Darstellung; beendet wird die Wiedergabe an genau einer Stelle, und zwar weil jemand es will. Die Bildebene hält das Modell selbst — dasselbe Muster, das VLC seit jeher verwendet. Dieselbe `UIView` wandert zwischen Kachel und Vollbild, statt neu zu entstehen; sonst setzte das Bild bei jedem Wechsel neu an. Bild-in-Bild wandert mit und bekommt endlich einen Delegaten: bisher war es eine Einbahnstraße, aus der man nicht in die App zurückkam. Die Kachel schwebt und lässt sich in jede Ecke ziehen, statt als Leiste unten zu kleben — eine Leiste nähme jeder Liste Platz weg und müsste in jedem Bildschirm bedacht werden. Die Eckenberechnung steht in MiniPlayerPlacement und ist geprüft: in einer View wäre eine Kachel, die halb aus dem Bild ragt, erst auf dem Gerät aufgefallen. Der Pfeil oben verkleinert, ein neuer Knopf daneben beendet wirklich. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
a9563be1e1 |
Live-TV: die Abspielliste ist fertig — sie gehört abgerufen, nicht umgerechnet
Das Belegen des Tuners klappt, und heraus kommt:
/livetv/sessions/1b5bb998…/DBACCA6D…/index.m3u8?offset=-1.000000
Eine fertige HLS-Abspielliste. Ich habe sie an den Transcoder gegeben —
also darum gebeten, etwas umzurechnen, das schon fertig ist. Daher die
nackte 400. Sie wird jetzt unmittelbar abgerufen, mit Token und
Gerätekennung in der Adresse, weil der Systemplayer keine Kopfzeilen
mitschickt. Die eigene Abfrage der Sitzung bleibt dabei erhalten.
Dazu das Gegenstück, ebenfalls aus dem Quelltext des Plex-Clients:
`DELETE /media/subscriptions/{id}` gibt den Tuner wieder frei. Er ist ein
Stück Hardware — vier Stück gibt es, und ein nicht freigegebener fehlt
beim nächsten Einschalten. Das passiert jetzt beim Schließen des Players
und auch dann, wenn das Belegen zwar klappt, aber nichts Abspielbares
dabei herauskommt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
a9a45e685d |
Live-TV: jeder Weg wird bis zum Ende geprüft, bevor der Player ihn bekommt
Wieder HTTP 400, und wieder ohne Begründung. Der Grund, dass das dreimal passieren konnte: eine gebaute Adresse galt schon als Erfolg. Ob der Server sie überhaupt annimmt, stellte sich erst im Player heraus — und dort steht nur „HTTP 400 (unhandled)". Jetzt ruft die App die Adresse einmal selbst ab, ohne Plex-Kopfzeilen, genau so wie AVPlayer es tut. Nur was mit 200 antwortet, geht an den Player. Was nicht, steht mit dem Wortlaut des Servers in der Spur. Weil weder der Schlüssel für die Sitzung noch die anschließend umzurechnende Adresse für diesen Teil der Plex-Schnittstelle beschrieben sind, werden die Kombinationen der Reihe nach durchprobiert — Adresse der Sitzung, Adresse aus der Antwort, Adresse des Teils darin, je mit mediaIndex -1 und 0, höchstens acht Versuche. Antwortet keiner, wird der Transcoder wieder angehalten, statt für nichts weiterzurechnen. Die Spur steht in einem Blatt statt in einem Dialog: die Antwort des Servers ist mehrzeilig, und ein Dialog schneidet sie ab — dann stünde dort wieder nur, dass es nicht ging. Dazu ein Nebeneffekt, der beim zweiten Sender zugeschlagen hätte: der Player kannte die Sitzungskennung des Live-Stroms nicht und hat den Tuner beim Schließen nicht freigegeben. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
861ca805a5 |
Live-TV: erst den Tuner belegen, dann umrechnen lassen
Der übersprungene Schritt. Ich habe die Sendung direkt an den Transcoder gegeben — ohne dass jemand den Tuner belegt hätte. Der Server fing an, merkte, dass er nichts zu senden hat, und brach ab: „die Wiedergabe wurde unterbrochen", ohne weitere Angabe. Jetzt zwei Schritte. Erst eine Live-Sitzung eröffnen, dann die Adresse dieser Sitzung umrechnen lassen — nicht die des Senders und nicht die der Sendung. mediaIndex und partIndex sind dabei -1: ein laufender Sender hat keine Fassungen zur Auswahl. Unter welchem Schlüssel sich die Sitzung eröffnen lässt, unterscheidet sich je nach Anbieter, deshalb werden die in Frage kommenden der Reihe nach probiert — der Raster-Schlüssel zuerst, weil er den Sender genauso bezeichnet wie die Liste, aus der er stammt. Dazu zwei Stellen, an denen Gründe bisher verschwanden: Vor dem Abspielen wird der Server gefragt, ob er den Strom überhaupt zulässt. Er antwortet im Klartext; der Player meldet nur, dass es nicht ging. Und wenn der Motor scheitert, steht sein Grund ab jetzt auf dem Bildschirm statt nur im Protokoll. „Die Wiedergabe wurde unterbrochen" allein sagt weder, ob der Server ablehnt, noch ob der Motor das Format nicht mag — und genau diese Unterscheidung hat diese Runde gekostet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
bbe47b83be |
Live-TV: das Programm kam nie an, und die Einschalt-Adresse wurde weggeworfen
Drei Fehler, alle im eigenen Code. Die Sendezeiten stehen beim Programmanbieter im Medien-Block, nicht am Eintrag darüber. Ich habe sie oben gesucht, nicht gefunden und jede Sendung verworfen — übrig blieb „Kein Programm verfügbar" auf jedem Sender, und ohne Sendungen fiel auch die Senderliste auf die Tunerliste zurück. Die Senderspalte im Raster fehlte ganz. Die Zeitachse ist mehrere tausend Punkte breit, und der HStack hat den Nachbarn dafür auf null gedrückt. Jetzt hat die Spalte Vorrang, und die Achse liegt als Auflage über einer durchsichtigen Fläche — eine Auflage zählt bei der Größenrechnung nicht mit. Der Abstand der Achsenbeschriftung gehört in die Spaltenbreite hinein statt daneben, sonst steht die ganze Achse acht Punkte neben dem Programm darunter. Und das Einschalten: die Adresse wurde berechnet und verworfen, dann bekam der Player eine Senderkennung, zu der er einen Film in der Mediathek suchte. Den gibt es nicht. Jetzt reicht die Anfrage die fertige Adresse durch, der Player spielt sie ohne Umweg, und meldet weder Fortschritt noch sucht er Metadaten. Scheitert es doch, steht ab jetzt eine Meldung da statt gar nichts. Geprüft wurde das Raster diesmal mit den Augen: ein Gerüst im Simulator zeichnet es mit erfundenen Sendern. Dafür bekommt es die Bildadresse als Abschluss übergeben, statt sie aus der Sitzung zu lesen. Dazu: Downloads zeigen jetzt an, wenn sie auf WLAN warten. Die Hintergrund-Sitzung wartet in dem Fall von sich aus — kein Fehler, nur Stillstand, und das sah bisher aus wie ein hängender Download. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
ac0c27c026 |
Fünf Punkte aus dem Praxistest
iPad meldete sich nie an Die Ursache ist unangenehm speziell: Wer im Browser schon bei Plex angemeldet ist, wird von app.plex.tv/auth sofort auf watch.plex.tv weitergeschickt — OHNE dass der Code verknüpft wird. Man sieht sich angemeldet, die App wartet vergeblich, und es gibt keinerlei Hinweis. Jetzt wird plex.tv/link mit eingetragenem Code geöffnet. Diese Seite tut nur eine Sache: den Code bestätigen. Die Gerätekennung steckt ohnehin schon im PIN. Dazu ein Ausweg: bleibt es nach fünfzehn Sekunden ohne Token, erscheint der Code im Anmeldebildschirm zum Abtippen. Schließt man das Fenster ohne Erfolg, bleibt er stehen statt abzumelden — er gilt weiter. Download fragte nicht nach der Qualität Jetzt Original, Full HD, HD oder Sparsam, jeweils mit geschätzter Größe. Die Größe ist das eigentliche Entscheidungskriterium — „Full HD" allein sagt nicht, ob es aufs Telefon passt. Die komprimierten Stufen laufen über den Universal-Transcoder und sind weiterhin NICHT am Server geprüft; steht so im Quelltext. Schwarzes Bild ohne Anzeige Der Player kannte nur „spielt" und „spielt nicht". Wartete er auf Daten, stand man vor einem Standbild ohne Hinweis. Es gibt jetzt einen eigenen Pufferzustand mit Anzeige. Aufmacher war beschnitten Er stand auf 3:4, Plex liefert als `art` aber 16:9 — mit scaledToFill ging links und rechts je ein Viertel verloren, ohne dass man es dem Bild ansah. Die Seitenverhältnisse liegen jetzt an einer Stelle in FlixyAspectRatio. Schwarze Leiste über dem Aufmacher .ultraThinMaterial statt undurchsichtigem Grund: das Bild läuft unscharf hinter der Leiste weiter, statt an einer harten Kante zu enden. Weichgezeichnet, nicht abgedunkelt. 139 Tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
bf0a63a924 |
Vier Punkte aus dem Praxistest
Download-Knopf tat nichts DownloadService.plan gab still nil zurück, wenn es keinen Auftrag bauen konnte, und download() reichte das als nil weiter. Der Knopf sah aus, als wäre er kaputt, dabei war der Grund jedes Mal bekannt. Es gibt jetzt einen eigenen Ablehnungsgrund mit vier unterschiedlichen Begründungen — keine Verbindung, keine Datei, kein abrufbarer Teil, keine Adresse. Eine Funktion, die kommentarlos nichts tut, ist selbst der Fehler. Live-TV galt als „nicht eingerichtet" Plex legt die Empfängerliste unter dem Schlüssel `Dvr` ab, die Hülle kannte aber nur Metadata, Directory und Hub. Sie las eine leere Liste und zog daraus die falsche Schlussfolgerung — auf einem Server, auf dem ein Empfänger eingerichtet war. Dasselbe galt für die Anbieterliste unter `MediaProvider`, ohne die es keinen Programmführer gibt. Beim Umbau wäre beinahe die Fehlerdiagnose verloren gegangen: mit `try?` über alle Schlüssel verschwände auch ein echter Dekodierfehler und ergäbe stillschweigend eine leere Liste. Aus „ein Feld heißt jetzt anders" würde „die Mediathek ist leer". Jetzt wird erst geprüft, ob der Schlüssel da ist, und dann ohne try? dekodiert. Fünf Tests halten beides fest. Qualität im Player wählbar Automatisch, Original, 1080p, 720p, 480p. Der Eingriff wird genau dann gebraucht, wenn es ruckelt — dafür will niemand den Film verlassen. Ein Wechsel baut den Strom neu auf (die Stufe steckt in der Adresse), merkt sich die Stelle und fährt sie wieder an. Der alte Transcoder wird vorher angehalten, sonst rechnet der Server zwei Ströme für einen Zuschauer. AVPlayer-Anzeige entfernt Sie war eine Prüfhilfe aus Phase 1. Dieselbe Angabe steht weiterhin unter Einstellungen → Diagnose, wo sie hingehört. 137 Tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
f10f0c4086 |
Diagnose-Bildschirm und Build-Datum
Zweimal in Folge stand „es läuft nicht" gegen „bei mir ist es behoben", ohne dass sich klären ließ, welcher Stand überhaupt auf dem Gerät liegt. Das soll nicht noch einmal passieren. Einstellungen → Diagnose zeigt jetzt: - welcher Server und über welche Verbindung, mit Warnung bei der gedrosselten Plex-Vermittlung - den letzten Abspielversuch: Titel, gewählter Weg, Quelladresse - jeden Schritt mit Zeitabstand zum Start. Der Abstand ist die eigentliche Auskunft: liegt zwischen „Quelle gewählt" und „spielt" eine Minute, hängt es an der Leitung; kommt „spielt" gar nicht, am Motor. - den Zeitpunkt, zu dem der laufende Build entstanden ist Das Build-Datum kommt aus dem Änderungsdatum der eigenen Binärdatei — kein Build-Skript, keine erzeugte Datei, und immer richtig. Der Bericht lässt sich kopieren und enthält keinen Token: die Quelladresse wird ohne Abfrageteil abgelegt, und dort steckt er. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
b7fc18823a |
Phase 5: Nachladen, Startseiten-Layout
Nachladen - DownloadRefiller wählt aus, WELCHE Folgen fehlen; wie viele, rechnet die Regel-Engine. Getrennt, weil das Rechnen ohne Server auskommt und damit prüfbar ist. - In Ausstrahlungsreihenfolge, Specials in Staffel 0 bleiben außen vor. - Läuft nach dem Ansehen und beim Start, nicht auf einem Zeitgeber: eine App, die im Hintergrund unaufgefordert Gigabytes lädt, wäre eine unangenehme Überraschung auf der Mobilfunkrechnung. Startseiten-Layout - Reihen ein-/ausblenden und per Bearbeiten-Modus umsortieren. - Die Anordnungslogik liegt als HubArrangement in PlexKit statt im App-Target — dort ist sie ohne Simulator testbar, und dort sitzen die unangenehmen Fälle: neue Reihen vom Server landen am Ende statt zu verschwinden, verschwundene aus der gespeicherten Liste stören nicht, ein doppelter Eintrag stürzt nicht ab. 11 Tests. 126 Tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
62158303a2 |
Phase 3: Download-Engine und Offline-Wiedergabe
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> |
||
|
|
55bcfe2fbf |
Phase 2: Transcoding, adaptive Bitrate, Spuren, Marker, Sync
Der Decider brauchte keine Änderung — nur eine andere Richtlinie. Genau dafür war er gebaut. Transcoding - TranscodeRequest baut die Parameter für /video/:/transcode/universal. directStream=1 lässt Plex nur umpacken, wenn der Bildcodec passt, statt pauschal neu zu kodieren. - Vor dem Start wird der Server gefragt (decision); sagt er nein, steht der Grund im Klartext da statt eines schwarzen Bildes. - Beim Verlassen wird der Transcoder angehalten. Ohne dieses Signal rechnet der Server minutenlang für niemanden weiter. - Die adaptive Bitrate kommt mit HLS: AVPlayer wechselt sie selbst. Qualitätseinstellungen - Getrennt für WLAN und Mobilfunk, fünf spürbare Stufen. Voreinstellung: zuhause Original, unterwegs 4 Mbit. - NetworkConditions erkennt Mobilfunk über isExpensive — das deckt auch den Hotspot ab, der technisch WLAN ist und trotzdem Volumen kostet. Spuren und Marken - Ton- und Untertitelwahl im Player, bevorzugte Sprache in den Einstellungen, nachsichtiger Sprachvergleich (de/deu/ger/de-DE). - Intro- und Abspann-Marken mit Überspringen-Knopf, optional automatisch. Bereits übersprungene Marken werden gemerkt, sonst käme man nach einem Rücksprung nie ins Intro zurück. - Nächste Folge inkl. Staffelwechsel; Specials in Staffel 0 werden nicht vorgezogen. Fortschritt - Alle zehn Sekunden an den Server, plus Schlussmeldung beim Verlassen. Erst die nimmt den Titel aus „wird gerade abgespielt" heraus. - markWatched/markUnwatched/rate/refreshLibrary stehen bereit. 86 Tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
9a69df4635 |
Phase 1: Anmeldung, Bibliothek, Detailseiten, Hybrid-Player
Die Datenschicht: - plex.tv-Anmeldung über das PIN-Verfahren, Token im Schlüsselbund - Serversuche und Verbindungsauswahl nach Rang (lokal vor direkt vor Vermittlung), mit Abgleich der Server-Kennung gegen veraltete plex.direct-Adressen - PlexServer als einzige Stelle, an der die App Endpunkte kennt: Mediatheken, Hubs, seitenweises Blättern mit Filtern, Metadaten, Staffeln/Folgen, Suche Die Oberfläche: - Startseite mit Aufmacher und den Reihen vom Server - Mediathek als Raster oder Liste, mit Filter- und Sortierblatt - Detailseiten für Filme, Folgen, Serien und Staffeln inkl. Besetzung, Fassungsauswahl und aufklappbaren technischen Angaben - Suche mit gemerkten Anfragen - Vollbildplayer mit eigenem Chrome Der Player: - AVPlayerEngine und VLCPlayerEngine hinter einem gemeinsamen Protokoll - VLCKit liegt in einem eigenen Package (FlixyVLC), damit `swift test` in FlixyKit nicht das 920-MB-Binärpaket mitzieht Am echten plex.tv geprüft: der Anmeldecode kommt an. Dabei fiel auf, dass `strong=true` einen dreißigstelligen Code liefert, der sich bei plex.tv/link nicht eintippen lässt — jetzt der kurze. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |