ad556c90347be308394e0fe7a43490ebd7dc7864
8
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ad556c9034 |
Ein Knopf an jeder Folge, und die Suche spielt nicht mehr sofort ab
**Staffelansicht: Download je Folge.** Für den Fall, den du beschrieben hast — Staffel halb gesehen, Rest nachholen. Ein Tipp öffnet dieselbe Qualitätsauswahl wie auf der Detailseite. Der Knopf steht **neben** dem Link, nicht darin: ein `Button` im Label eines `NavigationLink` löst auf iOS nie aus, und `.plain` lässt das lautlos geschehen. Dieselbe Falle steht in `QuickPlayTile` schon beschrieben. Als Geschwister in einer Reihe teilt das Layout den Platz auf, und der Titel kann gar nicht erst unter dem Knopf verschwinden. Die Kinderliste trägt keine Dateiangaben — ohne Nachladen wäre die Qualitätsliste leer und die Prüfung auf freien Speicher übersprungen. `DownloadButton` holt sie deshalb beim ersten Tipp nach (`loadsDetails`), mit Kreisel und fester Breite, damit die Zeile nicht springt. Nicht beim Erscheinen: zwanzig Zeilen wären zwanzig Anfragen für einen Knopf, den man meistens nicht drückt. Nur bei Folgen. Für eine ganze Staffel gibt es den Knopf oben schon. **Suche: Antippen führt auf die Seite.** Die Suche baut dieselbe Reihe wie die Startseite und war damit auch dieselbe Geste. Wer sucht, weiß aber noch nicht, was er sehen will — der erste Treffer ist eine Vermutung des Servers. Abspielen steht weiter im Menü unter langem Drücken. Dabei aufgefallen: `SearchView` hinterlegte `flixyItemChanged` nicht, anders als Startseite und Mediathek. „Als gesehen markieren" wirkte dort am Server, aber die Kachel lud nie neu — es sah aus, als wäre nichts passiert. Im Simulator geprüft: Knopf an jeder Folge, Auswahl mit echten Größen („Original · HD · 906,9 MB"), Suchtreffer öffnet die Detailseite. iOS und tvOS bauen, 489 Tests grün. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
6373566d1c |
Suchen am Fernseher — und die Leiste, die es dafür braucht
Das Backend konnte die Suche längst; es fehlte die Oberfläche. **`.searchable`, keine eigene Tastatur.** tvOS macht daraus einen eigenen Bildschirm mit Rastertastatur oben und Ergebnissen darunter — die Bedienung, die jeder von Apples Apps kennt. Etwas Eigenes zu bauen hiesse, sie nachzuahmen und dabei die Diktierfunktion zu verlieren. **Herausgelöst statt kopiert**, wie schon bei Start und Mediathek: `SearchFeed` trägt das Entprellen, die Mindestlänge und die zuletzt gesuchten Begriffe. Drei Stellen, an denen es nicht offensichtlich ist — und die dritte ist die wichtigste: geprüft wird auf `Task.isCancelled` und nicht auf `CancellationError`, weil eine abgebrochene Netzanfrage als Übertragungsfehler zurückkommt. Die naheliegende Prüfung lief ins Leere, und jeder Tastendruck leerte kurz die Liste. **Und erst jetzt eine Reiterleiste.** Bis hierher gab es genau einen Bildschirm, und eine Leiste mit einem Eintrag wäre Zierrat gewesen. Der Suchreiter trägt `role: .search`: tvOS stellt ihn dadurch ans Ende und gibt ihm die Lupe, dieselbe Stelle wie überall sonst. Keinen dritten Reiter für die Mediatheken — die stehen als Reihe auf der Startseite, und ein Reiter verspräche einen eigenen Bereich, hinter dem dasselbe Raster läge. Der offene Reiter wird ausdrücklich gehalten, nicht dem System überlassen: ein Link aus dem Regal über der App führt zur Startseite, und stünde gerade die Suche offen, passierte sichtbar nichts. Nebenbei bekommt die iOS-Suche einen Knopf zum Vergessen des Verlaufs. Wischen zum Löschen gab es dort, am Fernseher gibt es das nicht — und eine Liste, die sich nur auf einer Plattform leeren lässt, ist keine. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
81e22c4f97 |
Hinter der Insel läuft jetzt das Wasser durch
Der schwarze Streifen über der Navigation war kein gezeichneter Balken, sondern fehlender Inhalt: der gesamte Baum saß innerhalb des Sicherheitsrands. Deshalb erreichte kein noch so gut gesetzter Rand weiter unten die Fläche darüber — Inhalt kam dort nie hin. Jetzt reißt der HStack den Rand oben auf und gibt den Platz kontrolliert zurück: Seitenleiste und Insel bekommen ihren Abstand zur Statusleiste explizit, der Inhalt bekommt ihn als Sicherheitsrand. Der Aufmacher nimmt ihn sich wieder — um genau diese Strecke höher und um genau diese Strecke größer, sodass unten alles bleibt, wo es war. Dazu ein knapper Verlauf oben, damit Uhrzeit und Batterie auch über einem hellen Bild lesbar bleiben. Und die Leiste oben fährt beim Scrollen weg. Nur sie: der Sicherheitsrand darunter bleibt stehen, sonst ruckte die Seite bei jedem Ein- und Ausblenden unter dem Finger weg. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
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> |
||
|
|
95723588c2 |
Nachgesehen — und dabei vier Fehler gefunden, zwei davon meine neuen
Der Simulator läuft wieder. Ich habe alles durchgeklickt, was ich dir in den letzten beiden Runden ungeprüft gegeben habe. Es war nicht in Ordnung. **Zwei Navigationsleisten übereinander.** Auf dem iPhone hat die Leiste unten fünf Plätze; alles darüber schiebt das System in ein „Mehr"-Verzeichnis — und dessen Einträge bekamen zwei Zurück-Pfeile, einen vom System und einen von uns. Statt das zu flicken, gibt es jetzt die **Bibliothek**, die du ohnehin wolltest: auf dem iPhone ein Eintrag, hinter dem alle Mediatheken stehen, auf dem iPad weiter jede einzeln in der Seitenleiste. Damit bleibt die Leiste bei fünf Einträgen, und das „Mehr" entfällt. **Kacheln in der Mediathek öffneten nichts.** Kein Fehler, keine Meldung — Antippen führte einfach ins Leere. Der Grund: in einem Stapel dürfen nicht beide Arten von Verknüpfung nebeneinander vorkommen. Sobald ein Bildschirm über eine **Ansichts**-Verknüpfung geschoben wurde, fanden die **Wert**-Verknüpfungen darin ihr Ziel nicht mehr. Jetzt ist alles wertbasiert, und das Ziel für Detailseiten steht **einmal je Stapel** an dessen Wurzel statt in jedem Bildschirm einzeln. **Der Lupenknopf in der Kopfzeile zeigte auf ein Ziel, das es nie gab.** Kein Bildschirm hat je ein `SearchRoute`-Ziel angemeldet — der Knopf tat seit jeher nichts. Er ist weg; die Suche hat ihren eigenen Platz in der Leiste. **Der Download-Dialog zeigte Schlüssel statt Namen** — „quality.hd" statt „720p". Die drei Übersetzungen fehlten schlicht. **Die Kontokarte wuchs aus dem Bild heraus.** Die Leiste unten in der Seitenleiste hat eine feste Höhe; meine aufklappende Karte schob die Zeile mit dem Namen nach unten hinaus. Jetzt hängt sie als Popover darüber — die Zeile bleibt stehen, wie bei JellyTV. Verkleinerte Downloads bei Jellyfin laufen: Stufen erscheinen, die Adresse stimmt, die Datei kommt an. Dass die Attrappe nur 28 kB liefert und die App das als „keine Videodatei" zurückweist, ist richtig so — die Prüfung tut, was sie soll. Zwei Gruben in der Attrappe nebenbei gestopft: sie kannte `/stream.mp4` nicht und meldete ihr Testbild als 360p, womit es gar keine kleineren Stufen geben konnte. 263 Tests grün. Diesmal alles angesehen: iPhone und iPad, Einstellungen, Bibliothek, Detailseite, Download-Dialog, Kontokarte. 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> |
||
|
|
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> |