Reine Umbenennung, kein Verhalten geändert — bewusst als eigener Commit, damit „umbenannt" und „umgebaut" im Nachhinein auseinanderzuhalten sind. 712 mal `Flixy`, 778 mal `flixy`, 7 mal `FLIXY` in 169 Dateien, dazu 25 Pfade. Ziele: `Halo`, `HaloTV`, `HaloTopShelf`, `HaloUITests`, `HaloKit` mit `HaloDesign`, `HaloDownloads`, `HaloPlayback` und `HaloProbe`. `MediaKit`, `JellyfinKit` und `DownloadWidget` tragen den Namen nicht und bleiben. `docs/superpowers/specs/` ist ausgenommen. Datierte Entwurfsdokumente sind ein Protokoll getroffener Entscheidungen; sie nachträglich umzuschreiben hiesse, die Vergangenheit zu glätten. **Die Bundle-ID wandert mit** — ausdrücklich so gewollt, mit bekanntem Preis: `com.scarriffleservices.halo` ist für iOS eine *andere* App. Die installierte Fassung bleibt daneben stehen, mit Downloads und Anmeldung in ihrem Container; Halo startet leer. Einmal neu anmelden, heruntergeladene Folgen einmal neu laden. Dasselbe gilt für die App-Gruppe, die sich TV-App und Top Shelf teilen. **Eine Folge davon, die beim Entscheiden noch nicht auf dem Tisch lag:** der Aufräumschritt aus dem vorigen Commit kommt an die alten Plex-Token nicht mehr heran. Der Schlüsselbund grenzt nach Team und Bundle-Kennung ab — was unter `…flixy` liegt, ist für `…halo` unerreichbar. Erledigt wird es trotzdem, nur anders: **die alte App löschen.** iOS räumt deren Schlüsselbund-Einträge beim Deinstallieren mit ab. Der Code bleibt stehen, weil er für jeden Stand vor dieser Umbenennung richtig ist und im Jahr einen Startvergleich kostet. Die Projektdatei ist aus `project.yml` neu erzeugt, der alte Ordner gelöscht statt umbenannt — sie steht ohnehin nicht im Repo. Das Verzeichnis des Repos selbst heißt weiter `Flixy`; das umzubenennen ist ein Handgriff ausserhalb des Projekts. 368 Tests grün. iOS und tvOS bauen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
100 lines
4.0 KiB
Swift
100 lines
4.0 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 HaloDesign, 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)")
|
|
}
|
|
}
|
|
|
|
extension MediaLibrary.Kind {
|
|
/// Das Symbol, an dem man eine Mediathek erkennt.
|
|
///
|
|
/// **Eine Zuordnung, nicht drei.** Dieselbe Entscheidung stand bisher an
|
|
/// drei Stellen — in der Seitenleiste, im Zwischenbildschirm und am
|
|
/// Navigationsziel —, und die dritte war falsch: sie gab jeder Mediathek
|
|
/// „film", auch einer mit Serien. Drei Kopien einer Zuordnung halten sich
|
|
/// nicht von allein gleich.
|
|
public var symbol: String {
|
|
switch self {
|
|
case .movie: "film"
|
|
case .show: "tv"
|
|
case .music: "music.note"
|
|
case .photo: "photo"
|
|
case .liveTV: "dot.radiowaves.left.and.right"
|
|
case .other: "rectangle.stack"
|
|
}
|
|
}
|
|
}
|