Bugfix and Docs

This commit is contained in:
2026-03-10 13:12:57 +00:00
parent 3516ff8486
commit ac4d77dddf
75 changed files with 3511 additions and 5142 deletions

View File

@@ -1,16 +1,12 @@
# Cron API
Ripster enthält ein eingebautes Cron-System, mit dem **Skripte** und **Skript-Ketten** zeitgesteuert oder manuell ausgeführt werden können. Der Cron-Dienst benötigt keine externen Pakete der Cron-Expression-Parser ist vollständig im Backend implementiert.
Ripster enthält ein eingebautes Cron-System für Skripte und Skript-Ketten (`sourceType: script|chain`).
---
## Endpunkte
## GET /api/crons
### `GET /api/crons`
Alle konfigurierten Cron-Jobs auflisten.
**Antwort:**
Listet alle Cron-Jobs.
```json
{
@@ -21,13 +17,14 @@ Alle konfigurierten Cron-Jobs auflisten.
"cronExpression": "0 2 * * *",
"sourceType": "script",
"sourceId": 3,
"sourceName": "Backup-Skript",
"enabled": true,
"pushoverEnabled": true,
"lastRunAt": "2026-03-09T02:00:00.000Z",
"lastRunAt": "2026-03-10T02:00:00.000Z",
"lastRunStatus": "success",
"nextRunAt": "2026-03-10T02:00:00.000Z",
"nextRunAt": "2026-03-11T02:00:00.000Z",
"createdAt": "2026-03-01T10:00:00.000Z",
"updatedAt": "2026-03-09T02:00:00.000Z"
"updatedAt": "2026-03-10T02:00:05.000Z"
}
]
}
@@ -35,11 +32,9 @@ Alle konfigurierten Cron-Jobs auflisten.
---
### `POST /api/crons`
## POST /api/crons
Neuen Cron-Job anlegen.
**Body:**
Erstellt Cron-Job.
```json
{
@@ -52,16 +47,25 @@ Neuen Cron-Job anlegen.
}
```
| Feld | Typ | Pflicht | Beschreibung |
|------|-----|---------|-------------|
| `name` | string | ✓ | Anzeigename |
| `cronExpression` | string | ✓ | 5-Felder-Cron-Ausdruck (Minute Stunde Tag Monat Wochentag) |
| `sourceType` | string | ✓ | `"script"` oder `"chain"` |
| `sourceId` | number | ✓ | ID des Skripts bzw. der Kette |
| `enabled` | boolean | | Aktiviert (default: `true`) |
| `pushoverEnabled` | boolean | | PushOver-Benachrichtigung nach Ausführung (default: `true`) |
Response: `201` mit `{ "job": { ... } }`
**Antwort:** `201 Created`
---
## GET /api/crons/:id
Response:
```json
{ "job": { "id": 1, "name": "..." } }
```
---
## PUT /api/crons/:id
Aktualisiert Cron-Job. Felder wie bei `POST`.
Response:
```json
{ "job": { ... } }
@@ -69,53 +73,27 @@ Neuen Cron-Job anlegen.
---
### `GET /api/crons/:id`
## DELETE /api/crons/:id
Einzelnen Cron-Job abrufen.
**Antwort:**
Response:
```json
{ "job": { ... } }
{ "removed": { "id": 1, "name": "Nachtlauf Backup" } }
```
---
### `PUT /api/crons/:id`
## GET /api/crons/:id/logs
Cron-Job aktualisieren. Body-Felder entsprechen `POST /api/crons`.
**Antwort:**
```json
{ "job": { ... } }
```
---
### `DELETE /api/crons/:id`
Cron-Job löschen.
**Antwort:**
```json
{ "removed": { "id": 1 } }
```
---
### `GET /api/crons/:id/logs`
Ausführungs-Logs eines Cron-Jobs abrufen.
Liefert Ausführungs-Logs.
**Query-Parameter:**
| Parameter | Typ | Default | Beschreibung |
|-----------|-----|---------|-------------|
| `limit` | number | 20 | Anzahl Einträge (max. 100) |
| `limit` | number | `20` | Anzahl Einträge, max. `100` |
**Antwort:**
**Response:**
```json
{
@@ -123,62 +101,54 @@ Ausführungs-Logs eines Cron-Jobs abrufen.
{
"id": 42,
"cronJobId": 1,
"startedAt": "2026-03-09T02:00:01.000Z",
"finishedAt": "2026-03-09T02:00:05.000Z",
"startedAt": "2026-03-10T02:00:01.000Z",
"finishedAt": "2026-03-10T02:00:05.000Z",
"status": "success",
"exitCode": 0,
"stdout": "Backup abgeschlossen.",
"stderr": "",
"triggeredBy": "cron"
"output": "Backup abgeschlossen.",
"errorMessage": null
}
]
}
```
| Feld | Beschreibung |
|------|-------------|
| `status` | `"success"`, `"error"` oder `"running"` |
| `triggeredBy` | `"cron"` (zeitgesteuert) oder `"manual"` (manuell ausgelöst) |
`status`: `running` | `success` | `error`
---
### `POST /api/crons/:id/run`
## POST /api/crons/:id/run
Cron-Job sofort manuell auslösen (unabhängig vom Zeitplan).
Triggert Job manuell (asynchron).
**Antwort:**
**Response:**
```json
{
"status": "success",
"exitCode": 0,
"stdout": "...",
"stderr": ""
}
{ "triggered": true, "cronJobId": 1 }
```
Wenn Job bereits läuft: `409`.
---
### `POST /api/crons/validate-expression`
## POST /api/crons/validate-expression
Cron-Ausdruck validieren und nächsten Ausführungszeitpunkt berechnen.
Validiert 5-Felder-Cron-Ausdruck und berechnet nächsten Lauf.
**Body:**
**Request:**
```json
{ "cronExpression": "*/15 * * * *" }
```
**Antwort (gültig):**
**Gültige Response:**
```json
{
"valid": true,
"nextRunAt": "2026-03-09T14:15:00.000Z"
"nextRunAt": "2026-03-10T14:15:00.000Z"
}
```
**Antwort (ungültig):**
**Ungültige Response:**
```json
{
@@ -190,54 +160,23 @@ Cron-Ausdruck validieren und nächsten Ausführungszeitpunkt berechnen.
---
## Cron-Expression-Format
## Cron-Format
Ripster verwendet **5-Felder-Cron-Ausdrücke** (kein Sekunden-Feld):
Ripster unterstützt 5 Felder:
```
┌───────────── Minute (0-59)
│ ┌────────── Stunde (0-23)
│ │ ┌─────── Tag (1-31)
│ │ │ ┌──── Monat (1-12)
│ │ │ │ ┌─ Wochentag (0-7, 0 und 7 = Sonntag)
│ │ │ │ │
* * * * *
```text
Minute Stunde Tag Monat Wochentag
```
### Beispiele
Beispiele:
| Ausdruck | Beschreibung |
|----------|-------------|
| `0 2 * * *` | Täglich um 02:00 Uhr |
| `*/15 * * * *` | Alle 15 Minuten |
| `0 6 * * 1-5` | MontagFreitag um 06:00 Uhr |
| `30 23 * * 0` | Sonntags um 23:30 Uhr |
| `0 0 1 * *` | Erster Tag des Monats um Mitternacht |
### Unterstützte Syntax
| Syntax | Bedeutung |
|--------|----------|
| `*` | Jeder Wert |
| `*/n` | Jeder n-te Wert (Step) |
| `a-b` | Bereich von a bis b |
| `a,b,c` | Liste von Werten |
| Kombinierbar | z. B. `1,5-10,*/3` |
- `0 2 * * *` täglich 02:00
- `*/15 * * * *` alle 15 Minuten
- `0 6 * * 1-5` Mo-Fr 06:00
---
## WebSocket-Event
## WebSocket-Events zu Cron
Bei Änderungen an Cron-Jobs (Anlegen, Aktualisieren, Löschen) wird ein `CRON_JOBS_UPDATED`-Event gesendet:
```json
{
"type": "CRON_JOBS_UPDATED",
"payload": {
"action": "created",
"id": 1
}
}
```
`action` ist `"created"`, `"updated"` oder `"deleted"`.
- `CRON_JOBS_UPDATED` bei Create/Update/Delete
- `CRON_JOB_UPDATED` bei Laufzeitstatus (`running` -> `success|error`)

View File

@@ -1,23 +1,23 @@
# History API
Endpunkte für die Job-Histoire, Dateimanagement und Orphan-Import.
Endpunkte für Job-Historie, Orphan-Import und Löschoperationen.
---
## GET /api/history
Gibt eine Liste aller Jobs zurück, optional gefiltert.
Liefert Jobs (optionale Filter).
**Query-Parameter:**
| Parameter | Typ | Beschreibung |
|----------|-----|-------------|
| `status` | string | Filtert nach Status (z.B. `FINISHED`, `ERROR`) |
| `search` | string | Sucht in Filmtiteln |
| `status` | string | Filter nach Job-Status |
| `search` | string | Suche in Titel-Feldern |
**Beispiel:**
```
```text
GET /api/history?status=FINISHED&search=Inception
```
@@ -30,17 +30,15 @@ GET /api/history?status=FINISHED&search=Inception
"id": 42,
"status": "FINISHED",
"title": "Inception",
"imdb_id": "tt1375666",
"omdb_year": "2010",
"omdb_type": "movie",
"omdb_poster": "https://...",
"raw_path": "/mnt/nas/raw/Inception_t00.mkv",
"output_path": "/mnt/nas/movies/Inception (2010).mkv",
"created_at": "2024-01-15T10:00:00.000Z",
"updated_at": "2024-01-15T12:30:00.000Z"
"raw_path": "/mnt/raw/Inception - RAW - job-42",
"output_path": "/mnt/movies/Inception (2010)/Inception (2010).mkv",
"mediaType": "bluray",
"ripSuccessful": true,
"encodeSuccess": true,
"created_at": "2026-03-10T08:00:00.000Z",
"updated_at": "2026-03-10T10:00:00.000Z"
}
],
"total": 1
]
}
```
@@ -48,34 +46,37 @@ GET /api/history?status=FINISHED&search=Inception
## GET /api/history/:id
Gibt Detail-Informationen für einen einzelnen Job zurück.
**URL-Parameter:** `id` Job-ID
Liefert Job-Detail.
**Query-Parameter:**
| Parameter | Typ | Standard | Beschreibung |
|----------|-----|---------|-------------|
| `includeLogs` | boolean | `false` | Log-Inhalte einschließen |
| `includeLiveLog` | boolean | `false` | Aktuellen Live-Log einschließen |
| `includeLogs` | bool | `false` | Prozesslog laden |
| `includeLiveLog` | bool | `false` | alias-artig ebenfalls Prozesslog laden |
| `includeAllLogs` | bool | `false` | vollständiges Log statt Tail |
| `logTailLines` | number | `800` | Tail-Länge falls nicht `includeAllLogs` |
**Response:**
```json
{
"id": 42,
"status": "FINISHED",
"title": "Inception",
"imdb_id": "tt1375666",
"encode_plan": { ... },
"makemkv_output": { ... },
"mediainfo_output": { ... },
"handbrake_log": "/path/to/log",
"logs": {
"handbrake": "Encoding: task 1 of 1, 100.0%\n..."
},
"created_at": "2024-01-15T10:00:00.000Z",
"updated_at": "2024-01-15T12:30:00.000Z"
"job": {
"id": 42,
"status": "FINISHED",
"makemkvInfo": {},
"mediainfoInfo": {},
"handbrakeInfo": {},
"encodePlan": {},
"log": "...",
"log_count": 1,
"logMeta": {
"loaded": true,
"total": 800,
"returned": 800,
"truncated": true
}
}
}
```
@@ -83,14 +84,19 @@ Gibt Detail-Informationen für einen einzelnen Job zurück.
## GET /api/history/database
Gibt alle rohen Datenbankzeilen zurück (Debug-Ansicht).
Debug-Ansicht der DB-Zeilen (angereichert).
**Response:**
```json
{
"jobs": [ { "id": 1, "status": "FINISHED", ... } ],
"total": 15
"rows": [
{
"id": 42,
"status": "FINISHED",
"rawFolderName": "Inception - RAW - job-42"
}
]
}
```
@@ -98,18 +104,25 @@ Gibt alle rohen Datenbankzeilen zurück (Debug-Ansicht).
## GET /api/history/orphan-raw
Findet Raw-Ordner, die nicht als Jobs in der Datenbank registriert sind.
Sucht RAW-Ordner ohne zugehörigen Job.
**Response:**
```json
{
"orphans": [
"rawDir": "/mnt/raw",
"rawDirs": ["/mnt/raw", "/mnt/raw-bluray"],
"rows": [
{
"path": "/mnt/nas/raw/UnknownMovie_2023-12-01",
"size": "45.2 GB",
"modifiedAt": "2023-12-01T15:00:00.000Z",
"files": ["t00.mkv", "t01.mkv"]
"rawPath": "/mnt/raw/Inception (2010) [tt1375666] - RAW - job-99",
"folderName": "Inception (2010) [tt1375666] - RAW - job-99",
"title": "Inception",
"year": 2010,
"imdbId": "tt1375666",
"folderJobId": 99,
"entryCount": 4,
"hasBlurayStructure": true,
"lastModifiedAt": "2026-03-10T09:00:00.000Z"
}
]
}
@@ -119,35 +132,28 @@ Findet Raw-Ordner, die nicht als Jobs in der Datenbank registriert sind.
## POST /api/history/orphan-raw/import
Importiert einen Orphan-Raw-Ordner als Job in die Datenbank.
Importiert RAW-Ordner als FINISHED-Job.
**Request:**
```json
{
"path": "/mnt/nas/raw/UnknownMovie_2023-12-01"
}
{ "rawPath": "/mnt/raw/Inception (2010) [tt1375666] - RAW - job-99" }
```
**Response:**
```json
{
"ok": true,
"jobId": 99,
"message": "Orphan-Ordner als Job importiert"
"job": { "id": 77, "status": "FINISHED" },
"uiReset": { "reset": true, "state": "IDLE" }
}
```
Nach dem Import kann dem Job über `/api/history/:id/omdb/assign` Metadaten zugewiesen werden.
---
## POST /api/history/:id/omdb/assign
Weist einem bestehenden Job OMDb-Metadaten nachträglich zu.
**URL-Parameter:** `id` Job-ID
Weist OMDb-/Metadaten nachträglich zu.
**Request:**
@@ -155,44 +161,42 @@ Weist einem bestehenden Job OMDb-Metadaten nachträglich zu.
{
"imdbId": "tt1375666",
"title": "Inception",
"year": "2010",
"type": "movie",
"poster": "https://..."
"year": 2010,
"poster": "https://...",
"fromOmdb": true
}
```
**Response:**
```json
{ "ok": true }
{ "job": { "id": 42, "imdb_id": "tt1375666" } }
```
---
## POST /api/history/:id/delete-files
Löscht die Dateien eines Jobs (Raw und/oder Output), behält den Job-Eintrag.
**URL-Parameter:** `id` Job-ID
Löscht Dateien eines Jobs, behält DB-Eintrag.
**Request:**
```json
{
"deleteRaw": true,
"deleteOutput": false
}
{ "target": "both" }
```
`target`: `raw` | `movie` | `both`
**Response:**
```json
{
"ok": true,
"deleted": {
"raw": "/mnt/nas/raw/Inception_t00.mkv",
"output": null
}
"summary": {
"target": "both",
"raw": { "attempted": true, "deleted": true, "filesDeleted": 12, "dirsRemoved": 3, "reason": null },
"movie": { "attempted": true, "deleted": false, "filesDeleted": 0, "dirsRemoved": 0, "reason": "Movie-Datei/Pfad existiert nicht." }
},
"job": { "id": 42 }
}
```
@@ -200,23 +204,38 @@ Löscht die Dateien eines Jobs (Raw und/oder Output), behält den Job-Eintrag.
## POST /api/history/:id/delete
Löscht den Job-Eintrag aus der Datenbank, optional auch die Dateien.
**URL-Parameter:** `id` Job-ID
Löscht Job aus DB; optional auch Dateien.
**Request:**
```json
{
"deleteFiles": true
}
{ "target": "none" }
```
`target`: `none` | `raw` | `movie` | `both`
**Response:**
```json
{ "ok": true, "message": "Job gelöscht" }
{
"deleted": true,
"jobId": 42,
"fileTarget": "both",
"fileSummary": {
"target": "both",
"raw": { "filesDeleted": 10 },
"movie": { "filesDeleted": 1 }
},
"uiReset": {
"reset": true,
"state": "IDLE"
}
}
```
!!! warning "Unwiderruflich"
Das Löschen von Jobs und Dateien ist nicht rückgängig zu machen.
---
## Hinweise
- Ein aktiver Pipeline-Job kann nicht gelöscht werden (`409`).
- Alle Löschoperationen sind irreversibel.

View File

@@ -1,16 +1,21 @@
# API-Referenz
Ripster bietet eine **REST-API** für alle Operationen sowie einen **WebSocket-Endpunkt** für Echtzeit-Updates.
Ripster bietet eine REST-API für Steuerung/Verwaltung sowie einen WebSocket-Endpunkt für Echtzeit-Updates.
---
## Basis-URL
```
```text
http://localhost:3001
```
Konfigurierbar über die Umgebungsvariable `PORT`.
API-Prefix: `/api`
Beispiele:
- `GET /api/health`
- `GET /api/pipeline/state`
---
@@ -18,11 +23,19 @@ Konfigurierbar über die Umgebungsvariable `PORT`.
<div class="grid cards" markdown>
- :material-heart-pulse: **Health**
---
Service-Liveness.
`GET /api/health`
- :material-pipe: **Pipeline API**
---
Pipeline-Steuerung: Analyse starten, Metadaten setzen, Ripping und Encoding steuern.
Analyse, Start/Retry/Cancel, Queue, Re-Encode.
[:octicons-arrow-right-24: Pipeline API](pipeline.md)
@@ -30,7 +43,7 @@ Konfigurierbar über die Umgebungsvariable `PORT`.
---
Einstellungen lesen und schreiben.
Einstellungen, Skripte/Ketten, User-Presets.
[:octicons-arrow-right-24: Settings API](settings.md)
@@ -38,7 +51,7 @@ Konfigurierbar über die Umgebungsvariable `PORT`.
---
Job-Geschichte abfragen, Jobs löschen, Orphan-Ordner importieren.
Job-Historie, Orphan-Import, Löschoperationen.
[:octicons-arrow-right-24: History API](history.md)
@@ -46,7 +59,7 @@ Konfigurierbar über die Umgebungsvariable `PORT`.
---
Cron-Jobs verwalten, manuell auslösen und Ausführungs-Logs abrufen.
Zeitgesteuerte Skript-/Kettenausführung.
[:octicons-arrow-right-24: Cron API](crons.md)
@@ -54,7 +67,7 @@ Konfigurierbar über die Umgebungsvariable `PORT`.
---
Echtzeit-Events für Pipeline-Status, Fortschritt und Disc-Erkennung.
Pipeline-, Queue-, Disk-, Settings-, Cron- und Monitoring-Events.
[:octicons-arrow-right-24: WebSocket](websocket.md)
@@ -64,30 +77,41 @@ Konfigurierbar über die Umgebungsvariable `PORT`.
## Authentifizierung
Die API hat **keine Authentifizierung**. Sie ist für den Einsatz im lokalen Netzwerk konzipiert.
!!! warning "Produktionsbetrieb"
Falls Ripster öffentlich erreichbar sein soll, schütze die API mit einem Reverse-Proxy (z. B. nginx mit Basic Auth oder OAuth).
Es gibt keine eingebaute Authentifizierung. Ripster ist für lokalen Betrieb gedacht.
---
## Fehlerformat
Alle API-Fehler werden im folgenden Format zurückgegeben:
Fehler werden zentral als JSON geliefert:
```json
{
"error": "Job nicht gefunden",
"details": "Kein Job mit ID 999 vorhanden"
"error": {
"message": "Job nicht gefunden.",
"statusCode": 404,
"reqId": "req_...",
"details": [
{
"field": "name",
"message": "Name darf nicht leer sein."
}
]
}
}
```
HTTP-Statuscodes:
`details` ist optional (z. B. bei Validierungsfehlern).
---
## Häufige Statuscodes
| Code | Bedeutung |
|-----|-----------|
|------|-----------|
| `200` | Erfolg |
| `400` | Ungültige Anfrage |
| `201` | Ressource erstellt |
| `400` | Ungültige Anfrage / Validierungsfehler |
| `404` | Ressource nicht gefunden |
| `409` | Konflikt (z.B. Pipeline bereits aktiv) |
| `500` | Interner Serverfehler |
| `409` | Konflikt (z. B. falscher Pipeline-Zustand, Job läuft bereits) |
| `500` | Interner Fehler |

View File

@@ -1,14 +1,14 @@
# Pipeline API
Alle Endpunkte zur Steuerung des Ripster-Workflows.
Endpunkte zur Steuerung des Pipeline-Workflows.
---
## GET /api/pipeline/state
Liefert den aktuellen Pipeline-Snapshot.
Liefert aktuellen Pipeline- und Hardware-Monitoring-Snapshot.
**Response:**
**Response (Beispiel):**
```json
{
@@ -17,45 +17,46 @@ Liefert den aktuellen Pipeline-Snapshot.
"activeJobId": 42,
"progress": 0,
"eta": null,
"statusText": "Mediainfo geladen - bitte bestätigen",
"statusText": "Mediainfo bestätigt - Encode manuell starten",
"context": {
"jobId": 42
},
"jobProgress": {
"42": {
"state": "MEDIAINFO_CHECK",
"progress": 68.5,
"eta": null,
"statusText": "MEDIAINFO_CHECK 68.50%"
}
},
"queue": {
"maxParallelJobs": 1,
"runningCount": 0,
"queuedCount": 0,
"runningCount": 1,
"queuedCount": 2,
"runningJobs": [],
"queuedJobs": []
}
},
"hardwareMonitoring": {
"enabled": true,
"intervalMs": 5000,
"updatedAt": "2026-03-10T09:00:00.000Z",
"sample": {
"cpu": {},
"memory": {},
"gpu": {},
"storage": {}
},
"error": null
}
}
```
**Pipeline-Zustände:**
| Wert | Beschreibung |
|------|-------------|
| `IDLE` | Wartet auf Medium |
| `DISC_DETECTED` | Medium erkannt, wartet auf Analyse-Start |
| `METADATA_SELECTION` | Metadaten-Dialog aktiv |
| `WAITING_FOR_USER_DECISION` | Manuelle Playlist-Auswahl erforderlich |
| `READY_TO_START` | Übergang/Fallback vor Start |
| `RIPPING` | MakeMKV läuft |
| `MEDIAINFO_CHECK` | HandBrake-Scan + Plan-Erstellung |
| `READY_TO_ENCODE` | Review bereit |
| `ENCODING` | HandBrake-Encoding läuft (inkl. Post-Skripte) |
| `FINISHED` | Abgeschlossen |
| `CANCELLED` | Vom Benutzer abgebrochen |
| `ERROR` | Fehler |
---
## POST /api/pipeline/analyze
Startet die Analyse für die aktuell erkannte Disc.
**Request:** kein Body
Startet Disc-Analyse und legt Job an.
**Response:**
@@ -73,14 +74,21 @@ Startet die Analyse für die aktuell erkannte Disc.
## POST /api/pipeline/rescan-disc
Erzwingt eine erneute Laufwerksprüfung.
Erzwingt erneute Laufwerksprüfung.
**Response (Beispiel):**
```json
{
"result": {
"emitted": "discInserted"
"present": true,
"changed": true,
"emitted": "discInserted",
"device": {
"path": "/dev/sr0",
"discLabel": "INCEPTION",
"mediaProfile": "bluray"
}
}
}
```
@@ -89,7 +97,7 @@ Erzwingt eine erneute Laufwerksprüfung.
## GET /api/pipeline/omdb/search?q=<query>
Sucht OMDb-Titel.
OMDb-Titelsuche.
**Response:**
@@ -111,7 +119,7 @@ Sucht OMDb-Titel.
## POST /api/pipeline/select-metadata
Setzt Metadaten (und optional Playlist-Entscheidung).
Setzt Metadaten (und optional Playlist) für einen Job.
**Request:**
@@ -127,38 +135,35 @@ Setzt Metadaten (und optional Playlist-Entscheidung).
}
```
**Response:** `{ "job": { ... } }`
**Response:**
!!! note "Startlogik"
Nach Metadaten-Bestätigung wird der nächste Schritt automatisch ausgelöst (`startPreparedJob`).
Der Job startet direkt oder wird in die Queue eingereiht.
```json
{ "job": { "id": 42, "status": "READY_TO_START" } }
```
---
## POST /api/pipeline/start/:jobId
Startet einen vorbereiteten Job manuell (z. B. Fallback/Queue-Szenario).
Startet vorbereiteten Job oder queued ihn (je nach Parallel-Limit).
**Response (Beispiel):**
**Mögliche Responses:**
```json
{
"result": {
"started": true,
"stage": "RIPPING"
}
}
{ "result": { "started": true, "stage": "RIPPING" } }
```
Mögliche `stage`-Werte sind u. a. `RIPPING`, `MEDIAINFO_CHECK`, `ENCODING`.
```json
{ "result": { "queued": true, "started": false, "queuePosition": 2, "action": "START_PREPARED" } }
```
---
## POST /api/pipeline/confirm-encode/:jobId
Bestätigt Review-Auswahl (Titel/Tracks/Post-Skripte).
Bestätigt Review-Auswahl (Tracks, Pre/Post-Skripte/Ketten, User-Preset).
**Request:**
**Request (typisch):**
```json
{
@@ -169,78 +174,70 @@ Bestätigt Review-Auswahl (Titel/Tracks/Post-Skripte).
"subtitleTrackIds": [3]
}
},
"selectedPreEncodeScriptIds": [1],
"selectedPostEncodeScriptIds": [2, 7],
"selectedPreEncodeChainIds": [3],
"selectedPostEncodeChainIds": [4],
"selectedUserPresetId": 5,
"skipPipelineStateUpdate": false
}
```
**Response:** `{ "job": { ... } }`
**Response:**
```json
{ "job": { "id": 42, "encode_review_confirmed": 1 } }
```
---
## POST /api/pipeline/cancel
Bricht laufenden Job ab oder entfernt einen Queue-Eintrag.
Bricht laufenden Job ab oder entfernt Queue-Eintrag.
**Request (optional):**
```json
{
"jobId": 42
}
{ "jobId": 42 }
```
**Response (Beispiel):**
**Mögliche Responses:**
```json
{
"result": {
"cancelled": true,
"queuedOnly": false,
"jobId": 42
}
}
{ "result": { "cancelled": true, "queuedOnly": true, "jobId": 42 } }
```
```json
{ "result": { "cancelled": true, "queuedOnly": false, "jobId": 42 } }
```
```json
{ "result": { "cancelled": true, "queuedOnly": false, "pending": true, "jobId": 42 } }
```
---
## POST /api/pipeline/retry/:jobId
Startet einen Job aus `ERROR`/`CANCELLED` erneut (oder reiht ihn in die Queue ein).
**Response:** `{ "result": { ... } }`
---
## POST /api/pipeline/resume-ready/:jobId
Lädt einen `READY_TO_ENCODE`-Job nach Neustart wieder in die aktive Session.
**Response:** `{ "job": { ... } }`
---
Retry für `ERROR`/`CANCELLED`-Jobs (oder Queue-Einreihung).
## POST /api/pipeline/reencode/:jobId
Startet Re-Encode aus bestehendem RAW.
**Response:** `{ "result": { ... } }`
---
## POST /api/pipeline/restart-review/:jobId
Berechnet die Review aus vorhandenem RAW neu.
**Response:** `{ "result": { ... } }`
---
Berechnet Review aus RAW neu.
## POST /api/pipeline/restart-encode/:jobId
Startet Encoding mit der zuletzt bestätigten Auswahl neu.
Startet Encoding mit letzter bestätigter Review neu.
**Response:** `{ "result": { ... } }`
## POST /api/pipeline/resume-ready/:jobId
Lädt `READY_TO_ENCODE`-Job nach Neustart wieder in aktive Session.
Alle Endpunkte liefern `{ result: ... }` bzw. `{ job: ... }`.
---
@@ -248,9 +245,51 @@ Startet Encoding mit der zuletzt bestätigten Auswahl neu.
### GET /api/pipeline/queue
Liefert den aktuellen Queue-Status.
Liefert Queue-Snapshot.
**Response:** `{ "queue": { ... } }`
```json
{
"queue": {
"maxParallelJobs": 1,
"runningCount": 1,
"queuedCount": 3,
"runningJobs": [
{
"jobId": 41,
"title": "Inception",
"status": "ENCODING",
"lastState": "ENCODING"
}
],
"queuedJobs": [
{
"entryId": 11,
"position": 1,
"type": "job",
"jobId": 42,
"action": "START_PREPARED",
"actionLabel": "Start",
"title": "Matrix",
"status": "READY_TO_ENCODE",
"lastState": "READY_TO_ENCODE",
"hasScripts": true,
"hasChains": false,
"enqueuedAt": "2026-03-10T09:00:00.000Z"
},
{
"entryId": 12,
"position": 2,
"type": "wait",
"waitSeconds": 30,
"title": "Warten 30s",
"status": "QUEUED",
"enqueuedAt": "2026-03-10T09:01:00.000Z"
}
],
"updatedAt": "2026-03-10T09:01:02.000Z"
}
}
```
### POST /api/pipeline/queue/reorder
@@ -260,8 +299,71 @@ Sortiert Queue-Einträge neu.
```json
{
"orderedJobIds": [42, 43, 41]
"orderedEntryIds": [12, 11]
}
```
**Response:** `{ "queue": { ... } }`
Legacy fallback wird akzeptiert:
```json
{
"orderedJobIds": [42, 43]
}
```
### POST /api/pipeline/queue/entry
Fügt Nicht-Job-Queue-Eintrag hinzu (`script`, `chain`, `wait`).
**Request-Beispiele:**
```json
{ "type": "script", "scriptId": 3 }
```
```json
{ "type": "chain", "chainId": 2, "insertAfterEntryId": 11 }
```
```json
{ "type": "wait", "waitSeconds": 45 }
```
**Response:**
```json
{
"result": { "entryId": 12, "type": "wait", "position": 2 },
"queue": { "...": "..." }
}
```
### DELETE /api/pipeline/queue/entry/:entryId
Entfernt Queue-Eintrag.
**Response:**
```json
{ "queue": { "...": "..." } }
```
---
## Pipeline-Zustände
| State | Bedeutung |
|------|-----------|
| `IDLE` | Wartet auf Medium |
| `DISC_DETECTED` | Medium erkannt |
| `ANALYZING` | MakeMKV-Analyse läuft |
| `METADATA_SELECTION` | Metadaten-Auswahl |
| `WAITING_FOR_USER_DECISION` | Playlist-Entscheidung erforderlich |
| `READY_TO_START` | Übergang vor Start |
| `RIPPING` | MakeMKV-Rip läuft |
| `MEDIAINFO_CHECK` | Titel-/Track-Auswertung |
| `READY_TO_ENCODE` | Review bereit |
| `ENCODING` | HandBrake-Encoding läuft |
| `FINISHED` | Abgeschlossen |
| `CANCELLED` | Abgebrochen |
| `ERROR` | Fehler |

View File

@@ -1,38 +1,36 @@
# Settings API
Endpunkte zum Lesen und Schreiben der Anwendungseinstellungen.
Endpunkte für Einstellungen, Skripte, Skript-Ketten und User-Presets.
---
## GET /api/settings
Gibt alle Einstellungen kategorisiert zurück.
Liefert alle Einstellungen kategorisiert.
**Response:**
**Response (Struktur):**
```json
{
"paths": {
"raw_dir": {
"value": "/mnt/nas/raw",
"schema": {
"type": "string",
"label": "Raw-Verzeichnis",
"description": "Speicherort für rohe MKV-Dateien",
"required": true
}
},
"movie_dir": {
"value": "/mnt/nas/movies",
"schema": { ... }
"categories": [
{
"category": "Pfade",
"settings": [
{
"key": "raw_dir",
"label": "Raw Ausgabeordner",
"type": "path",
"required": true,
"description": "...",
"defaultValue": "data/output/raw",
"options": [],
"validation": { "minLength": 1 },
"value": "data/output/raw",
"orderIndex": 100
}
]
}
},
"tools": { ... },
"encoding": { ... },
"drive": { ... },
"makemkv": { ... },
"omdb": { ... },
"notifications": { ... }
]
}
```
@@ -42,42 +40,44 @@ Gibt alle Einstellungen kategorisiert zurück.
Aktualisiert eine einzelne Einstellung.
**URL-Parameter:** `key` Einstellungs-Schlüssel
**Request:**
```json
{
"value": "/mnt/storage/raw"
}
{ "value": "/mnt/storage/raw" }
```
**Response:**
```json
{ "ok": true, "key": "raw_dir", "value": "/mnt/storage/raw" }
{
"setting": {
"key": "raw_dir",
"value": "/mnt/storage/raw"
},
"reviewRefresh": {
"triggered": false,
"reason": "not_ready"
}
}
```
**Fehlerfälle:**
- `400` Ungültiger Wert (Validierungsfehler)
- `404` Einstellung nicht gefunden
!!! note "Encode-Review-Refresh"
Wenn eine encoding-relevante Einstellung geändert wird (z.B. `handbrake_preset`), wird der Encode-Plan für den aktuell wartenden Job automatisch neu berechnet.
`reviewRefresh` ist `null` oder ein Objekt mit Status der optionalen Review-Neuberechnung.
---
## PUT /api/settings
Aktualisiert mehrere Einstellungen auf einmal.
Aktualisiert mehrere Einstellungen atomar.
**Request:**
```json
{
"raw_dir": "/mnt/storage/raw",
"movie_dir": "/mnt/storage/movies",
"handbrake_preset": "H.265 MKV 720p30"
"settings": {
"raw_dir": "/mnt/storage/raw",
"movie_dir": "/mnt/storage/movies",
"handbrake_preset_bluray": "H.264 MKV 1080p30"
}
}
```
@@ -85,9 +85,36 @@ Aktualisiert mehrere Einstellungen auf einmal.
```json
{
"ok": true,
"updated": ["raw_dir", "movie_dir", "handbrake_preset"],
"errors": []
"changes": [
{ "key": "raw_dir", "value": "/mnt/storage/raw" },
{ "key": "movie_dir", "value": "/mnt/storage/movies" }
],
"reviewRefresh": {
"triggered": true,
"jobId": 42,
"relevantKeys": ["handbrake_preset_bluray"]
}
}
```
Bei Validierungsfehlern kommt `400` mit `error.details[]`.
---
## GET /api/settings/handbrake-presets
Liest Preset-Liste via `HandBrakeCLI -z` (mit Fallback auf konfigurierte Presets).
**Response (Beispiel):**
```json
{
"source": "handbrake-cli",
"message": null,
"options": [
{ "label": "General/", "value": "__group__general", "disabled": true, "category": "General" },
{ "label": " Fast 1080p30", "value": "Fast 1080p30", "category": "General" }
]
}
```
@@ -95,260 +122,217 @@ Aktualisiert mehrere Einstellungen auf einmal.
## POST /api/settings/pushover/test
Sendet eine Test-Benachrichtigung über PushOver.
Sendet Testnachricht über aktuelle PushOver-Settings.
**Request:** Kein Body erforderlich (verwendet gespeicherte Zugangsdaten)
**Response (Erfolg):**
```json
{ "ok": true, "message": "Test-Benachrichtigung gesendet" }
```
**Response (Fehler):**
```json
{ "ok": false, "error": "Ungültiger API-Token" }
```
---
## Skript-Verwaltung
Skripte werden über eigene Endpunkte unter `/api/settings/scripts` verwaltet. Jedes Skript hat eine `scriptBody`-Property (der Shell-Befehl oder mehrzeiliges Skript) und einen `orderIndex` für die Sortierung.
### GET /api/settings/scripts
Gibt alle Skripte zurück, sortiert nach `orderIndex`.
**Response:**
**Request (optional):**
```json
{
"scripts": [
{
"id": 1,
"name": "Zu Plex verschieben",
"scriptBody": "mv \"$RIPSTER_OUTPUT_PATH\" /mnt/plex/movies/",
"orderIndex": 1,
"createdAt": "2026-01-15T10:00:00.000Z",
"updatedAt": "2026-01-15T10:00:00.000Z"
}
]
"title": "Test",
"message": "Ripster Test"
}
```
---
### POST /api/settings/scripts
Legt ein neues Skript an.
**Request:**
```json
{
"name": "Zu Plex verschieben",
"scriptBody": "mv \"$RIPSTER_OUTPUT_PATH\" /mnt/plex/movies/"
}
```
| Feld | Typ | Pflicht | Beschreibung |
|------|-----|---------|-------------|
| `name` | string | ✅ | Anzeigename (eindeutig) |
| `scriptBody` | string | ✅ | Shell-Befehl oder mehrzeiliges Skript |
**Response:** `201 Created` `{ "script": { ... } }`
---
### PUT /api/settings/scripts/:id
Aktualisiert ein vorhandenes Skript. Alle Felder optional.
---
### DELETE /api/settings/scripts/:id
Löscht ein Skript.
!!! warning "Referenzen"
Das Skript wird gelöscht, auch wenn es in Job-Historien referenziert ist. In zukünftigen Reviews erscheint es nicht mehr.
---
### POST /api/settings/scripts/:id/test
Führt ein Skript mit Platzhalter-Umgebungsvariablen aus (Testlauf).
**Response:**
```json
{
"ok": true,
"exitCode": 0,
"stdout": "Testausgabe des Skripts",
"stderr": "",
"durationMs": 245
}
```
**Platzhalter-Werte beim Testlauf:**
| Variable | Testwert |
|---------|---------|
| `RIPSTER_OUTPUT_PATH` | `/tmp/ripster-test-output.mkv` |
| `RIPSTER_JOB_ID` | `0` |
| `RIPSTER_TITLE` | `Test Film` |
| `RIPSTER_YEAR` | `2024` |
| `RIPSTER_IMDB_ID` | `tt0000000` |
| `RIPSTER_RAW_PATH` | `/tmp/ripster-test-raw.mkv` |
---
### POST /api/settings/scripts/reorder
Ändert die Reihenfolge der Skripte (persistiert in `order_index`).
**Request:**
```json
{ "orderedScriptIds": [3, 1, 2] }
```
**Response:** `{ "scripts": [ ... ] }` alle Skripte in neuer Reihenfolge.
---
## Skript-Ketten-Verwaltung
Skript-Ketten werden unter `/api/settings/script-chains` verwaltet.
### GET /api/settings/script-chains
Gibt alle Ketten zurück (inkl. Schritte).
### POST /api/settings/script-chains
Legt eine neue Kette an.
```json
{ "name": "Nach Jellyfin deployen" }
```
### PUT /api/settings/script-chains/:id
Aktualisiert eine Kette (Name, Schritte).
### DELETE /api/settings/script-chains/:id
Löscht eine Kette und alle ihre Schritte.
### POST /api/settings/script-chains/:id/test
Führt eine Kette mit Platzhalter-Umgebungsvariablen aus (Testlauf).
**Response:**
```json
{
"result": {
"success": true,
"steps": [
{ "scriptId": 1, "scriptName": "Zu Plex verschieben", "success": true, "exitCode": 0 }
]
"sent": true,
"eventKey": "test",
"requestId": "..."
}
}
```
Wenn PushOver deaktiviert ist oder Credentials fehlen, kommt i. d. R. ebenfalls `200` mit `sent: false` + `reason`.
---
## Skripte
Basis: `/api/settings/scripts`
### GET /api/settings/scripts
```json
{ "scripts": [ { "id": 1, "name": "...", "scriptBody": "...", "orderIndex": 1, "createdAt": "...", "updatedAt": "..." } ] }
```
### POST /api/settings/scripts
```json
{ "name": "Move", "scriptBody": "mv \"$RIPSTER_OUTPUT_PATH\" /mnt/movies/" }
```
Response: `201` mit `{ "script": { ... } }`
### PUT /api/settings/scripts/:id
Body wie `POST`, Response `{ "script": { ... } }`.
### DELETE /api/settings/scripts/:id
Response `{ "removed": { ... } }`.
### POST /api/settings/scripts/reorder
```json
{ "orderedScriptIds": [3, 1, 2] }
```
Response `{ "scripts": [ ... ] }`.
### POST /api/settings/scripts/:id/test
Führt Skript als Testlauf aus.
```json
{
"result": {
"scriptId": 1,
"scriptName": "Move",
"success": true,
"exitCode": 0,
"signal": null,
"timedOut": false,
"durationMs": 120,
"stdout": "...",
"stderr": "...",
"stdoutTruncated": false,
"stderrTruncated": false
}
}
```
### Umgebungsvariablen für Skripte
Diese Variablen werden beim Ausführen gesetzt:
- `RIPSTER_SCRIPT_RUN_AT`
- `RIPSTER_JOB_ID`
- `RIPSTER_JOB_TITLE`
- `RIPSTER_MODE`
- `RIPSTER_INPUT_PATH`
- `RIPSTER_OUTPUT_PATH`
- `RIPSTER_RAW_PATH`
- `RIPSTER_SCRIPT_ID`
- `RIPSTER_SCRIPT_NAME`
- `RIPSTER_SCRIPT_SOURCE`
---
## Skript-Ketten
Basis: `/api/settings/script-chains`
Eine Kette hat Schritte vom Typ:
- `script` (`scriptId` erforderlich)
- `wait` (`waitSeconds` 1..3600)
### GET /api/settings/script-chains
Response `{ "chains": [ ... ] }` (inkl. `steps[]`).
### GET /api/settings/script-chains/:id
Response `{ "chain": { ... } }`.
### POST /api/settings/script-chains
```json
{
"name": "After Encode",
"steps": [
{ "stepType": "script", "scriptId": 1 },
{ "stepType": "wait", "waitSeconds": 15 },
{ "stepType": "script", "scriptId": 2 }
]
}
```
Response: `201` mit `{ "chain": { ... } }`
### PUT /api/settings/script-chains/:id
Body wie `POST`, Response `{ "chain": { ... } }`.
### DELETE /api/settings/script-chains/:id
Response `{ "removed": { ... } }`.
### POST /api/settings/script-chains/reorder
Ändert die Reihenfolge der Ketten (persistiert in `order_index`).
**Request:**
```json
{ "orderedChainIds": [2, 1, 3] }
```
Response `{ "chains": [ ... ] }`.
### POST /api/settings/script-chains/:id/test
Response:
```json
{
"result": {
"chainId": 2,
"chainName": "After Encode",
"steps": 3,
"succeeded": 3,
"failed": 0,
"aborted": false,
"results": []
}
}
```
---
## User-Presets
Benannte HandBrake-Preset-Sammlungen, die im Encode-Review schnell angewendet werden können. Unter `/api/settings/user-presets` verwaltet.
Basis: `/api/settings/user-presets`
### GET /api/settings/user-presets
Gibt alle User-Presets zurück. Optional gefiltert per Query-Parameter `mediaType`.
**Query-Parameter:**
| Parameter | Werte | Beschreibung |
|-----------|-------|-------------|
| `mediaType` | `bluray`, `dvd`, `other`, `all` | Filtert Presets nach Medientyp |
**Response:**
Optionaler Query-Parameter: `media_type=bluray|dvd|other|all`
```json
{
"presets": [
{
"id": 1,
"name": "Blu-ray High Quality",
"name": "Blu-ray HQ",
"mediaType": "bluray",
"handbrakePreset": "H.265 MKV 1080p30",
"handbrakePreset": "H.264 MKV 1080p30",
"extraArgs": "--encoder-preset slow",
"description": "Langsam, aber beste Qualität",
"createdAt": "2026-01-15T10:00:00.000Z",
"updatedAt": "2026-01-15T10:00:00.000Z"
"description": "...",
"createdAt": "...",
"updatedAt": "..."
}
]
}
```
---
### POST /api/settings/user-presets
Legt ein neues User-Preset an.
**Request:**
```json
{
"name": "Blu-ray High Quality",
"name": "Blu-ray HQ",
"mediaType": "bluray",
"handbrakePreset": "H.265 MKV 1080p30",
"handbrakePreset": "H.264 MKV 1080p30",
"extraArgs": "--encoder-preset slow",
"description": "Langsam, aber beste Qualität"
"description": "optional"
}
```
| Feld | Typ | Pflicht | Beschreibung |
|------|-----|---------|-------------|
| `name` | string | ✅ | Anzeigename |
| `mediaType` | string | — | `bluray`, `dvd`, `other`, `all` (Standard: `all`) |
| `handbrakePreset` | string | — | HandBrake-Preset-Name (`-Z`) |
| `extraArgs` | string | — | Zusatz-CLI-Argumente |
| `description` | string | — | Optionale Beschreibung |
**Response:** `201 Created` `{ "preset": { ... } }`
---
Response: `201` mit `{ "preset": { ... } }`
### PUT /api/settings/user-presets/:id
Aktualisiert ein User-Preset. Alle Felder optional.
---
Body mit beliebigen Feldern aus `POST`, Response `{ "preset": { ... } }`.
### DELETE /api/settings/user-presets/:id
Löscht ein User-Preset.
---
## Einstellungs-Schlüssel Referenz
Eine vollständige Übersicht aller Schlüssel:
[:octicons-arrow-right-24: Einstellungsreferenz](../configuration/settings-reference.md)
Response `{ "removed": { ... } }`.

View File

@@ -1,6 +1,6 @@
# WebSocket Events
Ripster sendet Echtzeit-Updates über WebSocket unter `/ws`.
Ripster sendet Echtzeit-Updates über `/ws`.
---
@@ -10,8 +10,8 @@ Ripster sendet Echtzeit-Updates über WebSocket unter `/ws`.
const ws = new WebSocket('ws://localhost:3001/ws');
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
console.log(message.type, message.payload);
const msg = JSON.parse(event.data);
console.log(msg.type, msg.payload);
};
```
@@ -19,36 +19,38 @@ ws.onmessage = (event) => {
## Nachrichtenformat
Alle Broadcasts haben dieses Schema:
Die meisten Broadcasts haben dieses Schema:
```json
{
"type": "EVENT_TYPE",
"payload": { },
"timestamp": "2026-03-05T10:00:00.000Z"
"payload": {},
"timestamp": "2026-03-10T09:00:00.000Z"
}
```
Ausnahme: `WS_CONNECTED` beim Verbindungsaufbau enthält kein `timestamp`.
---
## Event-Typen
### WS_CONNECTED
Wird direkt nach Verbindungsaufbau gesendet.
Sofort nach erfolgreicher Verbindung.
```json
{
"type": "WS_CONNECTED",
"payload": {
"connectedAt": "2026-03-05T10:00:00.000Z"
"connectedAt": "2026-03-10T09:00:00.000Z"
}
}
```
### PIPELINE_STATE_CHANGED
Snapshot bei Zustandswechsel.
Neuer Pipeline-Snapshot.
```json
{
@@ -56,14 +58,24 @@ Snapshot bei Zustandswechsel.
"payload": {
"state": "ENCODING",
"activeJobId": 42,
"progress": 73.5,
"progress": 62.5,
"eta": "00:12:34",
"statusText": "Encoding mit HandBrake",
"statusText": "ENCODING 62.50%",
"context": {},
"jobProgress": {
"42": {
"state": "ENCODING",
"progress": 62.5,
"eta": "00:12:34",
"statusText": "ENCODING 62.50%"
}
},
"queue": {
"maxParallelJobs": 1,
"runningCount": 1,
"queuedCount": 0
"queuedCount": 2,
"runningJobs": [],
"queuedJobs": []
}
}
}
@@ -71,7 +83,7 @@ Snapshot bei Zustandswechsel.
### PIPELINE_PROGRESS
Laufende Fortschrittsupdates während aktiver Phasen.
Laufende Fortschrittsupdates.
```json
{
@@ -79,33 +91,20 @@ Laufende Fortschrittsupdates während aktiver Phasen.
"payload": {
"state": "ENCODING",
"activeJobId": 42,
"progress": 73.5,
"progress": 62.5,
"eta": "00:12:34",
"statusText": "ENCODING 73.50% - task 1 of 1"
"statusText": "ENCODING 62.50%"
}
}
```
### PIPELINE_QUEUE_CHANGED
Aktualisierung der Job-Queue.
Queue-Snapshot aktualisiert.
```json
{
"type": "PIPELINE_QUEUE_CHANGED",
"payload": {
"maxParallelJobs": 1,
"runningCount": 1,
"queuedCount": 2,
"runningJobs": [],
"queuedJobs": []
}
}
```
### DISC_DETECTED / DISC_REMOVED
### DISC_DETECTED
Disc erkannt.
Disc-Insertion/-Removal.
```json
{
@@ -114,7 +113,6 @@ Disc erkannt.
"device": {
"path": "/dev/sr0",
"discLabel": "INCEPTION",
"label": "INCEPTION",
"model": "ASUS BW-16D1HT",
"fstype": "udf",
"mountpoint": null,
@@ -124,132 +122,93 @@ Disc erkannt.
}
```
`mediaProfile` ist `"bluray"`, `"dvd"`, `"other"` oder `null` (wenn nicht bestimmbar). Der Wert wird aus Dateisystemtyp (UDF/ISO9660), Laufwerk-Modell und Disc-Label abgeleitet.
`mediaProfile`: `bluray` | `dvd` | `other` | `null`
### DISC_REMOVED
### HARDWARE_MONITOR_UPDATE
Disc entfernt.
Snapshot aus Hardware-Monitoring.
```json
{
"type": "DISC_REMOVED",
"type": "HARDWARE_MONITOR_UPDATE",
"payload": {
"device": {
"path": "/dev/sr0"
}
"enabled": true,
"intervalMs": 5000,
"updatedAt": "2026-03-10T09:00:00.000Z",
"sample": {
"cpu": {},
"memory": {},
"gpu": {},
"storage": {}
},
"error": null
}
}
```
### PIPELINE_ERROR
Fehler bei Pipeline-Disc-Events im Backend.
```json
{
"type": "PIPELINE_ERROR",
"payload": {
"message": "..."
}
}
```
Fehler bei Disc-Event-Verarbeitung in Pipeline.
### DISK_DETECTION_ERROR
Fehler im Laufwerkserkennungsdienst.
Fehler in Laufwerkserkennung.
### SETTINGS_UPDATED
Einzelnes Setting wurde gespeichert.
### SETTINGS_BULK_UPDATED
Bulk-Settings gespeichert.
```json
{
"type": "DISK_DETECTION_ERROR",
"type": "SETTINGS_BULK_UPDATED",
"payload": {
"message": "..."
"count": 3,
"keys": ["raw_dir", "movie_dir", "handbrake_preset_bluray"]
}
}
```
### SETTINGS_SCRIPTS_UPDATED
Wird gesendet, wenn ein Skript angelegt, aktualisiert, gelöscht oder umsortiert wurde.
```json
{
"type": "SETTINGS_SCRIPTS_UPDATED",
"payload": {
"action": "reordered",
"count": 3
}
}
```
`action` ist `"created"`, `"updated"`, `"deleted"` oder `"reordered"`.
Skript geändert (`created|updated|deleted|reordered`).
### SETTINGS_SCRIPT_CHAINS_UPDATED
Wird gesendet bei Änderungen an Skript-Ketten.
```json
{
"type": "SETTINGS_SCRIPT_CHAINS_UPDATED",
"payload": {
"action": "created",
"id": 2
}
}
```
Skript-Kette geändert (`created|updated|deleted|reordered`).
### USER_PRESETS_UPDATED
Wird gesendet, wenn ein User-Preset angelegt, aktualisiert oder gelöscht wurde.
```json
{
"type": "USER_PRESETS_UPDATED",
"payload": {
"action": "created",
"id": 1
}
}
```
`action` ist `"created"`, `"updated"` oder `"deleted"`.
User-Preset geändert (`created|updated|deleted`).
### CRON_JOBS_UPDATED
Wird gesendet, wenn ein Cron-Job angelegt, aktualisiert oder gelöscht wurde.
Cron-Config geändert (`created|updated|deleted`).
### CRON_JOB_UPDATED
Laufzeitstatus eines Cron-Jobs geändert.
```json
{
"type": "CRON_JOBS_UPDATED",
"type": "CRON_JOB_UPDATED",
"payload": {
"action": "created",
"id": 1
"id": 1,
"lastRunStatus": "running",
"lastRunAt": "2026-03-10T10:00:00.000Z",
"nextRunAt": null
}
}
```
`action` ist `"created"`, `"updated"` oder `"deleted"`.
---
## Reconnect-Verhalten
`useWebSocket.js` versucht bei Verbindungsabbruch automatisch erneut zu verbinden.
`useWebSocket` verbindet bei Abbruch automatisch neu:
- fester Retry-Intervall: `1500ms`
- erneuter Versuch bis zum Unmount der Komponente
---
## React-Beispiel
```js
import { useWebSocket } from './hooks/useWebSocket';
useWebSocket({
onMessage: (msg) => {
if (msg.type === 'PIPELINE_STATE_CHANGED') {
setPipeline(msg.payload);
}
}
});
```
- Retry-Intervall: `1500ms`
- Wiederverbindung bis Komponente unmounted wird