Ein Download sah aus wie eine Videodatei, nicht wie ein Film

Vier Dinge, die beim Ausprobieren auffielen.

**Die Plakate blieben grau.** Sie lagen auf der Platte, der Lader wollte
sie aber nicht: er verlangte von jeder Antwort eine HTTPURLResponse, und
eine Datei antwortet nicht so. Jede lokale Adresse kam als `nil` zurück.

**Eine Serie hatte ohnehin nie ein Plakat.** Heruntergeladen wird nie die
Serie, nur ihre Folgen — und deren Bild ist das Szenenbild, quer. Jetzt
kommt das Serienplakat unter dem Serienschlüssel mit und geht mit der
letzten Folge wieder.

**Zwanzig Folgen waren zwanzig Kacheln.** Jetzt steht die Serie einmal
da, darunter wie viele Folgen bereitliegen; welche genau, zeigt ihre
Seite — auch ohne Server, dann aus dem, was auf dem Gerät liegt.

**Das Bild wurde schwarz, sobald jemand auf „Bildfüllend" wechselte.**
Daran war ein eigener ViewModifier schuld, der zwischen
`ignoresSafeArea()` und gar nichts umschaltete. Genau das ist in SwiftUI
ein Zweig: der Baum darunter wurde neu gebaut, die Bildebene mit ihm.
`edges: []` ignoriert nichts und bleibt doch derselbe Modifikator.

Dazu „Als Nächstes": die Reihe begann am Anfang der Serie statt dort, wo
der Benutzer steht. Wer bei Folge 2 war und Folge 1 nie abgehakt hatte,
bekam Folge 1 vorgeschlagen — und das Abhaken half nicht, weil die
Kachel sich nur selbst einen Haken malte, statt zu verschwinden.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-10-12 20:20:27 +02:00
co-authored by Claude Opus 5
parent 8f0568cb6c
commit 79d7d04583
14 changed files with 388 additions and 63 deletions
@@ -395,9 +395,16 @@ public final class DownloadCoordinator {
forgetTransfer(item.ratingKey)
fileStore.remove(fileName: item.localFileName)
fileStore.remove(fileName: item.artworkFileName)
let serie = item.seriesKey
context.delete(item)
save()
reloadItems()
// **Das Serienplakat geht mit der letzten Folge.** Es hängt an
// keinem Eintrag — ohne diese Zeile bliebe es für immer liegen.
if let serie, !items.contains(where: { $0.seriesKey == serie }) {
fileStore.remove(fileName: fileStore.artworkFileName(forRatingKey: serie))
}
refreshUsage()
}
@@ -427,10 +434,15 @@ public final class DownloadCoordinator {
/// **Weil eine Reihe ohne Bilder keine Reihe ist.** Ohne Verbindung gibt
/// es keine Adresse, von der ein Plakat nachzuladen wäre; was nicht beim
/// Herunterladen mitkam, bleibt eine graue Fläche.
/// **Nach der Datei gefragt, nicht nach dem Eintrag.** Hier wurde erst
/// der Download gesucht und dann sein Dateiname gelesen — damit gab es
/// nur Bilder zu Dingen, die selbst heruntergeladen wurden. Das Plakat
/// einer **Serie** gehört aber keiner einzelnen Folge; es liegt unter
/// dem Serienschlüssel, zu dem es keinen Eintrag gibt und nie geben
/// wird.
public func artworkURL(forRatingKey key: String) -> URL? {
guard let name = items.first(where: { $0.ratingKey == key })?.artworkFileName,
fileStore.exists(fileName: name)
else { return nil }
let name = fileStore.artworkFileName(forRatingKey: key)
guard fileStore.exists(fileName: name) else { return nil }
return fileStore.url(forFileName: name)
}
@@ -439,19 +451,32 @@ public final class DownloadCoordinator {
/// **Beim Anlegen, nicht erst beim Fertigwerden.** Die Liste zeigt den
/// Eintrag, sobald er in der Warteschlange steht; bis dahin wäre sonst
/// nichts zu sehen. Es sind ein paar Kilobyte.
/// Der Schlüssel muss zu keinem Download gehören: unter dem Schlüssel
/// einer Serie liegt ihr Plakat, obwohl die Serie selbst nie
/// heruntergeladen wird.
public func fetchArtwork(for ratingKey: String, from url: URL) async {
guard let item = items.first(where: { $0.ratingKey == ratingKey }),
item.artworkFileName == nil
else { return }
let name = fileStore.artworkFileName(forRatingKey: ratingKey)
guard !fileStore.exists(fileName: name) else {
vermerken(name, bei: ratingKey)
return
}
guard let (daten, antwort) = try? await URLSession.shared.data(from: url),
(antwort as? HTTPURLResponse).map({ (200..<300).contains($0.statusCode) }) ?? true,
!daten.isEmpty
else { return }
let name = fileStore.artworkFileName(forRatingKey: ratingKey)
guard fileStore.store(daten, as: name) != nil else { return }
vermerken(name, bei: ratingKey)
}
/// Hält am Eintrag fest, dass es ein Bild gibt — damit es beim Entfernen
/// mitgeht. Zum Serienschlüssel gibt es keinen Eintrag; dann bleibt die
/// Datei stehen, bis die letzte Folge der Serie geht.
private func vermerken(_ name: String, bei ratingKey: String) {
guard let item = items.first(where: { $0.ratingKey == ratingKey }),
item.artworkFileName != name
else { return }
item.artworkFileName = name
save()
}