Feat #1: Jellyfin-Anbindung — Bestand und Sichtungen abgleichen #13

Merged
florian.wilde merged 1 commits from feat/1-jellyfin-anbindung into main 2026-09-02 08:20:00 +00:00
Owner

Jellyfin einmal verbinden, danach übernimmt Flimmer Bestand und Sichtungen. Zugeordnet wird über die TMDB-Kennung, die Jellyfin zu jedem Item mitliefert — dieselbe Kennung, auf der Flimmers Katalog ohnehin aufbaut. Kein Raten über Titel und Jahreszahlen: die Zuordnung ist eindeutig oder sie findet nicht statt.

Umfang: Stufe 1 und 2

Die Abnahmeliste des Issues deckt genau diese ab. Stufe 3 (Zurückschreiben) ist nicht dabei — sie kommt in der Abnahme nicht vor, ist laut Beschreibung standardmäßig aus und bräuchte eine Versionsweiche für den umgezogenen Endpunkt, die sich ohne echten Server nicht verlässlich prüfen lässt. Das Feld writeBack ist wie beschrieben angelegt, die Logik dahinter bleibt leer.

Was dazugekommen ist

prisma/schema.prisma MediaServerConnection, MediaServerItem, Watch.source, zwei Enums
src/lib/jellyfin.ts Client: Anmeldung, Bestand, Serien-Kopfdaten, Verbindungsprüfung
src/lib/mediaserver.ts Zuordnung, Bestand, Sichtungen, Bericht
src/lib/secrets.ts AES-256-GCM, Schlüssel aus AUTH_SECRET
Einstellungen → Medienserver verbinden, prüfen, abgleichen, Bericht, Schalter
src/lib/sync.ts läuft im täglichen Abgleich mit
docs/ANLEITUNG.md eigener Abschnitt samt der Rewatch-Grenze

Die beiden Haken aus dem Issue

Anmeldung statt API-Schlüssel. Mit einem Schlüssel liefert Jellyfin den Bestand ohne UserData — also ohne Gesehen-Status, ohne Fehlermeldung. Der Verbindungstest prüft ausdrücklich nach und sagt es deutlich. Nachgestellt und geprüft (siehe unten).

Der Endpunkt zum Zurückschreiben betrifft nur Stufe 3, die hier nicht dabei ist.

Entdoppelt wird zweifach

Episoden über den vorhandenen Index (userId, episodeId, run). Filme brauchen eine eigene Prüfung: dort ist episodeId null, und Postgres behandelt NULL in eindeutigen Indizes als jeweils verschieden — der Index greift dort gar nicht.

Einbahnstraße

Der Abgleich ergänzt und löscht nie. Fehlende Titel werden nur mit gesetztem Häkchen aufgenommen — im täglichen Lauf also nie, eine Mediathek mit achthundert Filmen soll nicht nachts ungefragt zur Bibliothek werden.

Geprüft

Elf Tests gegen nachgebildete Antworten, ohne echten Server — inklusive eines Items ohne TMDB-Kennung und eines mit einer Kennung, die nicht im Katalog steht. 78 Tests grün, Typecheck sauber.

Zusätzlich der ganze Klickweg gegen einen echten HTTP-Server im Produktionsbau:

Schritt Ergebnis
Verbinden „Heimserver" erkannt, ✓ Gesehen-Status kommt mit
Token in der Datenbank s6sGpiNTHbTYZGNd.… — verschlüsselt
Abgleich 5 gelesen, 3× „hab ich", 1 nicht zuzuordnen (Item ohne Kennung)
Sichtungen 2 angelegt, Termin 2026-07-14 20:15 aus Jellyfin
Zweiter Lauf weiterhin 2 Sichtungen — nichts doppelt
Server ohne UserData „Der Gesehen-Status fehlt. Das passiert, wenn die Anmeldung über einen API-Schlüssel …"

Ein Zwischenergebnis war lehrreich: beim ersten Lauf gegen echte Seed-Daten kamen 0 Sichtungen heraus. Das war kein Fehler, sondern korrektes Entdoppeln — die betroffenen Episoden waren im Seed längst als gesehen markiert. Erst mit Inhalten, die noch offen waren, zeigte sich der Import.

Die Entwicklungsdatenbank ist nach den Proben wieder im Ausgangszustand.

Closes #1

Jellyfin einmal verbinden, danach übernimmt Flimmer Bestand und Sichtungen. Zugeordnet wird über die **TMDB-Kennung**, die Jellyfin zu jedem Item mitliefert — dieselbe Kennung, auf der Flimmers Katalog ohnehin aufbaut. Kein Raten über Titel und Jahreszahlen: die Zuordnung ist eindeutig oder sie findet nicht statt. ## Umfang: Stufe 1 und 2 Die Abnahmeliste des Issues deckt genau diese ab. **Stufe 3 (Zurückschreiben) ist nicht dabei** — sie kommt in der Abnahme nicht vor, ist laut Beschreibung standardmäßig aus und bräuchte eine Versionsweiche für den umgezogenen Endpunkt, die sich ohne echten Server nicht verlässlich prüfen lässt. Das Feld `writeBack` ist wie beschrieben angelegt, die Logik dahinter bleibt leer. ## Was dazugekommen ist | | | |---|---| | `prisma/schema.prisma` | `MediaServerConnection`, `MediaServerItem`, `Watch.source`, zwei Enums | | `src/lib/jellyfin.ts` | Client: Anmeldung, Bestand, Serien-Kopfdaten, Verbindungsprüfung | | `src/lib/mediaserver.ts` | Zuordnung, Bestand, Sichtungen, Bericht | | `src/lib/secrets.ts` | AES-256-GCM, Schlüssel aus `AUTH_SECRET` | | Einstellungen → Medienserver | verbinden, prüfen, abgleichen, Bericht, Schalter | | `src/lib/sync.ts` | läuft im täglichen Abgleich mit | | `docs/ANLEITUNG.md` | eigener Abschnitt samt der Rewatch-Grenze | ## Die beiden Haken aus dem Issue **Anmeldung statt API-Schlüssel.** Mit einem Schlüssel liefert Jellyfin den Bestand ohne `UserData` — also ohne Gesehen-Status, ohne Fehlermeldung. Der Verbindungstest prüft ausdrücklich nach und sagt es deutlich. Nachgestellt und geprüft (siehe unten). **Der Endpunkt zum Zurückschreiben** betrifft nur Stufe 3, die hier nicht dabei ist. ## Entdoppelt wird zweifach Episoden über den vorhandenen Index `(userId, episodeId, run)`. **Filme brauchen eine eigene Prüfung:** dort ist `episodeId` null, und Postgres behandelt NULL in eindeutigen Indizes als jeweils verschieden — der Index greift dort gar nicht. ## Einbahnstraße Der Abgleich ergänzt und löscht nie. Fehlende Titel werden nur mit gesetztem Häkchen aufgenommen — im täglichen Lauf also nie, eine Mediathek mit achthundert Filmen soll nicht nachts ungefragt zur Bibliothek werden. ## Geprüft **Elf Tests** gegen nachgebildete Antworten, ohne echten Server — inklusive eines Items ohne TMDB-Kennung und eines mit einer Kennung, die nicht im Katalog steht. **78 Tests grün**, Typecheck sauber. Zusätzlich der ganze Klickweg gegen einen **echten HTTP-Server** im Produktionsbau: | Schritt | Ergebnis | |---|---| | Verbinden | „Heimserver" erkannt, ✓ Gesehen-Status kommt mit | | Token in der Datenbank | `s6sGpiNTHbTYZGNd.…` — verschlüsselt | | Abgleich | 5 gelesen, 3× „hab ich", 1 nicht zuzuordnen (Item ohne Kennung) | | Sichtungen | 2 angelegt, Termin `2026-07-14 20:15` aus Jellyfin | | Zweiter Lauf | weiterhin 2 Sichtungen — nichts doppelt | | Server ohne `UserData` | „Der Gesehen-Status fehlt. Das passiert, wenn die Anmeldung über einen API-Schlüssel …" | Ein Zwischenergebnis war lehrreich: beim ersten Lauf gegen echte Seed-Daten kamen **0 Sichtungen** heraus. Das war kein Fehler, sondern korrektes Entdoppeln — die betroffenen Episoden waren im Seed längst als gesehen markiert. Erst mit Inhalten, die noch offen waren, zeigte sich der Import. Die Entwicklungsdatenbank ist nach den Proben wieder im Ausgangszustand. Closes #1
florian.wilde added 1 commit 2026-09-02 07:25:25 +00:00
Jellyfin anbinden: Bestand und Sichtungen uebernehmen
CI / pruefen (pull_request) Canceled after 0s
dd2b7dd48b
Zugeordnet wird ueber die TMDB-Kennung, die Jellyfin zu jedem Item
mitliefert — dieselbe Kennung, auf der Flimmers Katalog ohnehin aufbaut. Eine
unscharfe Suche ueber Titel und Jahr ist damit nicht noetig: die Zuordnung ist
eindeutig oder sie findet nicht statt. Was Jellyfin ohne Kennung fuehrt,
landet im Bericht statt falsch zugeordnet zu werden.

Angemeldet wird mit Benutzername und Passwort, nicht mit einem API-Schluessel.
Mit einem Schluessel liefert Jellyfin den Bestand ohne UserData, also ohne
Gesehen-Status — und zwar ohne Fehlermeldung. Der Verbindungstest prueft
deshalb ausdruecklich nach und sagt es, statt den Abgleich stumm ins Leere
laufen zu lassen. Das Zugangstoken liegt mit AES-256-GCM verschluesselt in der
Datenbank; abgeleitet aus AUTH_SECRET.

Der Abgleich ist eine Einbahnstrasse: er ergaenzt, was auf dem Server liegt
oder dort gelaufen ist, und loescht nie etwas, das in Flimmer steht. Fehlende
Titel werden nur aufgenommen, wenn das Haekchen gesetzt ist — im taeglichen
Lauf also nie.

Entdoppelt wird zweifach: Episoden ueber (userId, episodeId, run), Filme
ausdruecklich per Nachschlagen. Bei Filmen ist episodeId null, und Postgres
behandelt NULL im eindeutigen Index als jeweils verschieden — der Index greift
dort nicht.

Watch.source haelt fest, was von wo kam. Ohne diese Angabe liesse sich spaeter
nicht mehr sagen, welche Sichtung aus Jellyfin stammt.

Umgesetzt sind Stufe 1 und 2. Stufe 3 (Zurueckschreiben) kommt in der
Abnahmeliste nicht vor und braeuchte eine Versionsweiche, die sich ohne echten
Server nicht verlaesslich pruefen laesst; das Feld writeBack ist angelegt, die
Logik dahinter bleibt leer.

Elf Tests gegen nachgebildete Antworten. Zusaetzlich der ganze Klickweg gegen
einen echten HTTP-Server im Produktionsbau: verbinden, pruefen, abgleichen,
zweiter Lauf ohne Dubletten, und der Warnfall ohne UserData.

Refs #1
florian.wilde merged commit 2deb7e1aa5 into main 2026-09-02 08:20:00 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: florian.wilde/flimmer#13