Ein Bild, wo bisher nur eine Zahl stand
Wer zur Zeitleiste greift, sieht jetzt das Standbild der Stelle — im Web beim Überfahren, in der App beim Ziehen. **Erzeugt wird beim Abspielen, nicht auf Vorrat.** Ein Lauf über 453 Filme und 4213 Folgen wäre ein Abend ffmpeg und mehrere Gigabyte für Titel, die niemand anfasst. Startet jemand einen Titel, stellt sich die Datei hinten an; bis zum ersten Griff zur Leiste ist der Lauf durch. Gemessen an einer erzeugten Datei: 191 ms für 250 Sekunden Film, ein Zweistünder liegt damit bei etwa fünf Sekunden. **Ein ffmpeg-Aufruf, nicht siebenhundert.** `fps=1/10,scale=320:-2, tile=10x10` macht aus einem Durchlauf fertige Blätter zu hundert Kacheln. Bezahlbar ist das durch `-skip_frame nokey` — dieselbe Abkürzung wie beim Abtasten der Abspannhelligkeit: nur Keyframes dekodieren. Dass die Vorschau dadurch um zwei Sekunden danebenliegen kann, sieht niemand. **Der Spieler lädt ein Blatt und verschiebt danach nur den Ausschnitt.** Beim Fahren über die Leiste wird kein einziger Abruf mehr fällig. Die Rechnung Sekunde → Kachel steht an drei Stellen — Server, Web, App — und deshalb auch dreimal dieselbe; weicht eine ab, hält man die Vorschau für ein unscharfes Bild statt für einen Fehler. Neun Prüfungen decken die Ränder ab: Sekunde null, letzte Kachel eines Blattes, erste des nächsten, und hinter dem Ende. **Die Kachelmasse werden gemessen, nicht gerechnet.** `scale=320:-2` hält das Seitenverhältnis, ein Breitbildfilm ergibt also flachere Kacheln. Geraten wäre jede Kachel um ein paar Bildpunkte verschoben. Aufgeräumt wird mit dem Transcode-Cache: der Namensanfang trägt Kennung und Änderungszeit der Mediendatei, verwaiste Blätter sind daran ohne Tabelle erkennbar. Der Ordner taucht unter „Speicher" auf. **Und die Pfeiltasten spulen wieder.** Hatte die Zeitleiste den Fokus, überliess der Spieler die Pfeile dem Browser — der verschiebt `value` und löst `input` aus, aber `change` kommt bei Tastatureingabe nicht verlässlich, also wurde nie gesprungen. Die Ausnahme fällt weg; Pfeil heisst überall ±10 Sekunden. Nativ wären es ohnehin 100 Millisekunden gewesen, weil `step` auf 0,1 steht. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ed13d5fa43
commit
68abd71074
@@ -308,6 +308,32 @@ public actor FlaroBackend: MediaBackend {
|
||||
/// liefert, und rechnet nicht um. Ein Plakat wiegt achtzig Kilobyte; ein
|
||||
/// Server, der jede Kachel einzeln skaliert, spart Leitung und kostet
|
||||
/// Rechenzeit, die auf einem NAS knapper ist.
|
||||
/// Wie die Vorschaubilder einer Datei liegen.
|
||||
///
|
||||
/// **Der Server erzeugt sie beim Abspielen.** Steht noch nichts da,
|
||||
/// antwortet er mit `fertig: false` — dann gibt es hier nichts, und
|
||||
/// der Spieler fragt später noch einmal.
|
||||
public func vorschauplan(fileId: String) async -> Vorschauplan? {
|
||||
guard let auskunft = try? await client.send(
|
||||
FlaroRequest(path: "/api/v1/files/\(fileId)/vorschau"),
|
||||
as: Wire.Vorschau.self), auskunft.fertig
|
||||
else { return nil }
|
||||
|
||||
return Vorschauplan(
|
||||
intervall: auskunft.intervall,
|
||||
spalten: auskunft.spalten,
|
||||
zeilen: auskunft.zeilen,
|
||||
kachelBreite: Double(auskunft.kachelBreite),
|
||||
kachelHoehe: Double(auskunft.kachelHoehe),
|
||||
blaetter: auskunft.blaetter)
|
||||
}
|
||||
|
||||
/// **Ohne Kopfzeilen, wie bei den Plakaten.** Die Anmeldung hängt am
|
||||
/// Cookie; der Bildlader holt die Adresse roh und kommt damit durch.
|
||||
public nonisolated func vorschaublatt(fileId: String, blatt: Int) -> URL? {
|
||||
client.url(path: "/api/v1/files/\(fileId)/vorschau/\(blatt).jpg")
|
||||
}
|
||||
|
||||
public nonisolated func artworkURL(path: String?, width: Int, height: Int) -> URL? {
|
||||
rawImageURL(path: path)
|
||||
}
|
||||
|
||||
@@ -261,3 +261,16 @@ enum Wire {
|
||||
struct FavouriteRequest: Encodable, Sendable { let favourite: Bool }
|
||||
struct RatingRequest: Encodable, Sendable { let rating: Int? }
|
||||
}
|
||||
|
||||
extension Wire {
|
||||
/// Was der Server über die Vorschaubilder sagt.
|
||||
struct Vorschau: Decodable, Sendable {
|
||||
let fertig: Bool
|
||||
let intervall: Double
|
||||
let spalten: Int
|
||||
let zeilen: Int
|
||||
let kachelBreite: Int
|
||||
let kachelHoehe: Int
|
||||
let blaetter: Int
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user