Files
Flaro/Flixy/Session/MediaItem+Presentation.swift
T
ScarriffleandClaude Opus 5 87f23afe90 Die Startseite am Fernseher — und die Ladeschicht nur einmal
Aufmacher, Reihen, Mediatheken, Raster. Der sichtbare Teil ist neu; der
Teil darunter ist derselbe wie am Telefon, und das war die eigentliche
Arbeit.

**Herausgelöst statt kopiert.** `HomeFeed` und `LibraryFeed` tragen jetzt,
was vorher in `HomeView` und `LibraryView` stand: laden, ordnen, den
Aufmacher auslosen, den Zwischenspeicher abziehen, entdoppeln, nach einem
Fehlschlag sperren. Das zu kopieren wäre der schlimmste Ausgang dieses
Vorhabens gewesen — an mindestens drei dieser Stellen klebte schon je ein
Fehler, der sich als „es sind nicht alle Filme drin" äusserte und sonst
nichts. Einer davon, das Los des Aufmachers an der Position statt am
Titel, wäre in einer Kopie ein zweites Mal zu finden gewesen.

Mitgewandert sind `rowTitle`, `rowArtworkPath`, `posterArtworkPath`,
`runtimeLabel` und `HeroArtwork`: reine Ableitungen, die in einer
Ansichtsdatei lagen und deshalb nur das Telefon übersetzte.

**Nachgeladen wird am Fokus, nicht am Erscheinen.** Die alte Regel hiess
„die letzten fünf" und hing daran, dass eine Kachel sichtbar wird. Beim
ersten Zeichnen erscheinen aber alle sechzig, auch die letzten fünf — das
Nachladen lief sofort los statt beim Blättern. Auf einem Raster aus sechs
Spalten sind fünf Einträge ausserdem nicht einmal eine Zeile. `PagingWindow`
rechnet deshalb in Zeilen; sechs Tests halten es fest, darunter der Fall,
der die alte Regel entlarvt.

**Der Fokus setzt den Aufmacher.** Wer durch „Zuletzt hinzugefügt"
wandert, sieht oben, was er gerade anschaut. Kein Wechsel im Sieben-
sekundentakt wie am Telefon: vor einem Fernseher wäre ein Bild, das sich
von allein austauscht, während man es liest, eine Zumutung.

**Drei Dinge, die erst der Simulator zeigte.** Die Seiten müssen
bildfüllend laufen, weil ein Aufmacher, der achtzig Punkte vor der
Bildschirmkante aufhört, nach Fehler aussieht — also trägt `screenMargin`
auf tvOS den Überscan mit. Die Karten brauchen einen Fokus-Rückruf, weil
`\.isFocused` den nächsten fokussierbaren Vorfahren meint und das in einer
Reihe aus Karten nicht verlässlich die Karte ist. Und der Bau scheiterte
an „Unable to resolve module dependency: VLCKit" — der Abhängigkeits-
Sucher prüft `canImport(FlixyVLC)` auch für ein Ziel, das es nie verlinkt.
Ein `&& !os(tvOS)` genügt; das Paket selbst bleibt unverändert.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-29 19:36:49 +02:00

80 lines
3.3 KiB
Swift

import MediaKit
/// Welcher Titel und welches Bild auf eine Kachel gehören.
///
/// **Hier und nicht bei einer Ansicht.** Das stand in `MediaRow.swift` — und
/// damit in einer Datei, die nur das Telefon übersetzt. Der Fernseher zeigt
/// dieselben Kacheln mit denselben Rückfallketten; sie dort noch einmal
/// hinzuschreiben hiesse, zwei Stellen gleich zu halten, an denen die Fehler
/// erst nach Wochen auffallen: ein Plakat, das bei einer Staffel grau bleibt,
/// sieht nicht nach einem Fehler aus, sondern nach einem Server ohne Bild.
///
/// Reine Ableitungen aus `MediaItem`, keine Ansicht — deshalb neben den
/// übrigen Modellen und nicht in FlixyDesign, das `MediaItem` gar nicht kennt.
extension MediaItem {
/// Der Titel auf einer Kachel in einer Reihe.
///
/// Groß steht, worum es geht: bei einer Folge ihr eigener Titel, bei
/// einer Staffel der Name der Serie. Die Einordnung — Serie, Staffel —
/// gehört darunter. „Staffel 10" allein als Überschrift sagt nichts
/// darüber, welche Serie gemeint ist.
var rowTitle: String {
switch kind {
case .episode: title
// Die Serie steht je nach Antwort mal unter `parentTitle`, mal unter
// `seriesTitle`. Fehlt beides, bleibt „Staffel 2" — besser als
// gar keine Beschriftung.
case .season: parentTitle ?? seriesTitle ?? title
default: title
}
}
/// Welches Bild auf die Querformat-Kachel gehört.
///
/// Bei Folgen ist `thumb` das Szenenbild der Folge — genau richtig. Bei
/// Filmen ist `thumb` das Hochkant-Plakat, das im Querformat beschnitten
/// würde; dort ist `art` (das Hintergrundbild) die bessere Wahl.
var rowArtworkPath: String? {
switch kind {
case .episode: thumb ?? seriesThumb
// Eine Staffel hat bei Jellyfin oft gar kein eigenes Bild — sie leiht
// sich das der Serie. Ohne diesen Rückfall blieb „Nachtstreife,
// Staffel 7" eine graue Fläche, obwohl die Serie ein Plakat hat.
case .season: art ?? thumb ?? seriesArt ?? seriesThumb
default: art ?? thumb
}
}
/// Welches Bild auf die Hochkant-Kachel gehört.
///
/// Bei Folgen das Plakat der Serie statt des Szenenbilds: ein 16:9-Bild
/// hochkant beschnitten zeigt einen zufälligen Ausschnitt und ist zum
/// Wiedererkennen wertlos.
var posterArtworkPath: String? {
switch kind {
case .episode: seriesThumb ?? parentThumb ?? thumb
// Das eigene Staffelplakat zuerst: es zeigt die Staffel. Gibt es
// keines, ist das der Serie allemal besser als nichts.
case .season: thumb ?? seriesThumb
default: thumb
}
}
}
/// Laufzeiten als Text.
///
/// Aus derselben Ansichtsdatei hierher geholt wie die Kachel-Ableitungen
/// darüber: am Fernseher steht dieselbe Zeile unter demselben Aufmacher.
extension Duration {
/// Laufzeit als „2 Std 46 Min" bzw. „46 Min".
var runtimeLabel: String {
let totalMinutes = Int(components.seconds / 60)
let hours = totalMinutes / 60
let minutes = totalMinutes % 60
if hours > 0 {
return String(localized: "runtime.hoursMinutes \(hours) \(minutes)")
}
return String(localized: "runtime.minutes \(minutes)")
}
}