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>
Flaro Web
Die Weboberfläche für Rechner, auf denen es keine App gibt — Windows vor allem.
Aufbau
| Ordner | Was |
|---|---|
app/ |
Das Frontend (Vite, TypeScript) |
server/ |
Ein Server ohne Abhängigkeiten, der die Anfragen weiterreicht und die gebauten Dateien ausliefert |
frontend/ |
Die erste Fassung als einzelne Datei. Läuft, wird abgelöst. |
jellyfin-theme/ |
Ein Kleid für Jellyfins eigene Oberfläche — die kleine Lösung von vorher |
Warum ein eigener Server?
Er löst zwei Dinge, an denen die einzelne Datei scheitert, und zwar unabhängig davon, wie gut das Frontend geschrieben ist:
- Herkunft. Anfragen laufen über
/apiund damit über dieselbe Adresse wie die Seite. Kein CORS, auch nicht beim Streamen. - Klartext und Verschlüsselung. Läuft der Medienserver über
httpund die Seite überhttps, blockiert der Browser den Stream. Dagegen kann keine Zeile im Frontend etwas tun — ein Server davor schon.
Nebenbei bleibt der Zugangsschlüssel beim Server, statt im Browser zu liegen.
Betreiben
cd app && npm install && npm run build
cd ../server && FLARO_SERVER=http://jellyfin:8096 node index.js
Dann auf http://localhost:8080. Der Port lässt sich über PORT ändern.
Entwickeln
cd app && FLARO_SERVER=http://jellyfin:8096 npm run dev
Vite übernimmt dabei das Weiterreichen, der eigene Server wird nicht gebraucht.