docs: bebilderte Anleitung, API-Dokumentation und Screenshots

docs/ANLEITUNG.md führt einmal durch die ganze Anwendung — von der ersten
Anmeldung über das tägliche Abhaken bis zu Universen, Statistik und
Verwaltung, mit 18 Aufnahmen aus der laufenden Instanz.

Die Bilder entstehen mit npm run screenshots: aufgenommen mit doppelter
Pixeldichte für scharfe Schrift, abgelegt als WebP in halber Kantenlänge —
13 MB werden so zu 0,6 MB.
This commit is contained in:
Florian Wilde
2026-09-01 20:25:13 +02:00
parent cf38bd6854
commit 8d6f1d044a
24 changed files with 732 additions and 8 deletions
+190
View File
@@ -0,0 +1,190 @@
# Flimmer — REST-API
Alles, was die Oberfläche kann, geht auch über die Schnittstelle: Bibliothek
lesen und pflegen, Episoden abhaken, suchen, Kalender und Statistik abrufen,
Universen verwalten, Daten exportieren.
Die maschinenlesbare Beschreibung liegt unter
`GET /api/v1/openapi.json` (OpenAPI 3.1) — damit lassen sich Clients erzeugen
oder die Endpunkte in Insomnia, Bruno oder Postman importieren.
## Anmeldung
Jede Anfrage trägt einen persönlichen API-Schlüssel im `Authorization`-Kopf:
```bash
curl -H "Authorization: Bearer flm_a1b2c3d4.xxxxxxxxxxxx" \
https://flimmer.example.com/api/v1/me
```
Schlüssel legst du unter **Einstellungen → API-Schlüssel** an. Der Klartext ist
**einmalig** sichtbar; danach liegt in der Datenbank nur ein Hash. Ein Schlüssel
hat dieselben Rechte wie dein Konto — behandle ihn wie ein Passwort und zieh ihn
zurück, sobald du ihn nicht mehr brauchst.
Aus dem Browser heraus funktioniert stattdessen das Sitzungscookie; dieselben
Endpunkte bedienen also Skripte und das eigene Frontend.
## Antwortformat
Alle Antworten sind JSON. Zeitangaben in ISO 8601, Laufzeiten in Minuten.
Fehler tragen immer ein Feld `error`:
```json
{ "error": "Diesen Eintrag gibt es nicht." }
```
| Status | Bedeutung |
|---|---|
| 200 | In Ordnung |
| 401 | Kein oder ungültiger Schlüssel |
| 404 | Nicht vorhanden (oder nicht deins) |
| 422 | Anfrage unvollständig — `details` nennt die Felder |
| 502 / 503 | TMDB nicht erreichbar bzw. kein Schlüssel hinterlegt |
## Endpunkte
### Konto
```http
GET /api/v1/me
```
### Bibliothek
```http
GET /api/v1/bibliothek?status=WATCHING&typ=SERIES&genre=Drama&q=nord&limit=50&offset=0
POST /api/v1/bibliothek
```
Filter: `q`, `status`, `typ`, `genre`, `tag`, `dienst`, `limit`, `offset`.
Status: `WATCHING`, `PAUSED`, `COMPLETED`, `DROPPED`, `WATCHLIST`.
Aufnehmen — entweder mit interner Kennung oder direkt mit der TMDB-Kennung:
```bash
curl -X POST -H "Authorization: Bearer $KEY" -H "content-type: application/json" \
-d '{"tmdbId": 1396, "type": "SERIES", "status": "WATCHING"}' \
https://flimmer.example.com/api/v1/bibliothek
```
```http
GET /api/v1/bibliothek/{entryId}
PATCH /api/v1/bibliothek/{entryId}
DELETE /api/v1/bibliothek/{entryId}
```
`GET` liefert den Eintrag samt `progress` und `nextEpisode`.
`PATCH` ändert `status`, `rating` (1–10), `notes`, `whereToWatch`, `favorite`.
`DELETE` legt den Eintrag in den Papierkorb — dort bleibt er 30 Tage.
### Sichtungen
```http
GET /api/v1/bibliothek/{entryId}/sichtungen
POST /api/v1/bibliothek/{entryId}/sichtungen
DELETE /api/v1/bibliothek/{entryId}/sichtungen
```
`POST` kennt fünf Formen:
| Rumpf | Wirkung |
|---|---|
| `{"action":"next"}` | Die nächste offene Episode abhaken |
| `{"action":"movie"}` | Einen Film als gesehen markieren |
| `{"action":"episodes","episodeIds":["…"]}` | Eine Auswahl abhaken |
| `{"action":"season","seasonId":"…"}` | Eine ganze Staffel |
| `{"action":"upTo","episodeId":"…"}` | Alles bis einschließlich dieser Episode |
Die Antwort trägt `watchIds` — damit lässt sich genau diese Aktion zurücknehmen:
```bash
# Nächste Episode abhaken
IDS=$(curl -s -X POST -H "Authorization: Bearer $KEY" -H "content-type: application/json" \
-d '{"action":"next"}' \
https://flimmer.example.com/api/v1/bibliothek/$ENTRY/sichtungen | jq -c .watchIds)
# … und wieder zurücknehmen
curl -X DELETE -H "Authorization: Bearer $KEY" -H "content-type: application/json" \
-d "{\"watchIds\": $IDS}" \
https://flimmer.example.com/api/v1/bibliothek/$ENTRY/sichtungen
```
Alternativ nimmt `DELETE` auch `{"episodeIds":["…"]}` — dann verschwindet die
Markierung dieser Episoden im aktuellen Durchlauf.
### Suche
```http
GET /api/v1/suche?q=nordlicht&typ=serie
```
Durchsucht TMDB. Die Treffer tragen `tmdbId` und `type` — direkt verwendbar für
`POST /api/v1/bibliothek`. Ohne hinterlegten TMDB-Schlüssel antwortet der
Endpunkt mit 503.
### Kalender
```http
GET /api/v1/kalender?von=2026-09-01&bis=2026-09-30
```
Kommende Episoden der getrackten Serien. Ohne Zeitraum: die nächsten 30 Tage.
### Statistik
```http
GET /api/v1/statistik?jahr=2026
```
Liefert `totals`, `monthly`, `providers`, `genres` und `yearReview` auf einmal.
### Universen
```http
GET /api/v1/universen
POST /api/v1/universen
GET /api/v1/universen/{id}?reihenfolge={orderId}
POST /api/v1/universen/{id}
```
`GET /api/v1/universen/{id}` gibt die Einträge in der gewählten Reihenfolge
zurück, dazu `progress` und `nextUp`. Ohne `reihenfolge` gilt die
Erscheinungsreihenfolge.
### Export
```http
GET /api/v1/export?format=json
GET /api/v1/export?format=csv
```
JSON enthält zusätzlich alle Sichtungen und Universen.
## Beispiel: Serien mit offener Episode auflisten
```bash
#!/usr/bin/env bash
set -euo pipefail
KEY="flm_…"
HOST="https://flimmer.example.com"
curl -s -H "Authorization: Bearer $KEY" \
"$HOST/api/v1/bibliothek?status=WATCHING&typ=SERIES" \
| jq -r '.items[] | "\(.title): \(.watched)/\(.total)"'
```
## Grenzen
Es gibt bewusst keine Ratenbegrenzung: Flimmer läuft auf deinem eigenen Server,
und ein Limit gegen dich selbst hilft niemandem. Wer die Instanz öffentlich
erreichbar macht, setzt so etwas besser im Reverse Proxy.
Der Abgleich mit TMDB lässt sich über einen eigenen Endpunkt anstoßen — der
nimmt allerdings das Cron-Geheimnis, nicht einen API-Schlüssel:
```bash
curl -X POST -H "Authorization: Bearer $CRON_SECRET" \
https://flimmer.example.com/api/cron/sync
```