ef2596e2bb5f6ffe2da9fedb6ca97d30afd1214c
19
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ef2596e2bb |
Was heruntergeladen wird, muss der Systemplayer öffnen können
**Vorbereitung dafür, dass VLC verschwindet.** Online ist der Fall längst
gelöst: steht nur die Schachtel im Weg, packt der Server um — dieselben
Ströme, eine MP4-Hülle. Offline gibt es diesen Ausweg nicht. Was einmal als
Matroska auf dem Gerät liegt, kann hinterher niemand mehr umpacken, und ohne
zweite Engine bliebe die Datei schwarz.
Also entscheidet sich das beim Herunterladen. Fragt jemand das Original an und
AVFoundation könnte die Datei nicht öffnen, wird `remuxDownloadURL` benutzt:
`/Videos/{id}/stream.mp4` mit `allowVideoStreamCopy` und
`allowAudioStreamCopy`. Der Server reicht durch, was passt, und rechnet nur
um, was nicht passt — das Bild bleibt das Original, nur DTS oder TrueHD
werden zu AC3 oder AAC.
Drei Dinge hängen daran und sind mitgezogen:
- Die **Größe** ist dann keine Zusage mehr: dieselben Ströme in einer anderen
Hülle wiegen anders. `expectedIsExact` fällt auf falsch, und es gilt wieder
die weiche Prüfung.
- Der **Container** im Offline-Abzug ist „mp4", nicht die alte Schachtel.
Sonst entschiede der Player anhand einer Datei, die es so nicht mehr gibt.
- Der **Tonkodierer** bleibt leer. Ob aus DTS dann AAC oder AC3 wird, sagt der
Server vorher nicht; eine Behauptung wäre schlechter als die Lücke, und die
Spuren liest der Player ohnehin aus der Datei.
**Die Frage „kann das der Systemplayer?" steht jetzt an einer Stelle.**
`DeviceCapabilities.opens(container:videoCodec:audioCodec:)` — dieselbe Liste,
nach der auch beim Abspielen entschieden wird. Was der Server nicht nennt,
gilt als passend: vorsichtshalber umzupacken hiesse, aus einer tadellosen MP4
eine neu gerechnete Datei zu machen. Fünf Tests.
NOCH NICHT AN EINEM ECHTEN SERVER GEPRÜFT: ob Jellyfin das Kopieren hier
tatsächlich wählt, verrät erst die Dateigröße des ersten Downloads.
140 Tests in FlixyPlayback, iOS und tvOS bauen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
9e7f3393b4 |
DTS, TrueHD und Vorbis gehen jetzt an den Server statt an VLC
Bisher entschied der Decider: steht nur die Schachtel im Weg, wird
umgepackt — stimmt ein Codec nicht, geht es an VLC. Das war beim Ton zu
grob gedacht.
Beim Umpacken nennt das Geräteprofil dem Server, was ankommen darf:
"VideoCodec": "h264,hevc"
"AudioCodec": "aac,ac3,eac3,mp3"
Trifft ein Strom eine dieser Angaben, kopiert der Server ihn; trifft er
sie nicht, rechnet er **genau diesen einen** um. Eine Matroska mit HEVC
und DTS braucht also kein VLC: das Bild geht Byte für Byte durch, und nur
die Tonspur wird nach AC3 oder AAC kodiert. Das kostet Tonqualität und
sonst nichts.
Die Bedingung heisst deshalb nicht mehr „nur die Schachtel", sondern
„das Bild bleibt": Container und Ton dürfen im Weg stehen, der
Videocodec nicht. Dazu musste die Prüfung der Schachtel aufhören, sich
immer zu stellen — eine MP4 mit DTS stand mit einem reinen Tonhindernis
da und fiel trotzdem durch die Liste umpackbarer Container.
**Der grössere Gewinn liegt auf dem Apple TV.** Dort gibt es VLC nicht,
und bisher landete eine Datei mit DTS-Ton beim vollen Neurechnen — Bild
inbegriffen, obwohl mit dem Bild nichts war. Jetzt bleibt das Bild
unberührt. Auf iPhone, iPad und Mac verhält sich derselbe Film jetzt
gleich, und zwar über AVPlayer: mit Bild-in-Bild, AirPlay und
Sperrbildschirm, die es auf dem VLC-Weg nie gab.
Geprüft im Simulator gegen die Attrappe mit `--vlc` (meldet DTS): der
Film läuft, und in der Leiste steht jetzt der AirPlay-Knopf, der vorher
fehlte — der sichtbarste Beleg dafür, dass AVPlayer spielt und nicht VLC.
„Umgepackt" und „umgepackt, Ton umgerechnet" sind in den technischen
Infos und in der Diagnose auseinandergehalten. Ein Weg, der den Ton neu
kodiert, darf nicht „ohne Neukodierung" heissen.
Was bleibt bei VLC: nicht unterstützte **Bild**codecs, allen voran AV1,
und Dateien, die der Server noch nicht untersucht hat. Dafür gäbe es nur
das volle Neurechnen — eine eigene Entscheidung, nicht diese.
iOS und tvOS bauen, 463 Tests grün.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
12bfc9e530 |
Abspielen am Fernseher — mit Apples Player und unseren Spuren
Schritt sieben. Wiedergabe, Spurwahl, Titelzeile, „Intro überspringen". **Die schwierige Stelle war die Spurwahl.** Unsere Ton- und Untertitelwahl geht als `audioStreamIndex` an Jellyfin und verlangt bei einem Transcode einen **neuen Stream**. Apples eingebauter Spurwähler zeigt dagegen die Auswahlgruppen des **laufenden** Streams — beim Transcode genau eine. Er ist die richtige Oberfläche für die falsche Liste. Unsere Menüs sitzen deshalb über `transportBarCustomMenuItems` daneben; `customInfoViewControllers` wäre falsch, denn die Tonspur ist ein Bedienelement der Wiedergabe und kein Begleitmaterial. **Der Neuaufbau wird angesagt, nicht erklärt.** Jeder Eintrag, der Sekunden mit schwarzem Bild kostet, trägt das als Untertitel — vorher, nicht hinterher. Wer drückt, sieht den Haken sofort wandern, weil das Menü sich schliesst und darin kein Platz für einen Kreisel ist. Das schwarze Bild deckt eine Überlagerung in `contentOverlayView` zu, die hinter den Bedienelementen liegt und keinen Fokus nimmt — `customOverlayViewController` täte beides nicht und machte die Fernbedienung währenddessen tot. Und solange umgebaut wird, sind alle Einträge gesperrt: sonst räumt `restart()` eine Engine ab, die eine andere gerade baut. **Die Regel dahinter ist jetzt prüfbar.** „Wann kostet ein Spurwechsel einen Neuaufbau" lag bisher mitten im Vollzug von `selectAudioStream` und liess sich nur an einem echten Server mit mehreren Tonspuren auslösen. Als `TrackMenu` steht sie neben den übrigen Entscheidungsregeln, mit sieben Tests — darunter der Fall, auf den der Kommentar dort seit jeher hinweist: stimmt die Spurzahl nicht überein, ist die Zuordnung über die Reihenfolge falsch, und es muss neu aufgebaut werden. **Zwei stille Fehler nebenbei.** Die Tonsitzung stand hinter `#if os(iOS)` und wäre auf tvOS nie gesetzt worden — verfügbar ist sie dort seit 9.0, man hätte es nur daran gemerkt, dass sich die Wiedergabe gegen anderen Ton nicht durchsetzt. Und `PlayerModel.downloads` ist jetzt optional statt eines leeren Koordinators: ein leerer bräuchte einen `ModelContainer`, und der ist das Einzige, wofür diese App SwiftData benutzt. **Belegt statt vermutet.** Im Simulator lief die Wiedergabe gegen die Attrappe, und die Titelzeile zeigte „Unbekannt". Statt an AVKit zu drehen, hat ein Protokolleintrag die Frage beantwortet: `externalMetadata` wird gesetzt, der Wert stimmt, und „Unbekannt" ist wörtlich das, was die Attrappe für unbekannte Kennungen liefert. Geblieben ist davon nur die Erkenntnis, dass die Beschriftung auf das Item warten muss — beim Austausch des Players gibt es noch keines. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
4323c47db6 |
Schneller anfangen, dann hinaufgehen
Es dauerte eine halbe Minute bis zum ersten Bild. Der Grund war nicht die Leitung, sondern der Server: „Auto\" begann mit acht Megabit, und das heisst 1080p von der ersten Sekunde an. So lange braucht er, um das zu rechnen. Jetzt wird mit drei Megabit begonnen — also 720p. Das laeuft in wenigen Sekunden an, sieht einen Augenblick lang weicher aus, und von dort geht es hinauf, sobald sich zeigt, dass die Leitung mehr traegt. Dabei steckt eine Falle, die den ganzen Gewinn aufgezehrt haette: der Server baut seine Leiter aus der Bestellung. Wer mit drei Megabit anfaengt, bekommt eine Leiter, deren oberste Sprosse drei Megabit ist — und der billige Hebel bewegt sich nur **innerhalb** dieser Leiter. Aus dem schnellen Start waere ein Film geworden, der den ganzen Abend in 720p bleibt. Hoeher geht es deshalb ueber einen Neuaufbau, und der braucht Regeln: nicht zu frueh, nicht nach einem Ruckler, nicht ohne Vorrat, nicht oefter als zweimal. Sie stehen in `UpgradeRule` — reiner Typ, dreizehn Tests, wie beim Absenken. Und deutlich zurueckhaltender als dort: ein zu spaetes Absenken bedeutet Ruckeln, ein zu spaetes Hinaufschalten ein etwas weicheres Bild. Das erste ist ein Fehler, das zweite eine Geschmacksfrage. Bei Live-TV wird nicht hinaufgegangen: dort kostet jeder Neuaufbau den Tuner, und ein schaerferes Bild ist diesen Einsatz nicht wert. `StallLedger` kann jetzt sagen, wann der letzte Ruckler war — das brauchte die Regel, und „noch nie gestockt\" ist etwas anderes als „vor langer Zeit\". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
62691fc2bc |
Matroska geht jetzt an AVPlayer, nicht mehr an VLC
Ein Blu-ray-Rip liegt als Matroska vor, und AVFoundation oeffnet Matroska nicht — obwohl das Bild darin H.264 oder HEVC ist und der Ton AC3, also alles, was AVPlayer bestens kann. Im Weg stand allein die Verpackung. Dafuer gibt es jetzt einen dritten Weg neben Durchreichen und Neurechnen: umpacken. Dieselben Stroeme, Byte fuer Byte, in eine MP4. Kein Bild wird angefasst, es geht nichts an Qualitaet verloren, und den Server kostet es fast nichts. Entscheidend ist dabei `Protocol: http` statt `hls`. Eine Abspielliste laesst sich nicht vorausladen — AVPlayer holt Segment fuer Segment, und Apple laesst uns dazwischen nicht eingreifen. Eine fortlaufende Datei kommt ueber gewoehnliche Bereichsanfragen, und die kann man so weit vorausholen, wie man will. Der Vorratswunsch steht dort deshalb bei zehn Minuten statt bei einer. Der Gewinn ist gross: solche Titel bekommen Bild-in-Bild, AirPlay und die Systembedienung, die es bei VLC nicht gibt. Umgepackt wird nur, wenn **allein** die Schachtel im Weg steht. Stimmt auch nur ein Codec nicht — DTS, TrueHD, AV1 —, fehlt nicht der Zugang zur Datei, sondern das Koennen; dann bleibt es bei VLC. Sechs Tests halten die Grenze fest, in beide Richtungen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
983015b9ad |
Die Sprache lässt sich jetzt auch je Serie und je Mediathek sagen
Das Regelwerk stand schon und wurde abgefragt — es gab nur keinen Ort, an dem man etwas hineinschreiben konnte. Jetzt steht die Wahl auf der Serienseite und im Filter-Sheet der Mediathek, beide über denselben Auswahlknopf: zwei mit demselben Inhalt liefen unweigerlich auseinander. Bei der Mediathek in einem eigenen Abschnitt, nicht zwischen den Filtern. Ein Filter schränkt ein, was zu sehen ist; das hier bestimmt, was zu hören ist — dazwischengestellt läse es sich als „nur Titel mit deutscher Tonspur zeigen". Die Rangfolge Serie → Mediathek → allgemein ist dabei von den Einstellungen zu `AudioTrackChooser.resolve` gewandert. Sie ist eine Regel, keine Ablage, und im App-Ziel gibt es kein Unit-Test-Ziel — dort liesse sie sich nicht durchspielen. Sechs Tests decken sie jetzt ab, darunter der Fall, an dem man sonst lange sucht: eine leere Zeichenkette steht in den Einstellungen für „soll der Server entscheiden" und darf nicht als Sprache gelten, sonst schaltet sie die Ebene darüber stillschweigend ab. Nebenbei bekommt das Filter-Sheet die Mediathek selbst statt nur ihrer Art. Die Genres wurden vorher über irgendeine Mediathek dieser Art nachgeschlagen — bei zwei Filmmediatheken also über die falsche. Und der Puffer steht auf einer Minute statt auf 25 Sekunden. Es ist ein Mindestwunsch, keine Obergrenze; der harte Deckel liegt ohnehin woanders, nämlich darin, wie weit der Transcoder schon produziert hat. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
afb4a63655 |
Der Kern der Sprachwahl: Kürzel, die nicht lügen, und Abwägen statt Filtern
**`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> |
||
|
|
efed8717a3 |
„Auto" gibt nicht mehr auf, es versucht es kleiner
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> |
||
|
|
e324a474fd |
Watchdog heisst Watchdog, Engine heisst Engine
Eingedeutschte Fachbegriffe in den Kommentaren — „Wachhund" für Watchdog, „Motor" für Engine. Das liest sich albern und ist unklarer als das Original; niemand sagt Wachhund, wenn er einen Watchdog meint. Acht Stellen berichtigt. Gewöhnliche Sprache bleibt deutsch, der Stil der Kommentare ändert sich nicht. Dazu die Plex-Leiter messbar gemacht: `--leiter` baut dieselbe Anfrage zweimal, mit und ohne `autoAdjustQuality=1`, und zählt die Stufen. Die Zeile steht seit dem Umbau im Code und war nie geprüft — genau die Art ungeprüfter Annahme über Serververhalten, die bei Jellyfin vier Runden gekostet hat. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
a79d562642 |
Die Sicherheitsleine — der teure Weg, mit engen Regeln
Wenn der billige Hebel nichts ausrichtet, weil die Playlist nur eine Stufe hat oder schon die unterste läuft, bleibt nur der Neuaufbau mit kleinerer Bitrate: Transcoder anhalten, Motor abreißen, neu aushandeln, zurückspringen. Sekunden mit schwarzem Bild. **Ein Notnagel, keine Regelschleife.** Zu oft ausgelöst erzeugt er genau das Ruckeln, das er beheben soll. `DowngradeRule` ist deshalb eine Liste von Bedingungen, die alle gelten müssen, und keine Schwelle: nur bei „automatisch", nur wenn der billige Weg erschöpft ist, nur ab drei Rücklern in anderthalb Minuten oder einem einzelnen über fünfzehn Sekunden, nicht in den ersten zehn Sekunden, nicht binnen zehn Sekunden nach einem Sprung, höchstens alle drei Minuten, höchstens zweimal. Ein Sprung leert den Puffer **immer**. Ihn als Beleg für eine schlechte Leitung zu nehmen hieße, sich für das eigene Tun zu bestrafen. Das Urteil ist eine Aufzählung und kein `Bool`: „es hakt, aber wir tun nichts" ist ohne Grund von einem Fehler nicht zu unterscheiden. Vierzehn Tests schreiben jede Bedingung einzeln hin. Ist das Budget verbraucht, **wird es gesagt** statt lautlos weiter abgesenkt. Still bis zur untersten Sprosse zu gehen ist schlimmer, als einmal zuzugeben, dass die Leitung es nicht trägt. **Live-TV ist dabei**, wie gewünscht — aber mit Tuner-Abbau vorweg und Budget eins statt zwei. Er ist ein Stück Hardware, vier Stück gibt es, und ein doppelt belegter ist schlimmer als ein Stottern. Bekannte Grenze: `endLiveSession` meldet nicht, **ob** die Freigabe geklappt hat — das Protokoll gibt nichts zurück. Deshalb der eine Versuch und jeder Schritt im Protokoll, damit ein verlorener Tuner nachvollziehbar bleibt. Nebenbei aufgeräumt: die Wahl von Hand und die Sicherheitsleine teilen sich jetzt denselben Neuaufbau. Was sie unterscheidet, steht in `effectivePolicy` — die Wahl setzt `quality`, die Automatik `automaticCeiling`, und beides fließt dort zusammen. Die Automatik schreibt dabei nie `rememberedQuality` fort: eine schlechte Zugfahrt soll nicht dauerhaft das Wohnzimmer verschlechtern. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
52d6ebba43 |
„Automatisch" misst jetzt, statt die Einstellung durchzureichen
Bisher hieß `PlayerQuality.automatic` wörtlich: nimm die Zahl, die für WLAN oder Mobilfunk hinterlegt ist. Keine Messung, kein Nachregeln — die Entscheidung fiel einmal beim Start und galt bis zum Ende. `StreamHealth` beurteilt den Strom jetzt fortlaufend aus Proben im Sekundentakt. Ein reiner Typ: kein AVFoundation, keine Uhr, kein Netz. Damit lässt sich eine schlechte Zugfahrt als Zahlenreihe hinschreiben und nachsehen, was sie auslöst — zehn Tests tun genau das. **Welches Signal wann meldet**, ist der eigentliche Inhalt. Der Pufferverlauf ist der einzige echte Frühwarner: schrumpft er schneller, als die Zeit vergeht, kommt weniger herein als hinausgeht, und das sieht man zehn bis zwanzig Sekunden vor dem Rückler. `isLikelyToKeepUp` ist früh, aber sprunghaft — es zählt erst, wenn es anhält. Der Rücklerzähler meldet zu spät zum Regeln und bleibt trotzdem der Massstab, an dem sich hinterher ablesen lässt, ob es besser wurde. Die **gemessene** Bitrate steht bewusst nicht drin: auf einer kleinen Sprosse holt der Player wenig, ohne dass die Leitung schlecht wäre. Ausdrücklich kein Bandbreitentest. Der kostet Daten, ist beim Vorliegen veraltet und misst die Vergangenheit. Der Pufferverlauf ist die bessere Messung, weil er die einzige Größe misst, auf die es ankommt: lädt es schneller, als es läuft. Gehandelt wird über `preferredPeakBitRate` — der billige Hebel, der ohne Neuaufbau wirkt. Gezielt wird dabei auf eine Sprosse, **die es in `AVAsset.variants` wirklich gibt**: ein Wert zwischen zweien wählt dieselbe wie die untere und machte jede Anzeige darüber zur Lüge. Absichtlich unsymmetrisch — runter sofort, hinauf frühestens nach einer Minute Ruhe. Und getrennt von `quality`: die Automatik ruft **nicht** `select(quality:)`. Die Methode schreibt `rememberedQuality` fort und setzte eine manuelle Stufe — womit die Automatik sich selbst abschaltete und eine schlechte Zugfahrt dauerhaft das Wohnzimmer verschlechterte. Hat die Playlist nur eine Stufe, greift der Hebel ins Leere. Das ist kein Fehler, sondern der Fall, für den die Sicherheitsleine gebaut wird — bis dahin steht er wenigstens im Protokoll. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
903bfe5f63 |
Der Player weiß jetzt, wie es ihm geht
Bisher gab es über eine laufende Wiedergabe zwei Zahlen — gemessene Bitrate und verworfene Bilder —, und die standen nur zur Anzeige da. Die Frage „trägt die Leitung das gerade" war nicht zu beantworten, weder für den Nutzer noch für die App. **Die wichtigste neue Zahl ist `variantCount`.** Sie kommt aus `AVAsset.variants`, also vom Parser des Players, und beantwortet die Frage, ob es überhaupt etwas zum Umschalten gibt. Steht dort 1, greift jedes Nachregeln ins Leere — und genau das ist heute der Fall, weil die App bei beiden Servern eine Playlist mit einer Stufe bestellt. Dazu der Pufferstand aus `loadedTimeRanges`: der einzige echte Frühwarner. Schrumpft er schneller, als die Zeit vergeht, kommt weniger herein als hinausgeht — zehn bis zwanzig Sekunden, bevor das Bild steht. Alles andere meldet sich erst danach. **Verworfene Bilder werden jetzt über alle Protokolleinträge summiert.** Das Zugriffsprotokoll legt bei jedem Stufenwechsel einen neuen an, dessen Zähler bei null beginnt; wer nur `last` liest, hält eine Wiedergabe für makellos, die schon zwanzigmal Bilder verworfen hat. **Der Wachhund war blind für den häufigsten Fall.** Vierzig Sekunden am Stück ohne Daten brechen ab — aber er setzte sich bei jedem kurzen Erholen zurück, und zehn Rückler à dreißig Sekunden lösten ihn nie aus. `StallLedger` zählt daneben mit, wie viel Zeit im gleitenden Fenster fürs Warten draufging. Es bricht nichts ab, es stellt nur fest — und ist ein reiner Typ ohne Uhr und ohne AVFoundation, damit sich eine Folge von Rücklern als Zahlenreihe hinschreiben und das Urteil prüfen lässt. Sieben Tests, darunter genau der Fall, an dem der alte Wachhund vorbeisah. Gezählt wird nur der Weg vom Spielen ins Warten. Das anfängliche Puffern ist der Start, kein Rückler; würde es mitgezählt, hätte jede Wiedergabe eine Vorstrafe. Und zwei Kommentare sind berichtigt. Sie behaupteten, AVPlayer wechsle die Bitrate von selbst und das sei „die zugesagte automatische Qualitätsanpassung". Umschalten kann er nur zwischen Varianten, die in der Liste stehen. 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>
|
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
1ed64bf166 |
Phase 0: Projektgerüst, Design-Tokens und Navigation
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> |