CI / pruefen (pull_request) Canceled after 0s
Ein Universum kann mehrere Reihenfolgen haben, aber welche beim Oeffnen
erschien, liess sich nicht bestimmen — es war immer die
Erscheinungsreihenfolge. Das Feld isDefault gab es schon und wurde beim
Oeffnen auch ausgewertet; es fehlte nur der Weg, es zu setzen.
Der Standard gehoert dem Universum, nicht der einzelnen Person: wer
Schreibrecht hat, legt ihn fuer alle fest — so wie die Reihenfolgen selbst
auch geteilt sind. Die Rangfolge bleibt dadurch die schlichte: die Adresse
schlaegt den Standard schlaegt die erste.
setDefaultOrder() setzt zurueck und neu in einer Transaktion, damit nie zwei
Standards nebeneinander stehen. deleteOrder() faengt jetzt den Fall ab, dass
die Standardreihenfolge geloescht wird — dann uebernimmt die
Erscheinungsreihenfolge, statt dass die Auswahl auf "die erste" faellt.
Ueber die Schnittstelle geht es per PATCH /api/v1/universen/{id}. Der
Endpunkt prueft, dass die Reihenfolge zu genau diesem Universum gehoert:
ohne das setzte ein PATCH auf A den Standard in B, weil die Domaenenschicht
das Universum aus der Reihenfolge ableitet.
Vier Tests dazu: Umstellen samt Eindeutigkeit, Vorrang der Adresse,
Rueckfall beim Loeschen, Rechtepruefung. Klickweg und Endpunkt gegen einen
Produktionsbau geprueft.
Refs #4
215 lines
6.4 KiB
Markdown
215 lines
6.4 KiB
Markdown
# 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`, `sammlung`, `limit`,
|
||
`offset`. Status: `WATCHING`, `PAUSED`, `COMPLETED`, `DROPPED`, `WATCHLIST`.
|
||
`sammlung=1` liefert nur Titel, die in der eigenen Sammlung liegen.
|
||
|
||
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`
|
||
sowie `owned` (liegt der Titel in der eigenen Sammlung?) und `ownedSource`
|
||
(wo genau — Jellyfin, Plex, Blu-ray …).
|
||
`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}
|
||
PATCH /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
|
||
Standardreihenfolge des Universums — in `orders` trägt sie `isDefault`.
|
||
|
||
`PATCH` legt fest, welche das ist. Die Reihenfolge muss zu diesem Universum
|
||
gehören, und es braucht Schreibrecht:
|
||
|
||
```bash
|
||
curl -X PATCH -H "Authorization: Bearer $KEY" -H "content-type: application/json" \
|
||
-d '{"defaultOrderId": "…"}' \
|
||
https://flimmer.example.com/api/v1/universen/$ID
|
||
```
|
||
|
||
Je Universum trägt immer genau eine Reihenfolge den Standard. Wird die
|
||
Standardreihenfolge gelöscht, übernimmt 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: was ich habe, aber noch nicht gesehen habe
|
||
|
||
```bash
|
||
curl -s -H "Authorization: Bearer $KEY" \
|
||
"$HOST/api/v1/bibliothek?sammlung=1&status=WATCHLIST" \
|
||
| jq -r '.items[] | "\(.title) — liegt bei: \(.ownedSource // "ohne Angabe")"'
|
||
```
|
||
|
||
## 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
|
||
```
|