0.13.1-4 release snapshot

This commit is contained in:
2026-04-13 11:52:07 +00:00
parent 6115090da1
commit ba3732af6b
176 changed files with 103457 additions and 1 deletions
+290
View File
@@ -0,0 +1,290 @@
# Converter API
Endpunkte für den Datei-Converter: Datei-Explorer, Upload, Job-Verwaltung.
Basis-Pfad: `/api/converter`
---
## Scan & Explorer
### `GET /api/converter/tree`
Vollständiger Verzeichnisbaum des `converter_raw_dir` (FS-basiert, keine DB).
**Response:**
```json
{
"tree": [
{
"name": "filme",
"relPath": "filme",
"isDir": true,
"children": [
{
"name": "film.mkv",
"relPath": "filme/film.mkv",
"isDir": false,
"size": 10485760
}
]
}
]
}
```
---
### `GET /api/converter/browse?parent=relPath`
DB-basierter Datei-Explorer. Gibt Einträge für einen Unterordner zurück.
**Query-Parameter:**
| Parameter | Typ | Beschreibung |
|---|---|---|
| `parent` | string | Relativer Pfad zum Unterordner (leer = Root) |
**Response:**
```json
{
"entries": [
{
"id": 1,
"rel_path": "filme/film.mkv",
"is_dir": false,
"size": 10485760,
"job_id": null
}
],
"rawDir": "/data/output/converter-raw"
}
```
---
### `POST /api/converter/scan`
Manuellen Scan des `converter_raw_dir` auslösen. Aktualisiert `converter_scan_entries` in der DB.
**Response:**
```json
{ "result": { "added": 3, "removed": 1 } }
```
---
## Jobs erstellen
### `POST /api/converter/jobs/from-selection`
Jobs aus im Datei-Explorer ausgewählten Dateien erstellen.
**Body:**
```json
{
"relPaths": ["filme/film.mkv", "musik/track.flac"],
"audioMode": "individual"
}
```
| Feld | Typ | Beschreibung |
|---|---|---|
| `relPaths` | string[] | Relative Pfade der ausgewählten Dateien |
| `audioMode` | string | `individual` (ein Job pro Datei) oder `shared` (ein gemeinsamer Job) |
**Response:**
```json
{ "jobs": [{ "id": 42, "status": "READY_TO_START" }] }
```
---
### `POST /api/converter/create-jobs`
Jobs aus DB-Scan-Einträgen erstellen.
**Body:**
```json
{
"entries": [
{ "relPath": "filme/film.mkv", "converterMediaType": "video" }
]
}
```
---
### `POST /api/converter/upload`
Dateien hochladen (Multipart, max. 50 Dateien).
**Form-Felder:**
| Feld | Typ | Beschreibung |
|---|---|---|
| `files` | File[] | Hochzuladende Dateien |
| `folderName` | string | (Optional) Ziel-Unterordner |
**Response:**
```json
{
"folders": [{ "folderRelPath": "upload-2026-03-30", "fileCount": 2 }]
}
```
---
## Job-Verwaltung
### `GET /api/converter/jobs`
Alle Converter-Jobs zurückgeben.
**Response:**
```json
{ "jobs": [{ "id": 42, "status": "READY_TO_START", "title": "film.mkv" }] }
```
---
### `GET /api/converter/jobs/:jobId`
Einzelnen Converter-Job abrufen.
---
### `POST /api/converter/jobs/:jobId/config`
Konfigurationsentwurf für einen Job speichern (persistiert in `encode_plan_json`).
**Body:** Partielles Konfig-Objekt (Ausgabeformat, Presets, Metadaten, Track-Auswahl).
---
### `POST /api/converter/jobs/:jobId/assign-files`
Dateien einem bestehenden (noch nicht gestarteten) Job hinzufügen.
**Body:**
```json
{ "relPaths": ["musik/track2.flac"] }
```
---
### `POST /api/converter/jobs/:jobId/remove-file`
Datei aus einem Job entfernen (per relativer Pfad).
**Body:**
```json
{ "relPath": "musik/track2.flac" }
```
---
### `POST /api/converter/jobs/:jobId/remove-input`
Datei aus einem Job entfernen (per absolutem Eingabepfad).
**Body:**
```json
{ "inputPath": "/data/output/converter-raw/musik/track2.flac" }
```
---
### `POST /api/converter/jobs/:jobId/start`
Job mit finaler Konfiguration starten.
**Body:**
```json
{
"converterMediaType": "video",
"outputFormat": "mkv",
"userPreset": "H.264 MKV 1080p30",
"trackSelection": {},
"handBrakeTitleId": 1,
"audioFormatOptions": {}
}
```
---
### `POST /api/converter/jobs/:jobId/cancel`
Laufenden Job abbrechen.
---
### `DELETE /api/converter/jobs/:jobId`
Job aus der DB löschen.
---
## Datei-Operationen
Alle Datei-Operationen arbeiten direkt auf dem Dateisystem (ohne DB-Aktualisierung). Ein anschließender Scan-Aufruf synchronisiert die DB.
### `DELETE /api/converter/files`
Datei oder Ordner löschen.
**Body:**
```json
{ "relPath": "filme/film.mkv" }
```
---
### `POST /api/converter/files/rename`
Datei oder Ordner umbenennen.
**Body:**
```json
{ "relPath": "filme/film.mkv", "newName": "film-neu.mkv" }
```
---
### `POST /api/converter/files/move`
Datei oder Ordner verschieben.
**Body:**
```json
{ "relPath": "filme/film.mkv", "targetParentRelPath": "archiv" }
```
`targetParentRelPath = ""` verschiebt in das Root-Verzeichnis.
---
### `POST /api/converter/files/folder`
Neuen Ordner anlegen.
**Body:**
```json
{ "parentRelPath": "filme", "name": "neu" }
```
+182
View File
@@ -0,0 +1,182 @@
# Cron API
Ripster enthält ein eingebautes Cron-System für Skripte und Skript-Ketten (`sourceType: script|chain`).
---
## GET /api/crons
Listet alle Cron-Jobs.
```json
{
"jobs": [
{
"id": 1,
"name": "Nachtlauf Backup",
"cronExpression": "0 2 * * *",
"sourceType": "script",
"sourceId": 3,
"sourceName": "Backup-Skript",
"enabled": true,
"pushoverEnabled": true,
"lastRunAt": "2026-03-10T02:00:00.000Z",
"lastRunStatus": "success",
"nextRunAt": "2026-03-11T02:00:00.000Z",
"createdAt": "2026-03-01T10:00:00.000Z",
"updatedAt": "2026-03-10T02:00:05.000Z"
}
]
}
```
---
## POST /api/crons
Erstellt Cron-Job.
```json
{
"name": "Nachtlauf Backup",
"cronExpression": "0 2 * * *",
"sourceType": "script",
"sourceId": 3,
"enabled": true,
"pushoverEnabled": true
}
```
Response: `201` mit `{ "job": { ... } }`
---
## GET /api/crons/:id
Response:
```json
{ "job": { "id": 1, "name": "..." } }
```
---
## PUT /api/crons/:id
Aktualisiert Cron-Job. Felder wie bei `POST`.
Response:
```json
{ "job": { ... } }
```
---
## DELETE /api/crons/:id
Response:
```json
{ "removed": { "id": 1, "name": "Nachtlauf Backup" } }
```
---
## GET /api/crons/:id/logs
Liefert Ausführungs-Logs.
**Query-Parameter:**
| Parameter | Typ | Default | Beschreibung |
|-----------|-----|---------|-------------|
| `limit` | number | `20` | Anzahl Einträge, max. `100` |
**Response:**
```json
{
"logs": [
{
"id": 42,
"cronJobId": 1,
"startedAt": "2026-03-10T02:00:01.000Z",
"finishedAt": "2026-03-10T02:00:05.000Z",
"status": "success",
"output": "Backup abgeschlossen.",
"errorMessage": null
}
]
}
```
`status`: `running` | `success` | `error`
---
## POST /api/crons/:id/run
Triggert Job manuell (asynchron).
**Response:**
```json
{ "triggered": true, "cronJobId": 1 }
```
Wenn Job bereits läuft: `409`.
---
## POST /api/crons/validate-expression
Validiert 5-Felder-Cron-Ausdruck und berechnet nächsten Lauf.
**Request:**
```json
{ "cronExpression": "*/15 * * * *" }
```
**Gültige Response:**
```json
{
"valid": true,
"nextRunAt": "2026-03-10T14:15:00.000Z"
}
```
**Ungültige Response:**
```json
{
"valid": false,
"error": "Cron-Ausdruck muss genau 5 Felder haben (Minute Stunde Tag Monat Wochentag).",
"nextRunAt": null
}
```
---
## Cron-Format
Ripster unterstützt 5 Felder:
```text
Minute Stunde Tag Monat Wochentag
```
Beispiele:
- `0 2 * * *` täglich 02:00
- `*/15 * * * *` alle 15 Minuten
- `0 6 * * 1-5` Mo-Fr 06:00
---
## WebSocket-Events zu Cron
- `CRON_JOBS_UPDATED` bei Create/Update/Delete
- `CRON_JOB_UPDATED` bei Laufzeitstatus (`running` -> `success|error`)
+143
View File
@@ -0,0 +1,143 @@
# Downloads API
Endpunkte für die Download-Queue. Ausgabedateien aus der Job-Historie können als ZIP heruntergeladen werden.
Basis-Pfad: `/api/downloads`
---
## `GET /api/downloads`
Liste aller Download-Einträge plus Zusammenfassung.
**Response:**
```json
{
"items": [
{
"id": "dl-42-raw",
"jobId": 42,
"target": "raw",
"status": "ready",
"archiveName": "Job_42_raw.zip",
"outputPath": null,
"createdAt": "2026-03-30T10:00:00.000Z",
"updatedAt": "2026-03-30T10:01:00.000Z",
"errorMessage": null
}
],
"summary": {
"total": 3,
"pending": 1,
"processing": 0,
"ready": 1,
"failed": 1
}
}
```
**Status-Werte:**
| Status | Beschreibung |
|---|---|
| `pending` | In der Queue, noch nicht gestartet |
| `processing` | ZIP wird gerade erstellt |
| `ready` | ZIP fertig, Download verfügbar |
| `failed` | Fehler bei der Erstellung |
---
## `GET /api/downloads/summary`
Nur die Zusammenfassung der Download-Queue (ohne Item-Liste).
**Response:**
```json
{
"summary": {
"total": 3,
"pending": 1,
"processing": 0,
"ready": 1,
"failed": 1
}
}
```
---
## `POST /api/downloads/history/:jobId`
Job-Ausgabe in die Download-Queue einreihen.
**Parameter:**
| Parameter | Typ | Beschreibung |
|---|---|---|
| `jobId` | number | ID des Jobs in der Historie |
**Body:**
```json
{
"target": "raw",
"outputPath": null
}
```
| Feld | Typ | Default | Beschreibung |
|---|---|---|---|
| `target` | string | `raw` | `raw` (RAW-Dateien) oder `movie` (Ausgabedateien) |
| `outputPath` | string | `null` | Optionaler expliziter Ausgabepfad |
**Response (201 Created):**
```json
{
"created": true,
"id": "dl-42-raw",
"status": "pending",
"summary": { "total": 1, "pending": 1, "processing": 0, "ready": 0, "failed": 0 }
}
```
Ist der Eintrag bereits vorhanden, wird `201` durch `200` ersetzt und `created: false` zurückgegeben.
---
## `GET /api/downloads/:id/file`
Fertige ZIP-Datei herunterladen.
**Parameter:**
| Parameter | Typ | Beschreibung |
|---|---|---|
| `id` | string | Download-ID (z. B. `dl-42-raw`) |
**Response:** Datei-Download (`Content-Disposition: attachment`).
Schlägt fehl mit `404`, wenn kein Download-Eintrag mit dieser ID existiert oder die Datei noch nicht bereit ist.
---
## `DELETE /api/downloads/:id`
Download-Eintrag löschen (auch fertige ZIP-Dateien werden vom Dateisystem entfernt).
**Response:**
```json
{
"ok": true,
"summary": { "total": 2, "pending": 0, "processing": 0, "ready": 2, "failed": 0 }
}
```
---
## WebSocket
Statusänderungen werden über `DOWNLOADS_UPDATED` in Echtzeit gemeldet. Vollständige Dokumentation: [WebSocket Events](websocket.md#downloads_updated).
+221
View File
@@ -0,0 +1,221 @@
# History API
Endpunkte für Job-Historie, Orphan-Import und Löschoperationen.
---
## GET /api/history
Liefert Jobs (optionale Filter).
**Query-Parameter:**
| Parameter | Typ | Beschreibung |
|----------|-----|-------------|
| `status` | string | Filter nach Job-Status |
| `search` | string | Suche in Titel-Feldern |
**Beispiel:**
```text
GET /api/history?status=FINISHED&search=Inception
```
**Response:**
```json
{
"jobs": [
{
"id": 42,
"status": "FINISHED",
"title": "Inception",
"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"
}
]
}
```
---
## GET /api/history/:id
Liefert Job-Detail.
**Query-Parameter:**
| Parameter | Typ | Standard | Beschreibung |
|----------|-----|---------|-------------|
| `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
{
"job": {
"id": 42,
"status": "FINISHED",
"makemkvInfo": {},
"mediainfoInfo": {},
"handbrakeInfo": {},
"encodePlan": {},
"log": "...",
"log_count": 1,
"logMeta": {
"loaded": true,
"total": 800,
"returned": 800,
"truncated": true
}
}
}
```
---
## GET /api/history/orphan-raw
Sucht RAW-Ordner ohne zugehörigen Job.
**Response:**
```json
{
"rawDir": "/mnt/raw",
"rawDirs": ["/mnt/raw", "/mnt/raw-bluray"],
"rows": [
{
"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"
}
]
}
```
---
## POST /api/history/orphan-raw/import
Importiert RAW-Ordner als FINISHED-Job.
**Request:**
```json
{ "rawPath": "/mnt/raw/Inception (2010) [tt1375666] - RAW - job-99" }
```
**Response:**
```json
{
"job": { "id": 77, "status": "FINISHED" },
"uiReset": { "reset": true, "state": "IDLE" }
}
```
---
## POST /api/history/:id/omdb/assign
Weist OMDb-/Metadaten nachträglich zu.
**Request:**
```json
{
"imdbId": "tt1375666",
"title": "Inception",
"year": 2010,
"poster": "https://...",
"fromOmdb": true
}
```
**Response:**
```json
{ "job": { "id": 42, "imdb_id": "tt1375666" } }
```
---
## POST /api/history/:id/delete-files
Löscht Dateien eines Jobs, behält DB-Eintrag.
**Request:**
```json
{ "target": "both" }
```
`target`: `raw` | `movie` | `both`
**Response:**
```json
{
"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 }
}
```
---
## POST /api/history/:id/delete
Löscht Job aus DB; optional auch Dateien.
**Request:**
```json
{ "target": "none" }
```
`target`: `none` | `raw` | `movie` | `both`
**Response:**
```json
{
"deleted": true,
"jobId": 42,
"fileTarget": "both",
"fileSummary": {
"target": "both",
"raw": { "filesDeleted": 10 },
"movie": { "filesDeleted": 1 }
},
"uiReset": {
"reset": true,
"state": "IDLE"
}
}
```
---
## Hinweise
- Ein aktiver Pipeline-Job kann nicht gelöscht werden (`409`).
- Alle Löschoperationen sind irreversibel.
+93
View File
@@ -0,0 +1,93 @@
# Anhang: API-Referenz
REST- und WebSocket-Schnittstellen für Integration, Automatisierung und Debugging.
## Basis-URL
```text
http://localhost:3001
```
API-Prefix: `/api`
## API-Gruppen
<div class="grid cards" markdown>
- :material-heart-pulse: **Health**
---
Service-Liveness.
`GET /api/health`
- :material-pipe: **Pipeline API**
---
Analyse, Start/Retry/Cancel, Queue, Re-Encode.
[:octicons-arrow-right-24: Pipeline API](pipeline.md)
- :material-cog: **Settings API**
---
Einstellungen, Skripte/Ketten, User-Presets.
[:octicons-arrow-right-24: Settings API](settings.md)
- :material-history: **History API**
---
Job-Historie, Orphan-Import, Löschoperationen.
[:octicons-arrow-right-24: History API](history.md)
- :material-clock-outline: **Cron API**
---
Zeitgesteuerte Skript-/Kettenausführung.
[:octicons-arrow-right-24: Cron API](crons.md)
- :material-swap-horizontal: **Converter API**
---
Datei-Explorer, Upload, Job-Verwaltung für den Converter.
[:octicons-arrow-right-24: Converter API](converter.md)
- :material-download: **Downloads API**
---
Download-Queue für Ausgabedateien aus der Job-Historie.
[:octicons-arrow-right-24: Downloads API](downloads.md)
- :material-lightning-bolt: **Runtime Activities API**
---
Laufende und abgeschlossene Aktivitäten (Skripte, Ketten, Cron, Tasks).
[:octicons-arrow-right-24: Runtime Activities](runtime-activities.md)
- :material-websocket: **WebSocket Events**
---
Pipeline-, Queue-, Disk-, Settings-, Cron-, Converter- und Download-Events.
[:octicons-arrow-right-24: WebSocket](websocket.md)
</div>
## Hinweis
Ripster hat keine eingebaute Authentifizierung und ist für lokalen, geschützten Betrieb gedacht.
+369
View File
@@ -0,0 +1,369 @@
# Pipeline API
Endpunkte zur Steuerung des Pipeline-Workflows.
---
## GET /api/pipeline/state
Liefert aktuellen Pipeline- und Hardware-Monitoring-Snapshot.
**Response (Beispiel):**
```json
{
"pipeline": {
"state": "READY_TO_ENCODE",
"activeJobId": 42,
"progress": 0,
"eta": null,
"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": 1,
"queuedCount": 2,
"runningJobs": [],
"queuedJobs": []
}
},
"hardwareMonitoring": {
"enabled": true,
"intervalMs": 5000,
"updatedAt": "2026-03-10T09:00:00.000Z",
"sample": {
"cpu": {},
"memory": {},
"gpu": {},
"storage": {}
},
"error": null
}
}
```
---
## POST /api/pipeline/analyze
Startet Disc-Analyse und legt Job an.
**Response:**
```json
{
"result": {
"jobId": 42,
"detectedTitle": "INCEPTION",
"omdbCandidates": []
}
}
```
---
## POST /api/pipeline/rescan-disc
Erzwingt erneute Laufwerksprüfung.
**Response (Beispiel):**
```json
{
"result": {
"present": true,
"changed": true,
"emitted": "discInserted",
"device": {
"path": "/dev/sr0",
"discLabel": "INCEPTION",
"mediaProfile": "bluray"
}
}
}
```
---
## GET /api/pipeline/omdb/search?q=<query>
OMDb-Titelsuche.
**Response:**
```json
{
"results": [
{
"imdbId": "tt1375666",
"title": "Inception",
"year": "2010",
"type": "movie",
"poster": "https://..."
}
]
}
```
---
## POST /api/pipeline/select-metadata
Setzt Metadaten (und optional Playlist) für einen Job.
**Request:**
```json
{
"jobId": 42,
"title": "Inception",
"year": 2010,
"imdbId": "tt1375666",
"poster": "https://...",
"fromOmdb": true,
"selectedPlaylist": "00800"
}
```
**Response:**
```json
{ "job": { "id": 42, "status": "READY_TO_START" } }
```
---
## POST /api/pipeline/start/:jobId
Startet vorbereiteten Job oder queued ihn (je nach Parallel-Limit).
**Mögliche Responses:**
```json
{ "result": { "started": true, "stage": "RIPPING" } }
```
```json
{ "result": { "queued": true, "started": false, "queuePosition": 2, "action": "START_PREPARED" } }
```
---
## POST /api/pipeline/confirm-encode/:jobId
Bestätigt Review-Auswahl (Tracks, Pre/Post-Skripte/Ketten, User-Preset).
**Request (typisch):**
```json
{
"selectedEncodeTitleId": 1,
"selectedTrackSelection": {
"1": {
"audioTrackIds": [1, 2],
"subtitleTrackIds": [3]
}
},
"selectedPreEncodeScriptIds": [1],
"selectedPostEncodeScriptIds": [2, 7],
"selectedPreEncodeChainIds": [3],
"selectedPostEncodeChainIds": [4],
"selectedUserPresetId": 5,
"skipPipelineStateUpdate": false
}
```
**Response:**
```json
{ "job": { "id": 42, "encode_review_confirmed": 1 } }
```
---
## POST /api/pipeline/cancel
Bricht laufenden Job ab oder entfernt Queue-Eintrag.
**Request (optional):**
```json
{ "jobId": 42 }
```
**Mögliche Responses:**
```json
{ "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
Retry für `ERROR`/`CANCELLED`-Jobs (oder Queue-Einreihung).
## POST /api/pipeline/reencode/:jobId
Startet Re-Encode aus bestehendem RAW.
## POST /api/pipeline/restart-review/:jobId
Berechnet Review aus RAW neu.
## POST /api/pipeline/restart-encode/:jobId
Startet Encoding mit letzter bestätigter Review neu.
## POST /api/pipeline/resume-ready/:jobId
Lädt `READY_TO_ENCODE`-Job nach Neustart wieder in aktive Session.
Alle Endpunkte liefern `{ result: ... }` bzw. `{ job: ... }`.
---
## Queue-Endpunkte
### GET /api/pipeline/queue
Liefert Queue-Snapshot.
```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
Sortiert Queue-Einträge neu.
**Request:**
```json
{
"orderedEntryIds": [12, 11]
}
```
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 |
+235
View File
@@ -0,0 +1,235 @@
# Runtime Activities API
Ripster verfolgt alle laufenden und kürzlich abgeschlossenen Aktivitäten (Skripte, Skript-Ketten, Cron-Jobs, interne Tasks) in Echtzeit über den `RuntimeActivityService`.
---
## Übersicht
Aktivitäten entstehen, wenn Ripster intern Aktionen ausführt z. B. beim Start eines Cron-Jobs, beim Ausführen einer Skript-Kette oder beim Durchlaufen von Pipeline-Schritten. Sie sind **nicht persistent** (kein DB-Speicher) und werden nur im Arbeitsspeicher gehalten.
- **Aktive Aktivitäten** (`active`): Laufen gerade.
- **Letzte Aktivitäten** (`recent`): Abgeschlossen, max. 120 Einträge.
Änderungen werden über WebSocket (`RUNTIME_ACTIVITY_CHANGED`) in Echtzeit gesendet.
---
## Aktivitäts-Objekt
```json
{
"id": 7,
"type": "chain",
"name": "Post-Encode Aufräumen",
"status": "running",
"source": "cron",
"message": "Schritt 2 von 3",
"currentStep": "cleanup.sh",
"currentStepType": "script",
"currentScriptName": "cleanup.sh",
"stepIndex": 2,
"stepTotal": 3,
"parentActivityId": null,
"jobId": 42,
"cronJobId": 3,
"chainId": 5,
"scriptId": null,
"canCancel": true,
"canNextStep": false,
"outcome": "running",
"errorMessage": null,
"output": null,
"stdout": null,
"stderr": null,
"stdoutTruncated": false,
"stderrTruncated": false,
"startedAt": "2026-03-10T10:00:00.000Z",
"finishedAt": null,
"durationMs": null,
"exitCode": null,
"success": null
}
```
### Felder
| Feld | Typ | Beschreibung |
|------|-----|--------------|
| `id` | `number` | Eindeutige ID (Laufzähler, nicht persistent) |
| `type` | `string` | Art der Aktivität: `script` \| `chain` \| `cron` \| `task` |
| `name` | `string \| null` | Anzeigename der Aktivität |
| `status` | `string` | Aktueller Status: `running` \| `success` \| `error` |
| `source` | `string \| null` | Auslöser (z. B. `cron`, `pipeline`, `manual`) |
| `message` | `string \| null` | Kurztext zum aktuellen Zustand |
| `currentStep` | `string \| null` | Name des aktuell ausgeführten Schritts |
| `currentStepType` | `string \| null` | Typ des Schritts (z. B. `script`, `wait`) |
| `currentScriptName` | `string \| null` | Name des Skripts im aktuellen Schritt |
| `stepIndex` | `number \| null` | Aktueller Schritt (1-basiert) |
| `stepTotal` | `number \| null` | Gesamtanzahl Schritte |
| `parentActivityId` | `number \| null` | ID der übergeordneten Aktivität |
| `jobId` | `number \| null` | Verknüpfte Job-ID |
| `cronJobId` | `number \| null` | Verknüpfte Cron-Job-ID |
| `chainId` | `number \| null` | Verknüpfte Skript-Ketten-ID |
| `scriptId` | `number \| null` | Verknüpfte Skript-ID |
| `canCancel` | `boolean` | Abbrechen über API möglich |
| `canNextStep` | `boolean` | Nächster Schritt über API auslösbar |
| `outcome` | `string \| null` | Abschluss-Ergebnis: `success` \| `error` \| `cancelled` \| `skipped` \| `running` |
| `errorMessage` | `string \| null` | Fehlermeldung (max. 2.000 Zeichen) |
| `output` | `string \| null` | Allgemeine Ausgabe (max. 12.000 Zeichen) |
| `stdout` | `string \| null` | Standardausgabe des Prozesses (max. 12.000 Zeichen) |
| `stderr` | `string \| null` | Fehlerausgabe des Prozesses (max. 12.000 Zeichen) |
| `stdoutTruncated` | `boolean` | `true`, wenn `stdout` gekürzt wurde |
| `stderrTruncated` | `boolean` | `true`, wenn `stderr` gekürzt wurde |
| `startedAt` | `string` | ISO-8601-Zeitstempel des Starts |
| `finishedAt` | `string \| null` | ISO-8601-Zeitstempel des Endes |
| `durationMs` | `number \| null` | Laufzeit in Millisekunden |
| `exitCode` | `number \| null` | Exit-Code des Prozesses |
| `success` | `boolean \| null` | Erfolgsstatus (`null` bei laufender Aktivität) |
---
## Snapshot-Objekt
Alle Aktivitäts-Endpunkte geben einen Snapshot zurück:
```json
{
"active": [ /* laufende Aktivitäten, nach startedAt absteigend */ ],
"recent": [ /* abgeschlossene Aktivitäten, nach finishedAt absteigend, max. 120 */ ],
"updatedAt": "2026-03-10T10:05:00.000Z"
}
```
---
## Endpunkte
### GET `/api/activities`
Aktuellen Aktivitäts-Snapshot abrufen.
**Antwort:**
```json
{
"active": [],
"recent": [
{
"id": 5,
"type": "script",
"name": "notify.sh",
"status": "success",
"outcome": "success",
"startedAt": "2026-03-10T09:58:00.000Z",
"finishedAt": "2026-03-10T09:58:02.000Z",
"durationMs": 2100,
"exitCode": 0,
"success": true,
"canCancel": false,
"canNextStep": false
}
],
"updatedAt": "2026-03-10T10:05:00.000Z"
}
```
---
### POST `/api/activities/:id/cancel`
Aktive Aktivität abbrechen (nur wenn `canCancel: true`).
**Parameter:**
| Name | In | Typ | Beschreibung |
|------|----|-----|--------------|
| `id` | path | `number` | Aktivitäts-ID |
| `reason` | body | `string` | Optionaler Abbruchgrund |
**Request Body:**
```json
{ "reason": "Manueller Abbruch durch Benutzer" }
```
**Antwort (Erfolg):**
```json
{
"ok": true,
"action": null,
"snapshot": { "active": [], "recent": [], "updatedAt": "..." }
}
```
**Fehlercodes:**
| HTTP | Bedeutung |
|------|-----------|
| `404` | Aktivität nicht gefunden oder bereits abgeschlossen |
| `409` | Abbrechen wird von dieser Aktivität nicht unterstützt |
---
### POST `/api/activities/:id/next-step`
Nächsten Schritt einer Aktivität auslösen (nur wenn `canNextStep: true`).
**Parameter:**
| Name | In | Typ | Beschreibung |
|------|----|-----|--------------|
| `id` | path | `number` | Aktivitäts-ID |
**Antwort (Erfolg):**
```json
{
"ok": true,
"action": null,
"snapshot": { "active": [], "recent": [], "updatedAt": "..." }
}
```
**Fehlercodes:**
| HTTP | Bedeutung |
|------|-----------|
| `404` | Aktivität nicht gefunden |
| `409` | Nächster Schritt wird von dieser Aktivität nicht unterstützt |
---
### POST `/api/activities/clear-recent`
Alle abgeschlossenen Aktivitäten aus `recent` löschen.
**Antwort:**
```json
{
"ok": true,
"removed": 14,
"snapshot": { "active": [], "recent": [], "updatedAt": "..." }
}
```
---
## Grenzwerte
| Wert | Limit |
|------|-------|
| Maximale `recent`-Einträge | 120 |
| Maximale Länge `stdout` / `stderr` / `output` | 12.000 Zeichen |
| Maximale Länge `errorMessage` / `message` | 2.000 Zeichen |
| Maximale Länge `outcome` | 40 Zeichen |
Gekürzte Ausgaben erhalten den Suffix ` ...[gekürzt]` (bei Inline-Text) bzw. `\n...[gekürzt]` (bei mehrzeiligem Output).
---
## Echtzeit-Updates
Änderungen werden automatisch als [`RUNTIME_ACTIVITY_CHANGED`](websocket.md#runtime_activity_changed) WebSocket-Event gesendet. Die Frontend-Komponente braucht `GET /api/activities` nur beim initialen Laden aufzurufen.
+338
View File
@@ -0,0 +1,338 @@
# Settings API
Endpunkte für Einstellungen, Skripte, Skript-Ketten und User-Presets.
---
## GET /api/settings
Liefert alle Einstellungen kategorisiert.
**Response (Struktur):**
```json
{
"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
}
]
}
]
}
```
---
## PUT /api/settings/:key
Aktualisiert eine einzelne Einstellung.
**Request:**
```json
{ "value": "/mnt/storage/raw" }
```
**Response:**
```json
{
"setting": {
"key": "raw_dir",
"value": "/mnt/storage/raw"
},
"reviewRefresh": {
"triggered": false,
"reason": "not_ready"
}
}
```
`reviewRefresh` ist `null` oder ein Objekt mit Status der optionalen Review-Neuberechnung.
---
## PUT /api/settings
Aktualisiert mehrere Einstellungen atomar.
**Request:**
```json
{
"settings": {
"raw_dir": "/mnt/storage/raw",
"movie_dir": "/mnt/storage/movies",
"handbrake_preset_bluray": "H.264 MKV 1080p30"
}
}
```
**Response:**
```json
{
"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" }
]
}
```
---
## POST /api/settings/pushover/test
Sendet Testnachricht über aktuelle PushOver-Settings.
**Request (optional):**
```json
{
"title": "Test",
"message": "Ripster Test"
}
```
**Response:**
```json
{
"result": {
"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
```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
Basis: `/api/settings/user-presets`
### GET /api/settings/user-presets
Optionaler Query-Parameter: `media_type=bluray|dvd|other|all`
```json
{
"presets": [
{
"id": 1,
"name": "Blu-ray HQ",
"mediaType": "bluray",
"handbrakePreset": "H.264 MKV 1080p30",
"extraArgs": "--encoder-preset slow",
"description": "...",
"createdAt": "...",
"updatedAt": "..."
}
]
}
```
### POST /api/settings/user-presets
```json
{
"name": "Blu-ray HQ",
"mediaType": "bluray",
"handbrakePreset": "H.264 MKV 1080p30",
"extraArgs": "--encoder-preset slow",
"description": "optional"
}
```
Response: `201` mit `{ "preset": { ... } }`
### PUT /api/settings/user-presets/:id
Body mit beliebigen Feldern aus `POST`, Response `{ "preset": { ... } }`.
### DELETE /api/settings/user-presets/:id
Response `{ "removed": { ... } }`.
+294
View File
@@ -0,0 +1,294 @@
# WebSocket Events
Ripster sendet Echtzeit-Updates über `/ws`.
---
## Verbindung
```js
const ws = new WebSocket('ws://localhost:3001/ws');
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
console.log(msg.type, msg.payload);
};
```
---
## Nachrichtenformat
Die meisten Broadcasts haben dieses Schema:
```json
{
"type": "EVENT_TYPE",
"payload": {},
"timestamp": "2026-03-10T09:00:00.000Z"
}
```
Ausnahme: `WS_CONNECTED` beim Verbindungsaufbau enthält kein `timestamp`.
---
## Event-Typen
### WS_CONNECTED
Sofort nach erfolgreicher Verbindung.
```json
{
"type": "WS_CONNECTED",
"payload": {
"connectedAt": "2026-03-10T09:00:00.000Z"
}
}
```
### PIPELINE_STATE_CHANGED
Neuer Pipeline-Snapshot.
```json
{
"type": "PIPELINE_STATE_CHANGED",
"payload": {
"state": "ENCODING",
"activeJobId": 42,
"progress": 62.5,
"eta": "00:12:34",
"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": 2,
"runningJobs": [],
"queuedJobs": []
}
}
}
```
### PIPELINE_PROGRESS
Laufende Fortschrittsupdates.
```json
{
"type": "PIPELINE_PROGRESS",
"payload": {
"state": "ENCODING",
"activeJobId": 42,
"progress": 62.5,
"eta": "00:12:34",
"statusText": "ENCODING 62.50%"
}
}
```
### PIPELINE_QUEUE_CHANGED
Queue-Snapshot aktualisiert.
### DISC_DETECTED / DISC_REMOVED
Disc-Insertion/-Removal.
```json
{
"type": "DISC_DETECTED",
"payload": {
"device": {
"path": "/dev/sr0",
"discLabel": "INCEPTION",
"model": "ASUS BW-16D1HT",
"fstype": "udf",
"mountpoint": null,
"mediaProfile": "bluray"
}
}
}
```
`mediaProfile`: `bluray` | `dvd` | `other` | `null`
### HARDWARE_MONITOR_UPDATE
Snapshot aus Hardware-Monitoring.
```json
{
"type": "HARDWARE_MONITOR_UPDATE",
"payload": {
"enabled": true,
"intervalMs": 5000,
"updatedAt": "2026-03-10T09:00:00.000Z",
"sample": {
"cpu": {},
"memory": {},
"gpu": {},
"storage": {}
},
"error": null
}
}
```
### PIPELINE_ERROR
Fehler bei Disc-Event-Verarbeitung in Pipeline.
### DISK_DETECTION_ERROR
Fehler in Laufwerkserkennung.
### SETTINGS_UPDATED
Einzelnes Setting wurde gespeichert.
### SETTINGS_BULK_UPDATED
Bulk-Settings gespeichert.
```json
{
"type": "SETTINGS_BULK_UPDATED",
"payload": {
"count": 3,
"keys": ["raw_dir", "movie_dir", "handbrake_preset_bluray"]
}
}
```
### SETTINGS_SCRIPTS_UPDATED
Skript geändert (`created|updated|deleted|reordered`).
### SETTINGS_SCRIPT_CHAINS_UPDATED
Skript-Kette geändert (`created|updated|deleted|reordered`).
### USER_PRESETS_UPDATED
User-Preset geändert (`created|updated|deleted`).
### CRON_JOBS_UPDATED
Cron-Config geändert (`created|updated|deleted`).
### CRON_JOB_UPDATED
Laufzeitstatus eines Cron-Jobs geändert.
```json
{
"type": "CRON_JOB_UPDATED",
"payload": {
"id": 1,
"lastRunStatus": "running",
"lastRunAt": "2026-03-10T10:00:00.000Z",
"nextRunAt": null
}
}
```
### RUNTIME_ACTIVITY_CHANGED
Vollständiger Snapshot aller laufenden und kürzlich abgeschlossenen Aktivitäten.
Wird ausgelöst, wenn eine Aktivität gestartet, aktualisiert oder abgeschlossen wird sowie nach `clear-recent`.
```json
{
"type": "RUNTIME_ACTIVITY_CHANGED",
"payload": {
"active": [
{
"id": 7,
"type": "chain",
"name": "Post-Encode Aufräumen",
"status": "running",
"source": "cron",
"message": "Schritt 2 von 3",
"currentStep": "cleanup.sh",
"currentStepType": "script",
"stepIndex": 2,
"stepTotal": 3,
"canCancel": true,
"canNextStep": false,
"outcome": "running",
"startedAt": "2026-03-10T10:00:00.000Z",
"finishedAt": null,
"durationMs": null
}
],
"recent": [],
"updatedAt": "2026-03-10T10:00:05.000Z"
}
}
```
Vollständige Feldbeschreibung: [Runtime Activities API](runtime-activities.md).
### CONVERTER_SCAN_UPDATE
Wird nach einem Scan des Converter-Eingangsordners gesendet (manuell oder per Polling).
```json
{
"type": "CONVERTER_SCAN_UPDATE",
"payload": {
"entryCount": 12
}
}
```
### DOWNLOADS_UPDATED
Wird bei jeder Statusänderung in der Download-Queue gesendet.
```json
{
"type": "DOWNLOADS_UPDATED",
"payload": {
"reason": "ready",
"item": {
"id": "dl-1",
"jobId": 42,
"status": "ready",
"archiveName": "Job_42_movie.zip",
"createdAt": "2026-03-30T10:00:00.000Z"
},
"summary": {
"total": 3,
"pending": 1,
"processing": 0,
"ready": 1,
"failed": 1
}
}
}
```
Mögliche `reason`-Werte: `queued`, `processing`, `ready`, `failed`, `deleted`.
---
## Reconnect-Verhalten
`useWebSocket` verbindet bei Abbruch automatisch neu:
- Retry-Intervall: `1500ms`
- Wiederverbindung bis Komponente unmounted wird
+30
View File
@@ -0,0 +1,30 @@
# Technischer Anhang
Dieser Bereich enthält die technische Referenz hinter dem Benutzerhandbuch.
## Inhalt
- **Konfiguration**
- komplette Feldreferenz
- Umgebungsvariablen
- **Pipeline intern**
- Zustandsmodell
- Encode-Planung
- Playlist-Analyse
- Pre-/Post-Encode-Ausführungen
- **API-Referenz**
- REST-Endpunkte
- WebSocket-Events
- **Architektur**
- Backend-/Frontend-Aufbau
- Datenbank
- **Deployment**
- Betrieb in Entwicklung und Produktion
- **Externe Tools**
- MakeMKV, HandBrake, MediaInfo
## Wann du in den Anhang wechselst
- du integrierst Ripster mit anderen Systemen
- du betreibst mehrere Instanzen oder willst tiefer debuggen
- du brauchst Feld-/API-/Schema-Details für Automatisierung
+241
View File
@@ -0,0 +1,241 @@
# Backend-Services
Das Backend ist in Services aufgeteilt, die von Express-Routen orchestriert werden. Seit v0.12.0 erfolgt die Medienverarbeitung über ein **Plugin-System**.
---
## Plugin-System (`src/plugins/`)
Ab v0.12.0 ist die Pipeline modular aufgebaut. Jeder Medientyp wird von einem eigenen Plugin verwaltet.
### Basisklassen
- **`PluginBase.js`** — Abstrakte Basisklasse `SourcePlugin` mit Lifecycle-Methoden
- **`PluginRegistry.js`** — Dynamische Plugin-Erkennung, prioritätsbasierte Auswahl, Settings-Schema-Aggregation
- **`PluginContext.js`** — Ausführungskontext (Logger, Callbacks, Signals) für Plugin-Aufrufe
### Plugin-Lifecycle
Jedes Plugin implementiert die folgenden Methoden:
| Methode | Beschreibung |
|---|---|
| `detect(discInfo)` | Medientyp-Erkennung |
| `analyze(devicePath, job, ctx)` | Analyse / Metadaten-Extraktion |
| `rip(job, ctx)` | Rohdaten-Extraktion |
| `review(job, ctx)` | Optionale Vorschau-Vorbereitung |
| `encode(job, ctx)` | Finales Encoding/Konvertierung |
| `finalize(job, ctx)` | Nachbearbeitung |
| `onCancel(job, ctx)` | Bereinigung bei Abbruch |
| `onRetry(job, ctx)` | Zustand-Reset vor Retry |
### Implementierte Plugins
- **`BluRayPlugin.js`** — Blu-ray via MakeMKV (Backup-Modus)
- **`DVDPlugin.js`** — DVD via MakeMKV (MKV-Modus)
- **`CdPlugin.js`** — Audio-CD via cdparanoia + FFmpeg (experimentell)
- **`AudiobookPlugin.js`** — AAX/M4B via FFmpeg mit Kapitel-Splitting
- **`ConverterPlugin.js`** — Generische Audio/Video-Konvertierung via FFmpeg
- **`VideoDiscPlugin.js`** — Gemeinsame Basis für Blu-ray/DVD (MediaInfo-Parsing)
---
## `pipelineService.js`
Zentrale Workflow-Orchestrierung.
Aufgaben:
- Pipeline-State-Machine + Persistenz (`pipeline_state`)
- Disc-Analyse/Rip/Review/Encode via Plugin-Delegation
- Queue-Management (Jobs + `script|chain|wait` Einträge)
- Retry/Re-Encode/Restart-Flows
- WebSocket-Broadcasts für State/Progress/Queue
- Converter-Job-Verwaltung (`createConverterJobFromEntry`, `uploadConverterFiles`, `startConverterJob`)
Wichtige Methoden:
- `analyzeDisc()`
- `selectMetadata()`
- `startPreparedJob()`
- `confirmEncodeReview()`
- `cancel()`
- `retry()`
- `reencodeFromRaw()`
- `restartReviewFromRaw()`
- `restartEncodeWithLastSettings()`
- `resumeReadyToEncodeJob()`
- `enqueueNonJobEntry()`, `reorderQueue()`, `removeQueueEntry()`
- `createConverterJobFromEntry()`, `createConverterJobsFromSelection()`
- `uploadConverterFiles()`, `startConverterJob()`
---
## `diskDetectionService.js`
Pollt Laufwerk(e) und emittiert:
- `discInserted`
- `discRemoved`
- `error`
Zusatz:
- Modus `auto` oder `explicit`
- heuristische `mediaProfile`-Erkennung (`bluray`/`dvd`/`other`)
- `rescanAndEmit()` für manuellen Trigger
---
## `settingsService.js`
Settings-Layer mit Validation/Serialisierung.
Features:
- `getCategorizedSettings()` für UI-Form
- `setSettingValue()` / `setSettingsBulk()`
- profilspezifische Auflösung (`resolveEffectiveToolSettings`)
- CLI-Config-Building für MakeMKV/HandBrake/MediaInfo/FFmpeg
- HandBrake-Preset-Liste via `HandBrakeCLI -z`
- MakeMKV-Registration-Command aus `makemkv_registration_key`
- Pfadauflösung für alle Medienprofile inkl. Audiobook und Converter
---
## `historyService.js`
Historie + Dateioperationen.
Features:
- Job-Liste/Detail inkl. Log-Tail
- Orphan-RAW-Erkennung und Import
- OMDb-Nachzuweisung
- Dateilöschung (`raw|movie|both`)
- Job-Löschung (`none|raw|movie|both`)
---
## `audiobookService.js`
Audiobook-Verarbeitung (AAX/M4B).
Features:
- FFprobe-basierte Analyse (Kapitel, Metadaten)
- FFmpeg-basiertes Encoding mit Kapitel-Splitting
- Ausgabeformate: M4B, MP3, FLAC
- Template-basierte Pfade (`{author}`, `{title}`, `{year}`, `{narrator}`, `{series}`, `{part}`)
---
## `activationBytesService.js`
Verwaltung von Audible-Activation-Bytes für AAX-DRM-Handling.
Features:
- SHA-1-Prüfsumme der AAX-Datei berechnen
- Activation Bytes in `aax_activation_bytes`-Tabelle cachen
- Lookup via `audnexService` (wenn konfiguriert)
---
## `converterScanService.js`
Dateisystem-Überwachung des Converter-Eingangsordners.
Features:
- Vollständiger FS-Baum (`getTree()`)
- DB-basierter Datei-Explorer (`getEntries()`)
- Manueller und automatischer Scan (Polling)
- Pfad-Normalisierung mit Traversal-Schutz
- WebSocket-Broadcast: `CONVERTER_SCAN_UPDATE`
---
## `downloadService.js`
Download-Queue für Ausgabedateien aus der Historie.
Features:
- Job in Download-Queue einreihen (`enqueueHistoryJob`)
- ZIP-Archiv aus Ausgabedateien erstellen (`archiver`)
- Datei-Streaming via `res.download()`
- WebSocket-Broadcast: `DOWNLOADS_UPDATED` bei Status-Änderungen
- Download-Item löschen
---
## `cronService.js`
Integriertes Cron-System ohne externe Parser-Library.
Features:
- 5-Feld-Cron-Parser + `nextRun`-Berechnung
- Quellen: `script` oder `chain`
- Laufzeitlogs (`cron_run_logs`)
- manuelles Triggern
- WebSocket-Events: `CRON_JOBS_UPDATED`, `CRON_JOB_UPDATED`
---
## `runtimeActivityService.js`
In-Memory-Tracking aller laufenden und kürzlich abgeschlossenen Aktivitäten (Skripte, Ketten, Cron-Jobs, Tasks).
Features:
- `startActivity(type, payload)` → Aktivität registrieren, ID zurückgeben
- `updateActivity(id, patch)` → Laufende Aktivität aktualisieren
- `completeActivity(id, payload)` → Aktivität abschließen und in `recent` verschieben
- `setControls(id, { cancel, nextStep })` → Steuer-Handler registrieren (für `canCancel`/`canNextStep`)
- `requestCancel(id)` / `requestNextStep(id)` → Steuer-Handler aufrufen
- `clearRecent()` → Abgeschlossene Aktivitäten löschen
- `getSnapshot()` → Snapshot mit `active` + `recent` + `updatedAt`
- Broadcasts `RUNTIME_ACTIVITY_CHANGED` über WebSocket bei jeder Änderung
Limits:
- `recent` max. 120 Einträge
- `stdout`/`stderr`/`output` max. 12.000 Zeichen
- `message`/`errorMessage` max. 2.000 Zeichen
Vollständige API-Dokumentation: [Runtime Activities API](../api/runtime-activities.md)
---
## Weitere Services
- `scriptService.js` (CRUD + Test + Wrapper-Ausführung)
- `scriptChainService.js` (CRUD + Step-Execution)
- `userPresetService.js` (HandBrake User-Presets)
- `hardwareMonitorService.js` (CPU/RAM/GPU/Storage)
- `websocketService.js` (Client-Registry + Broadcast)
- `notificationService.js` (PushOver)
- `cdRipService.js` (CD-Ripping mit cdparanoia)
- `musicBrainzService.js` (MusicBrainz-Metadaten-Lookup)
- `audnexService.js` (Audnex-API für Audiobook-Metadaten)
- `coverArtRecoveryService.js` (Cover-Art-Wiederherstellung)
- `thumbnailService.js` (Vorschaubild-Generierung)
- `omdbService.js` (OMDb-Metadaten-Suche)
- `logger.js` (rotierende Datei-Logs)
---
## Bootstrapping (`src/index.js`)
Beim Start:
1. DB init/migrate
2. Pipeline-Init (inkl. Plugin-Registry)
3. Cron-Init
4. Express-Routes + Error-Handler
5. WebSocket-Server auf `/ws`
6. Hardware-Monitoring-Init
7. Disk-Detection-Start
8. Converter-Scan-Service-Init (Polling falls aktiviert)
+186
View File
@@ -0,0 +1,186 @@
# Datenbank
Ripster verwendet SQLite (`backend/data/ripster.db`).
---
## Tabellen
```text
settings_schema
settings_values
jobs
job_lineage_artifacts
job_output_folders
pipeline_state
scripts
script_chains
script_chain_steps
user_presets
cron_jobs
cron_run_logs
aax_activation_bytes
user_prefs
converter_scan_entries
```
---
## `jobs`
Speichert Pipeline-Lifecycle und Artefakte pro Job.
Zentrale Felder:
- Metadaten: `title`, `year`, `imdb_id`, `poster_url`, `omdb_json`, `selected_from_omdb`
- Laufzeit: `start_time`, `end_time`, `status`, `last_state`
- Pfade: `raw_path`, `output_path`, `encode_input_path`
- Tool-Ausgaben: `makemkv_info_json`, `handbrake_info_json`, `mediainfo_info_json`, `encode_plan_json`
- Kontrolle: `encode_review_confirmed`, `rip_successful`, `error_message`
- Medientyp: `media_type` (`bluray|dvd|cd|audiobook|converter`)
- Prüfsumme: `aax_checksum` (für AAX-Dateien)
- Audit: `created_at`, `updated_at`
---
## `job_lineage_artifacts`
Verknüpft Jobs mit ihren Quell-Jobs (z. B. Re-Encode aus einem vorherigen Rip).
Felder:
- `job_id` — der abgeleitete Job
- `source_job_id` — der Quell-Job
- `artifact_type` — Art der Verknüpfung (z. B. `reencode`, `restart_review`)
- `created_at`
---
## `job_output_folders`
Verfolgt Ausgabepfade pro Job (mehrere Ausgaben möglich, z. B. bei Kapitel-Splitting).
Felder:
- `job_id`
- `folder_path`
- `label` — optionaler Bezeichner
- `created_at`
---
## `pipeline_state`
Singleton-Tabelle (`id = 1`) für aktiven Snapshot:
- `state`
- `active_job_id`
- `progress`
- `eta`
- `status_text`
- `context_json`
- `updated_at`
---
## `settings_schema` + `settings_values`
- `settings_schema`: Definition (Typ, Default, Validation, Reihenfolge)
- `settings_values`: aktueller Wert pro Key
---
## `scripts`, `script_chains`, `script_chain_steps`
- `scripts`: Shell-Skripte (`name`, `script_body`, `order_index`)
- `script_chains`: Ketten (`name`, `order_index`)
- `script_chain_steps`: Schritte je Kette
- `step_type`: `script` oder `wait`
- `script_id` oder `wait_seconds`
---
## `user_presets`
Benannte HandBrake-Preset-Sets:
- `name`
- `media_type` (`bluray|dvd|other|all`)
- `handbrake_preset`
- `extra_args`
- `description`
---
## `cron_jobs` + `cron_run_logs`
- `cron_jobs`: Zeitplan + Status
- `cron_run_logs`: einzelne Läufe
- `status`: `running|success|error`
- `output`
- `error_message`
---
## `aax_activation_bytes`
Cache für Audible-Activation-Bytes (AAX-DRM).
Felder:
- `checksum` — SHA-1-Prüfsumme der AAX-Datei (Primärschlüssel)
- `activation_bytes` — Hex-String der Activation Bytes
- `created_at`
---
## `user_prefs`
Benutzer-spezifische Einstellungen (Key-Value).
Felder:
- `key`
- `value`
- `updated_at`
---
## `converter_scan_entries`
DB-seitige Erfassung gescannter Dateien im Converter-Eingangsordner.
Felder:
- `rel_path` — relativer Pfad zum `converter_raw_dir`
- `is_dir` — Verzeichnis-Flag
- `size` — Dateigröße in Bytes
- `modified_at` — Datei-Änderungsdatum
- `job_id` — zugewiesener Converter-Job (falls vorhanden)
- `scanned_at`
---
## Migration/Recovery
Beim Start werden Schema und Settings-Metadaten automatisch abgeglichen.
Bei korruptem SQLite-File:
1. Datei wird nach `backend/data/corrupt-backups/` verschoben
2. neue DB wird initialisiert
3. Schema wird neu aufgebaut
---
## Direkte Inspektion
```bash
sqlite3 backend/data/ripster.db
.mode table
SELECT id, status, title, media_type, created_at FROM jobs ORDER BY created_at DESC;
SELECT key, value FROM settings_values ORDER BY key;
SELECT checksum, activation_bytes FROM aax_activation_bytes;
SELECT rel_path, job_id FROM converter_scan_entries;
```
+117
View File
@@ -0,0 +1,117 @@
# Frontend-Komponenten
Frontend: React + PrimeReact + Vite.
---
## Hauptseiten
### `RipperPage.jsx`
Pipeline-Steuerung:
- Status/Progress/ETA
- Metadaten-Dialog
- Playlist-Entscheidung
- Review-Panel (Track-Auswahl)
- Queue-Interaktion (reorder/add/remove)
- Job-Aktionen (Start/Cancel/Retry/Re-Encode)
- Hardware-Monitoring-Anzeige
- Aktivitäts-Tracking (Skripte, Ketten, Cron)
### `SettingsPage.jsx`
Konfiguration:
- dynamisches Settings-Formular (`DynamicSettingsForm`)
- Skripte/Ketten inkl. Reorder/Test
- User-Presets
- Cron-Jobs (`CronJobsTab`)
### `HistoryPage.jsx`
Historie:
- Job-Liste/Filter
- Job-Details + Logs
- OMDb-Nachzuweisung
- Re-Encode/Restart-Workflows
### `ConverterPage.jsx`
Datei-Converter:
- Datei-Explorer des Converter-Eingangsordners (Baum-Ansicht)
- Upload von Audio/Video-Dateien (bis zu 50 Dateien gleichzeitig)
- Dateiverwaltung: Umbenennen, Verschieben, Löschen, Ordner erstellen
- Jobs aus Dateiauswahl erstellen
- Converter-Job-Konfiguration (Ausgabeformat, Preset, Tracks)
- Job-Start, Abbruch, Löschung
- Automatischer Scan-Status
### `DownloadsPage.jsx`
Download-Queue:
- Ausgabedateien aus der Job-Historie in die Queue einreihen
- Download-Status verfolgen (ausstehend, wird verarbeitet, bereit, fehlgeschlagen)
- Dateien als ZIP herunterladen
- Download-Einträge löschen
### `DatabasePage.jsx`
Expert-Modus:
- Tabellarische Rohsicht der Job-Datenbank
- Orphan-RAW-Import
---
## Wichtige Komponenten
- `PipelineStatusCard.jsx` — Pipeline-Status-Anzeige mit Progress
- `MetadataSelectionDialog.jsx` — OMDb-Metadaten-Auswahl
- `MediaInfoReviewPanel.jsx` — Track-Auswahl-Interface (Video/Audio/Untertitel)
- `JobDetailDialog.jsx` — Job-Detailansicht mit Logs
- `CronJobsTab.jsx` — Cron-Job-Verwaltung
- `DynamicSettingsForm.jsx` — Schema-gesteuertes Einstellungsformular
- `ConverterJobCard.jsx` — Converter-Job-Darstellung
- `CdRipConfigPanel.jsx` — Konfiguration für Audio-CD-Ripping
---
## API-Client (`api/client.js`)
- zentraler `request()` mit JSON-Handling
- Fehlerobjekt aus API wird auf `Error(message)` gemappt
- `VITE_API_BASE` default `/api`
---
## WebSocket (`hooks/useWebSocket.js`)
- URL: `VITE_WS_URL` oder automatisch `ws(s)://<host>/ws`
- Auto-Reconnect mit 1500ms Intervall
In `App.jsx` werden u. a. verarbeitet:
- `PIPELINE_STATE_CHANGED`
- `PIPELINE_PROGRESS`
- `PIPELINE_QUEUE_CHANGED`
- Disk erkannt / Disk entfernt
- `HARDWARE_MONITOR_UPDATE`
- `DOWNLOADS_UPDATED`
- `CONVERTER_SCAN_UPDATE`
- `RUNTIME_ACTIVITY_CHANGED`
---
## Build/Run
```bash
# dev
npm run dev --prefix frontend
# prod build
npm run build --prefix frontend
```
+52
View File
@@ -0,0 +1,52 @@
# Anhang: Architektur
Ripster ist eine Client-Server-Anwendung mit REST + WebSocket und externen CLI-Tools.
---
## Systemüberblick
```mermaid
graph TB
subgraph Browser["Browser (React)"]
Ripper[Ripper]
Settings[Einstellungen]
History[Historie]
end
subgraph Backend["Node.js Backend"]
API[REST API\nExpress]
WS[WebSocket\n/ws]
Pipeline[pipelineService]
Cron[cronService]
DB[(SQLite)]
end
subgraph Tools["Externe Tools"]
MakeMKV[makemkvcon]
HandBrake[HandBrakeCLI]
MediaInfo[mediainfo]
end
Browser <-->|HTTP| API
Browser <-->|WebSocket| WS
Pipeline --> MakeMKV
Pipeline --> HandBrake
Pipeline --> MediaInfo
API --> DB
Pipeline --> DB
Cron --> DB
```
---
## Details
<div class="grid cards" markdown>
- [:octicons-arrow-right-24: Übersicht](overview.md)
- [:octicons-arrow-right-24: Backend-Services](backend.md)
- [:octicons-arrow-right-24: Frontend-Komponenten](frontend.md)
- [:octicons-arrow-right-24: Datenbank](database.md)
</div>
+94
View File
@@ -0,0 +1,94 @@
# Architektur-Übersicht
---
## Kernprinzipien
### Event-getriebene Pipeline
`pipelineService` hält einen Snapshot der State-Machine und broadcastet Änderungen sofort via WebSocket.
```text
State-Änderung -> PIPELINE_STATE_CHANGED/PIPELINE_PROGRESS -> Frontend-Update
```
### Service-Layer
```text
Route -> Service -> DB/Tool-Execution
```
Routes enthalten kaum Business-Logik.
### Schema-getriebene Settings
Settings sind DB-schema-getrieben (`settings_schema` + `settings_values`), UI rendert dynamisch aus diesen Daten.
---
## Echtzeit-Kommunikation
WebSocket läuft auf `/ws`.
Wichtige Events:
- `PIPELINE_STATE_CHANGED`, `PIPELINE_PROGRESS`, `PIPELINE_QUEUE_CHANGED`
- Disk erkannt, Disk entfernt
- `HARDWARE_MONITOR_UPDATE`
- `SETTINGS_UPDATED`, `SETTINGS_BULK_UPDATED`
- `SETTINGS_SCRIPTS_UPDATED`, `SETTINGS_SCRIPT_CHAINS_UPDATED`, `USER_PRESETS_UPDATED`
- `CRON_JOBS_UPDATED`, `CRON_JOB_UPDATED`
- `PIPELINE_ERROR`, `DISK_DETECTION_ERROR`
---
## Prozessausführung
Externe Tools werden als Child-Processes gestartet (`processRunner`):
- Streaming von stdout/stderr
- Progress-Parsing (`progressParsers.js`)
- kontrollierter Abbruch (SIGINT/SIGKILL-Fallback)
---
## Persistenz
SQLite-Datei: `backend/data/ripster.db`
Kern-Tabellen:
- `jobs`, `pipeline_state`
- `settings_schema`, `settings_values`
- `scripts`, `script_chains`, `script_chain_steps`
- `user_presets`
- `cron_jobs`, `cron_run_logs`
Beim Start werden Schema und Settings-Migrationen automatisch ausgeführt.
---
## Fehlerbehandlung
Zentrales Error-Handling liefert:
```json
{
"error": {
"message": "...",
"statusCode": 400,
"reqId": "...",
"details": []
}
}
```
Fehlgeschlagene Jobs bleiben in der Historie (`Fehler` oder `Abgebrochen`) und können erneut gestartet werden.
---
## CORS & Runtime-Konfig
- `CORS_ORIGIN` default: `*`
- `LOG_LEVEL` default: `info`
- DB-/Log-Pfade über `DB_PATH`/`LOG_DIR` konfigurierbar
+67
View File
@@ -0,0 +1,67 @@
# Umgebungsvariablen
Umgebungsvariablen steuern Backend/Vite außerhalb der DB-basierten UI-Settings.
---
## Backend (`backend/.env`)
| Variable | Default (Code) | Beschreibung |
|---------|------------------|-------------|
| `PORT` | `3001` | Express-Port |
| `DB_PATH` | `backend/data/ripster.db` | SQLite-Datei (relativ zu `backend/`) |
| `LOG_DIR` | `backend/logs` | Fallback-Logverzeichnis (wenn `log_dir`-Setting nicht gesetzt/lesbar) |
| `CORS_ORIGIN` | `*` | CORS-Origin für API |
| `LOG_LEVEL` | `info` | `debug`, `info`, `warn`, `error` |
Beispiel:
```env
PORT=3001
DB_PATH=/var/lib/ripster/ripster.db
LOG_DIR=/var/log/ripster
CORS_ORIGIN=http://192.168.1.50:5173
LOG_LEVEL=info
```
Hinweis: `backend/.env.example` enthält bewusst dev-freundliche Werte (z. B. lokaler `CORS_ORIGIN`).
---
## Frontend (`frontend/.env`)
| Variable | Default | Beschreibung |
|---------|---------|-------------|
| `VITE_API_BASE` | `/api` | API-Basis für Fetch-Client |
| `VITE_WS_URL` | automatisch aus `window.location` + `/ws` | Optional explizite WebSocket-URL |
| `VITE_PUBLIC_ORIGIN` | leer | Öffentliche Vite-Origin (Remote-Dev) |
| `VITE_ALLOWED_HOSTS` | `true` | Komma-separierte Hostliste für Vite `allowedHosts` |
| `VITE_HMR_PROTOCOL` | abgeleitet aus `VITE_PUBLIC_ORIGIN` | HMR-Protokoll (`ws`/`wss`) |
| `VITE_HMR_HOST` | abgeleitet aus `VITE_PUBLIC_ORIGIN` | HMR-Host |
| `VITE_HMR_CLIENT_PORT` | abgeleitet aus `VITE_PUBLIC_ORIGIN` | HMR-Client-Port |
Beispiele:
```env
# lokal (mit Vite-Proxy)
VITE_API_BASE=/api
```
```env
# remote dev
VITE_API_BASE=http://192.168.1.50:3001/api
VITE_WS_URL=ws://192.168.1.50:3001/ws
VITE_PUBLIC_ORIGIN=http://192.168.1.50:5173
VITE_ALLOWED_HOSTS=192.168.1.50,ripster.local
VITE_HMR_PROTOCOL=ws
VITE_HMR_HOST=192.168.1.50
VITE_HMR_CLIENT_PORT=5173
```
---
## Priorität
1. Prozess-Umgebungsvariablen
2. `.env`
3. Code-Defaults
+29
View File
@@ -0,0 +1,29 @@
# Anhang: Konfiguration
Dieser Abschnitt ist die technische Referenz zu allen Konfigurationsarten in Ripster.
## Inhalte
<div class="grid cards" markdown>
- :material-tune: **Einstellungsreferenz**
---
Vollständige Liste aller UI-Settings (Typ, Default, Hinweise).
[:octicons-arrow-right-24: Einstellungsreferenz](settings-reference.md)
- :material-variable: **Umgebungsvariablen**
---
`backend/.env` und `frontend/.env` inkl. Prioritäten.
[:octicons-arrow-right-24: Umgebungsvariablen](environment.md)
</div>
## Zurück zum Handbuch
- [Benutzerhandbuch Überblick](../getting-started/index.md)
+181
View File
@@ -0,0 +1,181 @@
# Einstellungsreferenz
Diese Seite listet die Felder so, wie sie in der GUI unter `Settings` angezeigt werden.
Hinweis: Interne Schlüsselnamen werden hier bewusst nicht verwendet. Falls du sie für Integrationen brauchst, nutze die API-Dokumentation.
---
## Profil-System
Viele Felder sind pro Medientyp getrennt vorhanden:
- Blu-ray
- DVD
- Sonstiges
- Audiobook (neu ab v0.12.0)
---
## Template-Platzhalter
### Video-Templates
- `${title}` — Titel
- `${year}` — Erscheinungsjahr
- `${imdbId}` — IMDb-ID
### Audiobook-Templates
- `{author}` — Autor
- `{title}` — Titel
- `{year}` — Erscheinungsjahr
- `{narrator}` — Sprecher
- `{series}` — Buchreihe
- `{part}` — Teil-Nummer
- `{format}` — Ausgabeformat
### Converter-Templates (Video)
- `{title}`, `{year}`
### Converter-Templates (Audio)
- `{artist}`, `{title}`, `{album}`, `{year}`, `{trackNr}`
Nicht gesetzte Werte werden zu `unknown`.
---
## Kategorie: Pfade
| Feldname in der GUI | Typ | Default |
|---|---|---|
| `Raw Ausgabeordner` | path | `data/output/raw` |
| `Raw Ausgabeordner (Blu-ray)` | path | `null` |
| `Raw Ausgabeordner (DVD)` | path | `null` |
| `Raw Ausgabeordner (Sonstiges)` | path | `null` |
| `Eigentümer Raw-Ordner (Blu-ray)` | string | `null` |
| `Eigentümer Raw-Ordner (DVD)` | string | `null` |
| `Eigentümer Raw-Ordner (Sonstiges)` | string | `null` |
| `Film Ausgabeordner` | path | `data/output/movies` |
| `Film Ausgabeordner (Blu-ray)` | path | `null` |
| `Film Ausgabeordner (DVD)` | path | `null` |
| `Film Ausgabeordner (Sonstiges)` | path | `null` |
| `Eigentümer Film-Ordner (Blu-ray)` | string | `null` |
| `Eigentümer Film-Ordner (DVD)` | string | `null` |
| `Eigentümer Film-Ordner (Sonstiges)` | string | `null` |
| `Log Ordner` | path | `data/logs` |
| `Audiobook RAW-Ordner` | path | `null` |
| `Eigentümer Audiobook RAW-Ordner` | string | `null` |
| `Audiobook Output-Ordner` | path | `null` |
| `Eigentümer Audiobook Output-Ordner` | string | `null` |
| `Output Template (Audiobook)` | string | `{author}/{author} - {title} ({year})` |
| `Audiobook RAW Template` | string | `{author} - {title} ({year})` |
| `Converter Raw-Ordner` | path | `null` |
| `Converter Ausgabe (Video)` | path | `null` |
| `Converter Ausgabe (Audio)` | path | `null` |
| `Output-Template (Video)` | string | `{title}` |
| `Output-Template (Audio)` | string | `{artist} - {title}` |
---
## Kategorie: Laufwerk
| Feldname in der GUI | Typ | Default | Hinweis |
|---|---|---|---|
| `Laufwerksmodus` | select | `auto` | `Auto Discovery` oder `Explizites Device` |
| `Device Pfad` | path | `/dev/sr0` | relevant bei `Explizites Device` |
| `MakeMKV Source Index` | number | `0` | Disc-Index im Auto-Modus |
| `Disc Auto-Erkennung aktiviert` | boolean | `true` | Automatische Disc-Erkennung per Polling |
| `Polling Intervall (ms)` | number | `4000` | 1000..60000 |
---
## Kategorie: Monitoring
| Feldname in der GUI | Typ | Default |
|---|---|---|
| `Hardware Monitoring aktiviert` | boolean | `true` |
| `Hardware Monitoring Intervall (ms)` | number | `5000` |
---
## Kategorie: Tools (global)
| Feldname in der GUI | Typ | Default |
|---|---|---|
| `MakeMKV Kommando` | string | `makemkvcon` |
| `MakeMKV Key` | string | `null` |
| `Mediainfo Kommando` | string | `mediainfo` |
| `Minimale Titellaenge (Minuten)` | number | `60` |
| `HandBrake Kommando` | string | `HandBrakeCLI` |
| `FFmpeg Kommando` | string | `ffmpeg` |
| `FFprobe Kommando` | string | `ffprobe` |
| `Encode-Neustart: unvollständige Ausgabe löschen` | boolean | `true` |
| `Parallele Jobs` | number | `1` |
### Blu-ray-spezifisch
| Feldname in der GUI | Typ | Default |
|---|---|---|
| `Mediainfo Extra Args` (Blu-ray) | string | `null` |
| `MakeMKV Rip Modus` (Blu-ray) | select | `backup` |
| `MakeMKV Analyze Extra Args` (Blu-ray) | string | `null` |
| `MakeMKV Rip Extra Args` (Blu-ray) | string | `null` |
| `HandBrake Preset` (Blu-ray) | string | `H.264 MKV 1080p30` |
| `HandBrake Extra Args` (Blu-ray) | string | `null` |
| `Ausgabeformat` (Blu-ray) | select | `mkv` |
| `Dateiname Template` (Blu-ray) | string | `${title} (${year})` |
| `Ordnername Template` (Blu-ray) | string | `null` |
### DVD-spezifisch
| Feldname in der GUI | Typ | Default |
|---|---|---|
| `Mediainfo Extra Args` (DVD) | string | `null` |
| `MakeMKV Rip Modus` (DVD) | select | `mkv` |
| `MakeMKV Analyze Extra Args` (DVD) | string | `null` |
| `MakeMKV Rip Extra Args` (DVD) | string | `null` |
| `HandBrake Preset` (DVD) | string | `H.264 MKV 480p30` |
| `HandBrake Extra Args` (DVD) | string | `null` |
| `Ausgabeformat` (DVD) | select | `mkv` |
| `Dateiname Template` (DVD) | string | `${title} (${year})` |
| `Ordnername Template` (DVD) | string | `null` |
---
## Kategorie: Converter
| Feldname in der GUI | Typ | Default | Hinweis |
|---|---|---|---|
| `Erlaubte Datei-Endungen*` | string | `mkv,mp4,m2ts,iso,avi,mov,flac,mp3,wav,m4a,ogg,opus` | In der UI als Checkbox-Liste, ohne Punkt |
| `Auto-Scan (Polling)` | boolean | `false` | Converter-Ordner automatisch scannen |
| `Polling-Intervall (Sekunden)` | number | `300` | 30..86400 |
---
## Kategorie: Metadaten
| Feldname in der GUI | Typ | Default |
|---|---|---|
| `OMDb API Key` | string | `null` |
| `OMDb Typ` | select | `movie` |
---
## Kategorie: Benachrichtigungen (PushOver)
| Feldname in der GUI | Typ | Default |
|---|---|---|
| `PushOver aktiviert` | boolean | `false` |
| `PushOver Token` | string | `null` |
| `PushOver User` | string | `null` |
| `PushOver Device (optional)` | string | `null` |
| `PushOver Titel-Präfix` | string | `Ripster` |
| `PushOver Priority` | number | `0` |
| `PushOver Timeout (ms)` | number | `7000` |
| `Bei Metadaten-Auswahl senden` | boolean | `true` |
| `Bei Rip-Start senden` | boolean | `true` |
| `Bei Encode-Start senden` | boolean | `true` |
| `Bei Erfolg senden` | boolean | `true` |
| `Bei Fehler senden` | boolean | `true` |
| `Bei Abbruch senden` | boolean | `true` |
| `Bei Re-Encode Start senden` | boolean | `true` |
| `Bei Re-Encode Erfolg senden` | boolean | `true` |
+85
View File
@@ -0,0 +1,85 @@
# Entwicklungsumgebung
---
## Voraussetzungen
- Node.js >= 20.19.0
- externe Tools installiert (`makemkvcon`, `HandBrakeCLI`, `mediainfo`)
---
## Schnellstart
```bash
./start.sh
```
Startet:
- Backend (`http://localhost:3001`, mit nodemon)
- Frontend (`http://localhost:5173`, mit Vite HMR)
Stoppen: `Ctrl+C`.
---
## Manuell
### Backend
```bash
cd backend
npm install
npm run dev
```
### Frontend
```bash
cd frontend
npm install
npm run dev
```
---
## Vite-Proxy (Dev)
`frontend/vite.config.js` proxied standardmäßig:
- `/api` -> `http://127.0.0.1:3001`
- `/ws` -> `ws://127.0.0.1:3001`
---
## Remote-Dev (optional)
Beispiel `frontend/.env.local`:
```env
VITE_API_BASE=http://192.168.1.100:3001/api
VITE_WS_URL=ws://192.168.1.100:3001/ws
VITE_PUBLIC_ORIGIN=http://192.168.1.100:5173
VITE_ALLOWED_HOSTS=192.168.1.100,ripster.local
VITE_HMR_PROTOCOL=ws
VITE_HMR_HOST=192.168.1.100
VITE_HMR_CLIENT_PORT=5173
```
---
## Nützliche Kommandos
```bash
# Root dev (backend + frontend)
npm run dev
# einzeln
npm run dev:backend
npm run dev:frontend
# Frontend Build
npm run build:frontend
```
+23
View File
@@ -0,0 +1,23 @@
# Anhang: Deployment
Technische Betriebsdokumentation für Entwicklung und Produktion.
<div class="grid cards" markdown>
- :material-laptop: **Entwicklungsumgebung**
---
Lokale Entwicklung mit Hot-Reload.
[:octicons-arrow-right-24: Entwicklung](development.md)
- :material-server: **Produktion**
---
Installation und Betrieb auf Servern.
[:octicons-arrow-right-24: Produktion](production.md)
</div>
+233
View File
@@ -0,0 +1,233 @@
# Produktions-Deployment
---
## Automatische Installation (empfohlen)
Das mitgelieferte `install.sh` richtet Ripster vollautomatisch ein inklusive Node.js, MakeMKV, HandBrake, nginx und systemd-Dienst.
**Unterstützte Systeme laut Script:** Debian, Ubuntu, Linux Mint, Pop!_OS
**Voraussetzung:** root-Rechte, Internetzugang
### Schnellstart via curl
```bash
curl -fsSL https://raw.githubusercontent.com/Mboehmlaender/ripster/main/install.sh | sudo bash
```
Oder mit wget:
```bash
wget -qO- https://raw.githubusercontent.com/Mboehmlaender/ripster/main/install.sh | sudo bash
```
!!! warning "Optionen nur via Datei"
Beim Pipen von curl/wget können keine Argumente übergeben werden. Für benutzerdefinierte Optionen zuerst herunterladen und dann mit `sudo bash install.sh [Optionen]` ausführen.
### Optionen
| Option | Standard | Beschreibung |
|--------|----------|--------------|
| `--branch <branch>` | `dev` | Git-Branch für die Installation |
| `--dir <pfad>` | `/opt/ripster` | Installationsverzeichnis |
| `--user <benutzer>` | `ripster` | Systembenutzer für den Dienst |
| `--port <port>` | `3001` | Backend-Port |
| `--host <hostname>` | Auto (Maschinen-IP) | Hostname/IP für die Weboberfläche |
| `--no-makemkv` | | MakeMKV-Installation überspringen |
| `--no-handbrake` | | HandBrake-Installation überspringen |
| `--no-nginx` | | nginx-Einrichtung überspringen |
| `--reinstall` | | Bestehende Installation aktualisieren (Daten bleiben erhalten) |
| `-h`, `--help` | | Hilfe anzeigen |
### Beispiele
```bash
# Standard-Installation
sudo bash install.sh
# Anderen Branch und Port verwenden
sudo bash install.sh --branch dev --port 8080
# Ohne MakeMKV (bereits installiert)
sudo bash install.sh --no-makemkv
# Bestehende Installation aktualisieren
sudo bash install.sh --reinstall
# Ohne nginx (eigener Reverse-Proxy)
sudo bash install.sh --no-nginx --host mein-server.local
```
### Was das Skript erledigt
1. **Systemprüfung** OS-Erkennung und Root-Check
2. **Systempakete** `curl`, `wget`, `git`, `mediainfo`, `udev` u. a.
3. **Node.js 20** via NodeSource, falls noch nicht installiert
4. **MakeMKV** aktuelle Version wird aus dem offiziellen Forum ermittelt und aus dem Quellcode kompiliert (kann mit `--no-makemkv` übersprungen werden)
5. **HandBrake** interaktive Auswahl:
- **Option 1**: Standard (`apt install handbrake-cli`)
- **Option 2**: Gebündelte GPU-Version mit NVDEC aus `bin/HandBrakeCLI`
6. **Systembenutzer** `ripster` ohne Login-Shell und ohne Home-Verzeichnis; Gruppen werden (falls vorhanden) ergänzt: `cdrom`, `optical`, `disk`, `video`, `render`
7. **Repository** klont Branch nach `--dir` (bei `--reinstall`: sichert `backend/data`, aktualisiert Repo, stellt Daten wieder her)
8. **Verzeichnisse** stellt u. a. sicher: `backend/data`, `backend/logs`, `backend/data/output/raw`, `backend/data/output/movies`, `backend/data/logs`
9. **npm-Abhängigkeiten** Root, Backend (nur production), Frontend
10. **Frontend-Build** `npm run build` mit relativen API-URLs (nginx-kompatibel)
11. **Backend `.env`** wird automatisch generiert (bei `--reinstall` bleibt bestehende `.env` erhalten)
12. **Berechtigungen** zunächst `ripster:ripster` auf Installationsverzeichnis; bei sudo-Aufruf werden Output-/Log-Ordner auf `<aufrufender user>:ripster` mit `775` gesetzt; `.env` wird auf `600` gesetzt
13. **MakeMKV User-Ordner** erstellt bei sudo-Aufruf `~/.MakeMKV` für den aufrufenden Benutzer
14. **systemd-Dienst** `ripster-backend.service` erstellt, aktiviert und gestartet
15. **nginx** konfiguriert als Reverse-Proxy für Frontend, `/api/` und `/ws` (kann mit `--no-nginx` übersprungen werden)
### Nach der Installation
```bash
# Status prüfen
sudo systemctl status ripster-backend
# Logs verfolgen
sudo journalctl -u ripster-backend -f
# Neustart
sudo systemctl restart ripster-backend
# Aktualisieren
sudo bash /opt/ripster/install.sh --reinstall
```
**Zugriff:** `http://<Maschinen-IP>` (oder der mit `--host` angegebene Hostname)
### HandBrake-Modus (GPU/NVDEC)
Bei nicht-interaktiver Ausführung (Pipe von curl) wird automatisch die Standard-Version gewählt. Für die GPU-Version zuerst herunterladen:
```bash
curl -fsSL https://raw.githubusercontent.com/Mboehmlaender/ripster/main/install.sh -o install.sh
sudo bash install.sh
# → Interaktive Auswahl: Option 2 für NVDEC
```
Das gebündelte Binary liegt unter `bin/HandBrakeCLI` und wird nach `/usr/local/bin/HandBrakeCLI` kopiert.
---
## Manuelle Installation
Die folgenden Abschnitte beschreiben die einzelnen Schritte für manuelle oder angepasste Setups.
### Empfohlene Architektur
```text
Client
-> nginx (Reverse Proxy + statisches Frontend)
-> Backend API/WebSocket (Node.js, Port 3001)
```
Wichtig: Das Backend serviert im aktuellen Stand keine `frontend/dist`-Dateien automatisch.
---
## 1) Frontend builden
```bash
cd frontend
npm install
npm run build
```
Artefakte liegen in `frontend/dist/`.
---
## 2) Backend als systemd-Service
Beispiel `/etc/systemd/system/ripster-backend.service`:
```ini
[Unit]
Description=Ripster Backend
After=network.target
[Service]
Type=simple
User=ripster
WorkingDirectory=/opt/ripster/backend
ExecStart=/usr/bin/env node src/index.js
Restart=on-failure
RestartSec=5
Environment=NODE_ENV=production
Environment=PORT=3001
Environment=LOG_LEVEL=info
[Install]
WantedBy=multi-user.target
```
Aktivieren:
```bash
sudo systemctl daemon-reload
sudo systemctl enable --now ripster-backend
sudo systemctl status ripster-backend
```
---
## 3) nginx konfigurieren
Beispiel `/etc/nginx/sites-available/ripster`:
```nginx
server {
listen 80;
server_name ripster.local;
client_max_body_size 8G;
root /opt/ripster/frontend/dist;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://127.0.0.1:3001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /ws {
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}
```
Aktivieren:
```bash
sudo ln -s /etc/nginx/sites-available/ripster /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
```
Hinweis: Fuer groessere Uploads wie `.aax`-Audiobooks muss `client_max_body_size` ausreichend hoch gesetzt sein. Im mitgelieferten Beispiel sind `8G` hinterlegt.
---
## Datenbank-Backup
```bash
sqlite3 /opt/ripster/backend/data/ripster.db \
".backup '/var/backups/ripster-$(date +%Y%m%d).db'"
```
---
## Sicherheit
- Ripster hat keine eingebaute Authentifizierung.
- Für externen Zugriff mindestens Basic Auth + TLS + Netzwerksegmentierung/VPN einsetzen.
- Secrets nicht ins Repo committen (`.env`, Settings-Felder).
+71
View File
@@ -0,0 +1,71 @@
# Ersteinrichtung
Nach der Installation erfolgt die tägliche Konfiguration fast vollständig in der GUI unter `Settings`.
## Ziel
Vor dem ersten echten Job müssen Pfade, Tools und Metadatenzugriff sauber gesetzt sein.
## Reihenfolge (empfohlen)
### 1. `Settings` -> Tab `Konfiguration`
Setze zuerst diese Pflichtwerte:
| Bereich | Wichtige Felder |
|---|---|
| Pfade | `Raw Ausgabeordner`, `Film Ausgabeordner`, `Log Ordner` |
| Tools | `MakeMKV Kommando`, `HandBrake Kommando`, `Mediainfo Kommando` |
| Metadaten | `OMDb API Key`, optional `OMDb Typ` |
Danach `Änderungen speichern`.
### 2. Medienprofile prüfen
Wenn du Blu-ray und DVD unterschiedlich behandeln willst, pflege die profilbezogenen Felder:
- `*_bluray`
- `*_dvd`
- optional `*_other`
Typische Beispiele:
- `HandBrake Preset` (Blu-ray und DVD)
- `Raw Ausgabeordner` (Blu-ray und DVD)
- `Dateiname Template` (Blu-ray und DVD)
### 3. Queue und Monitoring festlegen
- `Parallele Jobs` für den gleichzeitigen Durchsatz
- `Hardware Monitoring aktiviert` + `Hardware Monitoring Intervall (ms)` für Live-Metriken im Ripper
### 4. Optional: Push-Benachrichtigungen
In den Benachrichtigungsfeldern setzen:
- `PushOver aktiviert`
- `PushOver Token`
- `PushOver User`
Dann über `PushOver Test` direkt prüfen.
## 2-Minuten-Funktionstest
1. `Ripper` öffnen
2. Disc einlegen
3. `Analyse starten`
4. Metadaten übernehmen
5. Bis `Bereit zum Encodieren` laufen lassen
Wenn diese Schritte funktionieren, ist die Grundkonfiguration korrekt.
## Wenn Werte nicht gespeichert werden
- Feld mit Fehler markieren lassen (rote Validierung im Formular)
- Pfadangaben und numerische Werte prüfen
- bei Tool-Pfaden direkt CLI-Aufruf im Terminal testen
## Weiter
- [Erster Lauf](quickstart.md)
- [GUI-Seiten im Detail](../gui/index.md)
+33
View File
@@ -0,0 +1,33 @@
# Benutzerhandbuch Überblick
Dieses Kapitel ist für den **Betrieb von Ripster im Alltag** geschrieben.
## Zielgruppe
- Anwender, die Discs verarbeiten wollen
- Betreiber, die den täglichen Ablauf stabil fahren möchten
- Power-User, die Queue/Skripte/Cron im UI steuern möchten
## Kapitelstruktur
| Kapitel | Zweck |
|---|---|
| [Voraussetzungen](prerequisites.md) | Produktions- vs. Dev-Voraussetzungen klar trennen |
| [Installation](installation.md) | Ripster aufsetzen und starten |
| [Ersteinrichtung](configuration.md) | Pfade, Tools und Metadaten korrekt setzen |
| [Erster Lauf](quickstart.md) | Ein kompletter Job von Disc bis Datei |
| [GUI-Seiten](../gui/index.md) | Alle Ansichten und Aktionen im Detail |
| [Workflows](../workflows/index.md) | Typische Abläufe und Entscheidungen aus User-Sicht |
## Wenn du neu startest
1. [Voraussetzungen](prerequisites.md)
2. [Installation](installation.md)
3. [Ersteinrichtung](configuration.md)
4. [Erster Lauf](quickstart.md)
## Wenn Ripster bereits läuft
1. [GUI-Seiten](../gui/index.md)
2. [Workflows](../workflows/index.md)
3. Bei Detailfragen: [Technischer Anhang](../appendix/index.md)
+109
View File
@@ -0,0 +1,109 @@
# Installation
Die empfohlene Installation läuft über `install.sh` und richtet Ripster vollständig ein.
Du musst dafür **keine Tools manuell vorinstallieren**. Das Skript installiert die benötigten Abhängigkeiten automatisch, sofern sie nicht explizit mit `--no-*` übersprungen werden.
## Unterstützte Systeme und Anforderungen
- unterstützt laut Script: `debian`, `ubuntu`, `linuxmint`, `pop`
- Ausführung als `root` (oder via `sudo`)
- Internetzugang während der Installation
## Schritt-für-Schritt
### 1. Installationsskript herunterladen
```bash
wget -qO install.sh https://raw.githubusercontent.com/Mboehmlaender/ripster/main/install.sh
```
### 2. Installation ausführen
```bash
sudo bash install.sh
```
Während der Installation wählst du den HandBrake-Modus:
- `1` Standard (Paketinstallation)
- `2` GPU/NVDEC (gebündeltes Binary)
### 3. Dienststatus prüfen
```bash
sudo systemctl status ripster-backend
```
### 4. Weboberfläche öffnen
- mit nginx (Standard): `http://<Server-IP>`
- ohne nginx (`--no-nginx`): API auf `http://<Server-IP>:3001/api`
## Was `install.sh` konkret einrichtet
1. prüft Betriebssystem, Root-Rechte und ermittelt Host/IP
2. aktualisiert Paketquellen und installiert Basispakete (`curl`, `wget`, `git`, `mediainfo`, `udev` ...)
3. installiert Node.js 20 (falls nicht passend vorhanden)
4. installiert optional MakeMKV (Build aus den offiziellen Quellen)
5. installiert optional HandBrakeCLI (Standard oder GPU/NVDEC)
6. installiert optional nginx
7. legt den Systembenutzer `ripster` an (ohne Login-Shell, ohne Home) und ergänzt Gruppen (`cdrom`, `optical`, `disk`, `video`, `render`, falls vorhanden)
8. klont das Repository nach `/opt/ripster` (oder aktualisiert bei `--reinstall`)
9. legt Verzeichnisse an:
- `/opt/ripster/backend/data`
- `/opt/ripster/backend/logs`
- `/opt/ripster/backend/data/output/raw`
- `/opt/ripster/backend/data/output/movies`
- `/opt/ripster/backend/data/logs`
10. installiert npm-Abhängigkeiten (Root, Backend, Frontend) und baut das Frontend
11. erzeugt `backend/.env` (bei `--reinstall` bleibt bestehende `.env` erhalten)
12. setzt Rechte/Besitz und erstellt bei sudo-Installation zusätzlich `~/.MakeMKV` für den aufrufenden Benutzer
13. erzeugt und startet `ripster-backend.service`
14. konfiguriert und startet nginx (falls nicht übersprungen)
## Wichtige Optionen
| Option | Default laut Script | Zweck |
|---|---|---|
| `--branch <branch>` | `dev` | Branch für die Installation |
| `--dir <pfad>` | `/opt/ripster` | Installationsverzeichnis |
| `--user <benutzer>` | `ripster` | Service-Benutzer |
| `--port <port>` | `3001` | Backend-Port |
| `--host <hostname>` | automatisch ermittelte Host-IP | Hostname/IP für Webzugriff/CORS |
| `--no-makemkv` | aus | MakeMKV-Installation überspringen |
| `--no-handbrake` | aus | HandBrake-Installation überspringen |
| `--no-nginx` | aus | nginx-Setup überspringen |
| `--reinstall` | aus | bestehende Installation aktualisieren |
Beispiele:
```bash
sudo bash install.sh --branch dev
sudo bash install.sh --port 8080 --host ripster.local
sudo bash install.sh --reinstall
```
## Betrieb im Alltag
```bash
# Logs live ansehen
sudo journalctl -u ripster-backend -f
# Dienst neu starten
sudo systemctl restart ripster-backend
# Update aus bestehender Installation
sudo bash /opt/ripster/install.sh --reinstall
```
## Häufige Stolperstellen
- Laufwerk nicht zugreifbar: Laufwerksrechte/Gruppen prüfen
- CLI-Tool fehlt wegen `--no-*`: Tool nachinstallieren oder Befehl in Settings korrigieren
- UI nicht erreichbar: nginx-Status und Firewall prüfen
## Danach weiter
1. [Ersteinrichtung](configuration.md)
2. [Erster Lauf](quickstart.md)
+48
View File
@@ -0,0 +1,48 @@
# Voraussetzungen
Die Voraussetzungen hängen davon ab, **wie** du Ripster betreibst.
## Produktionsbetrieb mit `install.sh` (Standard)
Für den normalen Betrieb sind nur wenige Punkte vorab nötig.
### Pflicht
- unterstütztes Linux-System (laut Script: Debian, Ubuntu, Linux Mint, Pop!_OS)
- `root`-Rechte
- Internetzugang während der Installation
- optisches Laufwerk für Disc-Betrieb
`install.sh` installiert die benötigten Tools selbst (u. a. Node.js, MakeMKV, HandBrakeCLI, MediaInfo), sofern sie nicht explizit per `--no-*` übersprungen werden.
### Laufwerk kurz prüfen
```bash
ls /dev/sr*
lsblk | grep rom
```
Wenn nötig Rechte setzen (Beispiel):
```bash
sudo chmod a+rw /dev/sr0
```
### Optional vorab
- OMDb API-Key (kann auch nach Installation in den `Settings` gesetzt werden)
- PushOver-Zugangsdaten (optional)
## Entwicklungsmodus (nur für Dev)
Wenn du lokal entwickelst (`./start.sh`, `npm run dev`), gelten zusätzliche Voraussetzungen:
- Node.js >= 20.19.0
- `makemkvcon`, `HandBrakeCLI`, `mediainfo` im `PATH`
Details: [Entwicklungsumgebung](../deployment/development.md)
## Abschluss-Checkliste
- [ ] Produktionsbetrieb: Linux + root + Internet + Laufwerk vorhanden
- [ ] Dev-Modus (nur falls benötigt): Node.js und CLI-Tools verfügbar
+70
View File
@@ -0,0 +1,70 @@
# Erster Lauf
Dieser Ablauf zeigt einen vollständigen Job aus Anwendersicht: von Disc-Erkennung bis fertiger Datei.
## 1. Ripper öffnen und Disc einlegen
Erwartung:
- Status wechselt auf `Medium erkannt`
- im Bereich `Disk-Information` sind Laufwerksdaten sichtbar
Wenn nichts passiert: `Laufwerk neu lesen`.
## 2. Analyse starten
Aktion im Ripper:
- `Analyse starten`
Erwartung:
- Status `Analyse`
- danach Metadaten-Dialog
## 3. Metadaten auswählen
Im Dialog `Metadaten auswählen`:
1. OMDb-Suche nutzen oder manuell eintragen
2. passenden Treffer markieren
3. `Auswahl übernehmen`
## 4. Auf den nächsten Zustand reagieren
- Normalfall ohne vorhandenes RAW: `Rippen` -> `Mediainfo-Pruefung` -> `Bereit zum Encodieren`
- bei vorhandenem RAW: direkt `Mediainfo-Pruefung` -> `Bereit zum Encodieren`
- bei unklarer Blu-ray-Playlist: `Warte auf Auswahl` (Playlist auswählen und übernehmen)
## 5. Review in `Bereit zum Encodieren`
Im aufgeklappten Job (`Pipeline-Status`):
- Encode-Titel wählen
- Audio-/Subtitle-Spuren prüfen
- optional User-Preset auswählen
- optional Pre-/Post-Skripte bzw. Ketten hinzufügen
Dann `Encoding starten`.
## 6. Encoding überwachen
Während `Encodieren`:
- Fortschritt + ETA im Ripper
- Live-Log im `Pipeline-Status`
- Queue- und Skript/Cron-Status parallel beobachtbar
## 7. Ergebnis prüfen
Bei `Fertig`:
1. Seite `Historie` öffnen
2. Job in Details öffnen
3. Output-Pfad, Status und Log prüfen
## Typische Folgeaktionen
- Falsches OMDb-Match: in `Historie` -> `OMDb neu zuordnen`
- Neue Encodierung aus RAW: `RAW neu encodieren`
- Prüfung komplett neu aufbauen: `Review neu starten`
+91
View File
@@ -0,0 +1,91 @@
# Converter
Die Converter-Seite ermöglicht das Konvertieren vorhandener Audio- und Video-Dateien sowie das Verarbeiten von Audiobooks (AAX/M4B).
---
## Überblick
Der Converter arbeitet mit einem konfigurierbaren Eingangsordner (`converter_raw_dir`). Dateien werden dort abgelegt (manuell oder per Upload) und dann als Converter-Jobs in die Pipeline eingereiht.
---
## Datei-Explorer
Der Datei-Explorer zeigt den Inhalt des `converter_raw_dir` als Baumstruktur.
### Verfügbare Aktionen
| Aktion | Beschreibung |
|---|---|
| **Upload** | Dateien direkt aus dem Browser hochladen (bis zu 50 Dateien) |
| **Ordner erstellen** | Neuen Unterordner anlegen |
| **Umbenennen** | Datei oder Ordner umbenennen |
| **Verschieben** | Datei oder Ordner in einen anderen Unterordner verschieben |
| **Löschen** | Datei oder Ordner löschen (unwiderruflich) |
| **Scannen** | Eingangsordner manuell neu scannen |
### Dateiauswahl
Dateien können per Checkbox ausgewählt werden. Aus der Auswahl lassen sich Jobs erstellen:
- **Ein Job pro Datei** — jede Datei wird als eigenständiger Job angelegt
- **Gemeinsamer Job (Audio)** — mehrere Audio-Dateien werden zu einem gemeinsamen Job zusammengefasst
Erlaubte Dateiendungen werden in den Settings unter `Converter > Erlaubte Datei-Endungen*` als Checkboxen konfiguriert.
---
## Jobs erstellen und konfigurieren
Nach der Auswahl und Job-Erstellung erscheinen die Jobs in der Job-Liste.
### Job-Konfiguration
Jeder Job kann konfiguriert werden:
- **Medientyp:** Video, Audio oder Audiobook
- **Ausgabeformat:** z. B. `mkv`, `mp4`, `m4b`, `mp3`, `flac`
- **HandBrake-Preset:** für Video-Konvertierung
- **Track-Auswahl:** Video-, Audio- und Untertitel-Spuren
- **Metadaten:** Titel, Autor, Jahr (für Audiobooks)
- **MusicBrainz-Suche:** automatische Metadaten-Suche für Audiodateien
### Job-Start
Jobs starten sofort oder reihen sich in die Queue ein (abhängig von `Parallele Jobs`).
---
## Auto-Scan (Polling)
Wenn in den Settings `Converter > Auto-Scan (Polling)` aktiviert ist, wird der Eingangsordner automatisch in regelmäßigen Abständen gescannt. Neue Dateien werden in der DB erfasst und im Explorer angezeigt.
Das Scan-Intervall wird unter `Converter > Polling-Intervall (Sekunden)` konfiguriert (Standard: 300 Sekunden).
---
## Audiobook-Verarbeitung
Für AAX-Dateien (Audible) ist folgendes erforderlich:
1. AAX-Datei in den Eingangsordner hochladen
2. Job mit Medientyp `Audiobook` erstellen
3. Activation Bytes müssen verfügbar sein (werden beim ersten Mal abgefragt und gecacht)
4. Ausgabeformat wählen: M4B (empfohlen), MP3 oder FLAC
5. Kapitel-Splitting konfigurieren (optional)
---
## Einstellungen
| Setting | Pfad in GUI |
|---|---|
| Eingangsordner | `Settings > Pfade > Converter Raw-Ordner` |
| Videoausgabe | `Settings > Pfade > Converter Ausgabe (Video)` |
| Audioausgabe | `Settings > Pfade > Converter Ausgabe (Audio)` |
| Erlaubte Endungen | `Settings > Converter > Erlaubte Datei-Endungen*` |
| Auto-Scan | `Settings > Converter > Auto-Scan (Polling)` |
| Scan-Intervall | `Settings > Converter > Polling-Intervall (Sekunden)` |
| Output-Template Video | `Settings > Pfade > Output-Template (Video)` |
| Output-Template Audio | `Settings > Pfade > Output-Template (Audio)` |
+29
View File
@@ -0,0 +1,29 @@
# Database (Expert)
`/database` ist eine erweiterte Ansicht für Power-User und Recovery-Fälle.
## Zugriff
- Route direkt aufrufen: `/database`
- nicht Teil der Standard-Navigation
## Bereiche
### `Gefundene RAW-Einträge`
Listet RAW-Ordner, die keinen zugehörigen Job-Eintrag haben.
Aktionen:
- `RAW prüfen` (Scan der konfigurierten RAW-Pfade)
- `Job anlegen` (Orphan-RAW in Historie importieren)
## Typischer Einsatz
- nach manuellen Dateioperationen
- nach Migrationen oder Recovery
- wenn RAW-Dateien vorhanden sind, aber kein Historieneintrag existiert
## Vorsicht
Diese Seite erlaubt Eingriffe mit direkter Auswirkung auf Datenbestand und Historie. Vor Lösch- oder Importaktionen Pfade und Zieljob sorgfältig prüfen.
+53
View File
@@ -0,0 +1,53 @@
# Downloads
Die Downloads-Seite ermöglicht es, Ausgabedateien aus abgeschlossenen Jobs als ZIP-Archiv herunterzuladen.
---
## Überblick
Jobs, die in der Historie abgeschlossen sind, können in die Download-Queue eingereiht werden. Ripster erstellt daraus ein ZIP-Archiv, das direkt im Browser heruntergeladen werden kann.
---
## Download-Queue
Die Queue zeigt alle aktuellen Download-Einträge mit ihrem Status:
| Status | Beschreibung |
|---|---|
| **Ausstehend** | In der Queue, ZIP-Erstellung noch nicht gestartet |
| **Wird verarbeitet** | ZIP-Archiv wird gerade erstellt |
| **Bereit** | ZIP fertig, Download-Button verfügbar |
| **Fehlgeschlagen** | Fehler bei der ZIP-Erstellung |
---
## Download einreihen
Downloads können über die **Historie** eingereiht werden:
1. Job in der Historie öffnen
2. **Download einreihen** wählen
3. Ziel auswählen: `RAW-Dateien` oder `Ausgabedateien`
4. Den Eintrag in der Downloads-Queue verfolgen
---
## Download starten
Sobald der Status **Bereit** angezeigt wird, kann die ZIP-Datei mit dem Download-Button heruntergeladen werden.
ZIP-Dateinamen folgen dem Muster: `Job_<id>_<target>.zip`
---
## Einträge löschen
Abgeschlossene oder fehlerhafte Einträge können einzeln gelöscht werden. Die dazugehörige ZIP-Datei wird dabei ebenfalls vom Server entfernt.
---
## Echtzeit-Updates
Die Downloads-Seite aktualisiert sich automatisch per WebSocket (`DOWNLOADS_UPDATED`). Statusänderungen (z. B. von `Wird verarbeitet` zu `Bereit`) werden ohne Seiten-Reload angezeigt.
+62
View File
@@ -0,0 +1,62 @@
# Historie
Die Seite `Historie` ist für Suche, Prüfung und Nachbearbeitung bestehender Jobs.
## Hauptansicht
Filter und Werkzeuge:
- Suche (Titel/IMDb)
- Status-Filter
- Medium-Filter (`Blu-ray`, `DVD`, `Sonstiges`)
- Sortierung
- Listen-/Grid-Layout
Jeder Eintrag zeigt:
- Poster, Titel, Jahr, IMDb
- Medium-Indikator
- Status
- Start/Ende
- Verfügbarkeit von RAW/Movie
- Ratings (wenn OMDb-Daten vorhanden)
Klick auf einen Eintrag öffnet die Detailansicht.
---
## Job-Detaildialog
Bereiche:
- Film-Infos + OMDb-Details
- Job-Infos (Status, Pfade, Erfolgsflags, Fehler)
- hinterlegte Encode-Auswahl
- ausgeführter HandBrake-Befehl
- strukturierte JSON-Blöcke (OMDb/MakeMKV/MediaInfo/EncodePlan/HandBrake)
- Log-Ladefunktionen (`Tail`, `Vollständig`)
## Typische Aktionen im Detaildialog
- `OMDb neu zuordnen`
- `Encode neu starten`
- `Review neu starten`
- `RAW neu encodieren`
- `RAW löschen`, `Movie löschen`, `Beides löschen`
- `Historieneintrag löschen`
- bei Queue-Lock: `Aus Queue löschen`
## Wann welche Aktion?
| Ziel | Aktion |
|---|---|
| Metadaten korrigieren | `OMDb neu zuordnen` |
| mit gleicher bestätigter Auswahl neu encodieren | `Encode neu starten` |
| Titel-/Spurprüfung komplett neu berechnen | `Review neu starten` |
| aus vorhandenem RAW erneut encodieren | `RAW neu encodieren` |
| Speicher freigeben | Dateilöschaktionen |
## Logs
- `Tail laden (800)` für schnelle Fehleranalyse
- `Vollständiges Log laden` für vollständige Nachverfolgung
+28
View File
@@ -0,0 +1,28 @@
# GUI-Seiten
Ripster hat fünf Hauptseiten in der Navigation plus eine Expert-Seite.
## Seitenüberblick
| Seite | Zweck |
|---|---|
| [Ripper](ripper.md) | Live-Betrieb: Pipeline, Queue, Aktivitäten, Disc-Infos |
| [Settings](settings.md) | Konfiguration, Skripte, Ketten, Presets, Cronjobs |
| [Historie](history.md) | abgeschlossene/laufende Jobs durchsuchen und nachbearbeiten |
| [Converter](converter.md) | Audio/Video-Dateien konvertieren, Datei-Explorer, Upload |
| [Downloads](downloads.md) | Ausgabedateien als ZIP herunterladen |
| [Database (Expert)](database.md) | tabellarische Rohsicht inkl. Orphan-RAW-Import |
## Empfohlene Nutzung im Alltag
1. **Start eines neuen Disc-Jobs:** `Ripper`
2. **Dateien konvertieren oder Audiobooks verarbeiten:** `Converter`
3. **Regeln/Automatisierung anpassen:** `Settings`
4. **Ergebnisse prüfen oder Jobs nachbearbeiten:** `Historie`
5. **Dateien herunterladen:** `Downloads`
6. **Sonderfälle/Recovery:** `Database`
## Hinweise zur Navigation
- `Ripper`, `Settings`, `Historie`, `Converter` und `Downloads` sind direkt in der Kopfnavigation.
- `Database` ist als Expert-Route verfügbar: `/database`.
+124
View File
@@ -0,0 +1,124 @@
# Ripper
Das Ripper ist die **Betriebszentrale** für laufende Jobs.
## Aufbau der Seite
Die Bereiche erscheinen in dieser Reihenfolge:
1. `Hardware Monitoring`
2. `Job Queue`
3. `Skript- / Cron-Status`
4. `Job Übersicht`
5. `Disk-Information`
---
## 1) Hardware Monitoring
Zeigt live:
- CPU (gesamt + optional pro Kern)
- RAM
- GPU-Auslastung/Temperatur/VRAM
- freien Speicher in den konfigurierten Pfaden
Wichtig für den Betrieb:
- Hohe Speicherauslastung oder fast volle Zielpfade früh erkennen
- über `Settings` aktivierbar/deaktivierbar (`Hardware Monitoring aktiviert`)
## 2) Job Queue
Zwei Spalten:
- `Laufende Jobs`
- `Warteschlange`
Mögliche Aktionen:
- Queue per Drag-and-Drop umsortieren
- Queue-Job entfernen (`X`)
- zusätzliche Queue-Elemente einfügen (`+`):
- Skript
- Skriptkette
- Wartezeit
Hinweis:
- `Parallel` zeigt den Wert aus `Settings` -> `Parallele Jobs`.
## 3) Skript- / Cron-Status
Zeigt:
- aktive Ausführungen (Skripte, Ketten, Cron)
- zuletzt abgeschlossene Ausführungen
Mögliche Aktionen:
- laufende Ketten: `Nächster Schritt`
- laufende Einträge: `Abbrechen`
- Historie der Aktivitäten: `Liste leeren`
## 4) Job Übersicht
Kompakte Jobliste mit Status, Fortschritt, ETA. Klick auf einen Job klappt die Detailsteuerung auf.
Im aufgeklappten Zustand erscheint die Karte `Pipeline-Status` mit allen zustandsabhängigen Aktionen.
### Zustandsabhängige Hauptaktionen
| Zustand | Typische Aktion |
|---|---|
| `Medium erkannt` / `Bereit` | `Analyse starten` |
| `Metadatenauswahl` | `Metadaten öffnen` |
| `Warte auf Auswahl` | Playlist wählen und `Playlist übernehmen` |
| `Startbereit` | `Job starten` |
| `Bereit zum Encodieren` | Tracks/Skripte prüfen, dann `Encoding starten` |
| laufend (`Analyse`/`Rippen`/`Encodieren`) | `Abbrechen` |
| `Fehler` / `Abgebrochen` | `Retry Rippen`, `Disk-Analyse neu starten` |
Zusätzlich je nach Job:
- `Review neu starten`
- `Encode neu starten`
- `Aus Queue löschen`
### Titel-/Spurprüfung (`Bereit zum Encodieren`)
Im selben Block siehst du:
- Auswahl des Encode-Titels
- Audio-/Subtitle-Trackauswahl
- User-Preset-Auswahl
- Pre-/Post-Encode-Skripte und Ketten
- Preview des finalen HandBrakeCLI-Befehls
## 5) Disk-Information
Zeigt aktuelles Laufwerk und Disc-Metadaten (`Pfad`, `Modell`, `Disc-Label`, `Mount`).
Aktionen:
- `Laufwerk neu lesen`
- `Disk neu analysieren`
- `Metadaten-Modal öffnen`
---
## Wichtige Dialoge im Ripper
### Metadaten auswählen
- OMDb-Suche + Ergebnisliste
- manuelle Eingabe als Fallback
- `Auswahl übernehmen` startet den nächsten Pipeline-Schritt
### Abbruch-Bereinigung
Nach Abbruch kann Ripster optional fragen, ob erzeugte RAW- oder Movie-Dateien gelöscht werden sollen.
### Queue-Eintrag einfügen
Erstellt gezielt einen Skript-, Ketten- oder Warte-Eintrag an einer bestimmten Queue-Position.
+92
View File
@@ -0,0 +1,92 @@
# Settings
Die Seite `Settings` steuert Konfiguration und Automatisierung.
## Tabs im Überblick
| Tab | Zweck |
|---|---|
| `Konfiguration` | alle Kernsettings (Pfade, Tools, Monitoring, Metadaten, Queue, Benachrichtigungen) |
| `Scripte` | einzelne Bash-Skripte verwalten und testen |
| `Skriptketten` | Sequenzen aus Skript- und Warte-Schritten bauen |
| `Encode-Presets` | benutzerdefinierte Presets für das Review im Ripper |
| `Cronjobs` | zeitgesteuerte Skript-/Kettenausführung |
---
## Tab `Konfiguration`
Wichtiges Bedienmuster:
1. Werte ändern
2. `Änderungen speichern`
3. bei Bedarf `Änderungen verwerfen` oder `Neu laden`
Zusätzlich:
- `PushOver Test` sendet eine Testnachricht
- Änderungen werden erst nach Speichern wirksam
- Tool-Preset-Felder bieten HandBrake-Presetauswahl direkt im Formular
## Tab `Scripte`
Funktionen:
- Skript anlegen, bearbeiten, löschen
- Skript testen (`Test`)
- Reihenfolge per Drag-and-Drop
Praxis:
- Reihenfolge ist wichtig, weil ausgewählte Skripte später sequentiell abgearbeitet werden.
- Testresultate zeigen Exit-Code, Dauer und stdout/stderr.
## Tab `Skriptketten`
Funktionen:
- Kette anlegen/bearbeiten/löschen
- Kette testen
- Reihenfolge der Ketten per Drag-and-Drop
Im Ketten-Editor:
- Bausteine links (`Warten`, vorhandene Skripte)
- Schritte rechts per Klick oder Drag-and-Drop hinzufügen
- Schrittreihenfolge im Canvas ändern
## Tab `Encode-Presets`
Ein Preset bündelt:
- optional HandBrake-Preset (`-Z`)
- optionale Extra-Args
- Medientyp (`Universell`, `Blu-ray`, `DVD`, `Sonstiges`)
Verwendung:
- Diese Presets erscheinen später im Ripper im Review (`Bereit zum Encodieren`).
## Tab `Cronjobs`
Funktionen:
- Cronjob anlegen und bearbeiten
- Quelle wählen: Skript oder Skriptkette
- Cron-Ausdruck validieren
- `Jetzt ausführen`
- Logs je Cronjob anzeigen
- `Aktiviert` und `Pushover` toggeln
Hilfen:
- Beispiele für Cron-Ausdrücke direkt im Dialog
- Link zu `crontab.guru` im Editor
---
## Empfehlung für stabile Nutzung
1. Erst `Konfiguration` sauber setzen
2. dann Skripte/Ketten testen
3. danach Cronjobs aktivieren
+33
View File
@@ -0,0 +1,33 @@
# Ripster Handbuch
Dieses Dokumentationsset ist als **Benutzerhandbuch** aufgebaut: erst Bedienung und Alltag, dann Technik im Anhang.
---
## Schnellstart in 3 Schritten
1. Voraussetzungen prüfen und installieren: [Installation](getting-started/installation.md)
2. Grundkonfiguration in der UI setzen: [Ersteinrichtung](getting-started/configuration.md)
3. Ersten vollständigen Job durchlaufen: [Erster Lauf](getting-started/quickstart.md)
---
## Was du hier findest
- **Benutzerhandbuch**
- Installation
- GUI-Seiten im Detail (`Ripper`, `Settings`, `Historie`, `Database`)
- typische Arbeitsabläufe aus Anwendersicht
- **Technischer Anhang**
- vollständige Einstellungsreferenz
- Pipeline-/API-/Architekturdetails
- Deployment und Tool-Hintergründe
---
## Empfohlene Lesereihenfolge
1. [Benutzerhandbuch Überblick](getting-started/index.md)
2. [GUI-Seiten](gui/index.md)
3. [Workflows aus Nutzersicht](workflows/index.md)
4. Bei Bedarf: [Technischer Anhang](appendix/index.md)
+103
View File
@@ -0,0 +1,103 @@
# Encode-Planung & Track-Auswahl
Vor dem eigentlichen Encoding erstellt Ripster einen Encode-Plan und zeigt ihn im Review an.
---
## Ablauf
```text
Quelle bestimmen (Disc/RAW)
-> HandBrake-Scan (--scan --json)
-> Plan erstellen (Titel, Audio, Untertitel)
-> Status: Bereit zum Encodieren
-> Benutzer bestaetigt Auswahl
-> finaler HandBrake-Aufruf
```
---
## Review-Inhalt (Status: `Bereit zum Encodieren`)
- auswählbarer Encode-Titel
- Audio-Track-Auswahl
- Untertitel-Track-Auswahl inkl. Flags
- `burnIn`
- `forced`
- `defaultTrack`
- optionale User-Presets (HandBrake Preset + Extra Args)
- optionale Pre-/Post-Skripte und Ketten
---
## Bestaetigung (`confirm-encode`)
Typischer Payload:
```json
{
"selectedEncodeTitleId": 1,
"selectedTrackSelection": {
"1": {
"audioTrackIds": [1, 2],
"subtitleTrackIds": [3]
}
},
"selectedPreEncodeScriptIds": [1],
"selectedPostEncodeScriptIds": [2],
"selectedPreEncodeChainIds": [3],
"selectedPostEncodeChainIds": [4],
"selectedUserPresetId": 5
}
```
Die bestätigte Auswahl wird im Job gespeichert und für Neustarts wiederverwendet.
---
## HandBrake-Aufruf
Grundstruktur:
```bash
HandBrakeCLI \
-i <input> \
-o <output> \
-t <titleId> \
-Z "<preset>" \
<extra-args> \
-a <audioTrackIds|none> \
-s <subtitleTrackIds|none>
```
Untertitel-Flags werden bei Bedarf ergänzt:
- `--subtitle-burned=<id>`
- `--subtitle-default=<id>`
- `--subtitle-forced=<id>` oder `--subtitle-forced`
---
## Pre-/Post-Encode-Ausführungen
- Pre-Encode läuft vor HandBrake
- Post-Encode läuft nach HandBrake
Fehlerverhalten:
- Pre-Encode-Fehler: Job endet mit Status `Fehler` (Encode startet nicht)
- Post-Encode-Fehler: Job kann `Fertig` bleiben, enthält aber Fehlerhinweis/Script-Summary
---
## Dateinamen/Ordner
Der finale Outputpfad wird aus den Templates in den Settings aufgebaut.
Platzhalter:
- `${title}`
- `${year}`
- `${imdbId}`
Ungültige Dateizeichen werden bereinigt.
+43
View File
@@ -0,0 +1,43 @@
# Anhang: Pipeline intern
Dieser Abschnitt beschreibt die technische Pipeline-Logik hinter den UI-Workflows.
<div class="grid cards" markdown>
- :material-state-machine: **Workflow & Zustände**
---
Zustandsmodell, Übergänge, Queue-Verhalten.
[:octicons-arrow-right-24: Workflow](workflow.md)
- :material-film: **Encode-Planung**
---
Aufbereitung von Titeln/Tracks und Bestätigungslogik.
[:octicons-arrow-right-24: Encoding](encoding.md)
- :material-playlist-check: **Playlist-Analyse**
---
Bewertung mehrdeutiger Blu-ray-Playlists.
[:octicons-arrow-right-24: Playlist-Analyse](playlist-analysis.md)
- :material-script-text: **Pre-/Post-Encode-Ausführungen**
---
Skript- und Kettenlauf vor/nach dem Encoding.
[:octicons-arrow-right-24: Encode-Skripte](post-encode-scripts.md)
</div>
## Zurück zum Handbuch
- [Workflows aus Nutzersicht](../workflows/index.md)
+60
View File
@@ -0,0 +1,60 @@
# Playlist-Analyse
Ripster analysiert bei Blu-ray-ähnlichen Quellen Playlists und fordert bei Mehrdeutigkeit eine manuelle Auswahl an.
---
## Ziel
Erkennen, welche Playlist sehr wahrscheinlich der Hauptfilm ist, statt versehentlich eine Fake-/Dummy-Playlist zu verwenden.
---
## Eingabedaten
Die Analyse basiert auf MakeMKV-Infos (u. a. Playlist-/Segment-Struktur, Laufzeiten, Titelzuordnung).
---
## Auswertung (vereinfacht)
Für Kandidaten werden u. a. berücksichtigt:
- Laufzeit
- Segment-Reihenfolge
- Rückwärtssprünge/große Sprünge
- Kohärenz linearer Segmentfolgen
- Duplikatgruppen mit ähnlicher Laufzeit
Daraus entstehen intern Kandidatenlisten, Bewertungen und eine Empfehlung.
---
## Wann muss der Benutzer entscheiden?
Wenn nach Filterung mehr als ein relevanter Kandidat übrig bleibt, wechselt der Job in der GUI auf:
- `Warte auf Auswahl`
Dann muss eine Playlist bestätigt werden, bevor der Workflow weiterläuft.
---
## Konfigurationseinfluss
| Feld in `Settings` | Wirkung |
|---|---|
| `Minimale Titellaenge (Minuten)` | Mindestlaufzeit für Kandidaten |
Default ist aktuell `60` Minuten.
---
## UI-Verhalten
Bei manueller Entscheidung zeigt das Ripper Kandidaten inkl. Score/Bewertung und markiert eine Empfehlung.
Nach Bestätigung:
- mit vorhandenem RAW -> zurück zu `Mediainfo-Pruefung`
- ohne RAW -> Startpfad über `Startbereit` / `Rippen`
+70
View File
@@ -0,0 +1,70 @@
# Encode-Skripte (Pre & Post)
Ripster kann Skripte und Skript-Ketten vor und nach dem Encode ausführen.
---
## Ablauf
```text
Bereit zum Encodieren
-> Pre-Encode Skripte/Ketten
-> HandBrake Encoding
-> Post-Encode Skripte/Ketten
-> Fertig oder Fehler
```
---
## Auswahl im Review
Im Review-Panel kannst du getrennt wählen:
- `selectedPreEncodeScriptIds`
- `selectedPostEncodeScriptIds`
- `selectedPreEncodeChainIds`
- `selectedPostEncodeChainIds`
---
## Fehlerverhalten
- Pre-Encode-Fehler stoppen die Kette und führen zu `Fehler`.
- Post-Encode-Fehler stoppen die restlichen Post-Schritte; Job kann dennoch `Fertig` sein (mit Fehlerzusatz im Status/Log).
---
## Verfügbare Umgebungsvariablen
Beim Script-Run werden 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
Ketten unterstützen zwei Step-Typen:
- `script` (führt ein hinterlegtes Skript aus)
- `wait` (wartet `waitSeconds`)
Bei Fehler in einem Script-Step wird die Kette abgebrochen.
---
## Testläufe
- Skript testen: `POST /api/settings/scripts/:id/test`
- Kette testen: `POST /api/settings/script-chains/:id/test`
Ergebnisse enthalten Erfolg/Exit-Code, Laufzeit und stdout/stderr.
+87
View File
@@ -0,0 +1,87 @@
# Workflow & Zustände
Ripster steuert den Ablauf als Zustandsmaschine.
---
## Zustandsdiagramm (vereinfacht)
```mermaid
flowchart LR
A[Bereit] --> B[Medium erkannt]
B --> C[Analyse]
C --> D[Metadatenauswahl]
D --> E[Startbereit]
E --> F[Rippen]
E --> G[Mediainfo-Pruefung]
G --> H[Warte auf Auswahl]
H --> G
G --> I[Bereit zum Encodieren]
I --> J[Encodieren]
J --> K[Fertig]
J --> L[Fehler]
F --> L
F --> M[Abgebrochen]
```
---
## Statusliste (GUI-Anzeige)
| Status in der GUI | Bedeutung |
|---|---|
| `Bereit` | Wartet auf Disc |
| `Medium erkannt` | Disc wurde erkannt |
| `Analyse` | MakeMKV-Analyse läuft |
| `Metadatenauswahl` | Metadaten müssen bestätigt werden |
| `Warte auf Auswahl` | Playlist-Auswahl ist erforderlich |
| `Startbereit` | kurzer Übergang vor Start |
| `Rippen` | MakeMKV-Rip läuft |
| `Mediainfo-Pruefung` | Titel/Spuren werden ausgewertet |
| `Bereit zum Encodieren` | Review ist bereit |
| `Encodieren` | HandBrake läuft |
| `Fertig` | erfolgreich abgeschlossen |
| `Abgebrochen` | manuell oder technisch abgebrochen |
| `Fehler` | fehlgeschlagen |
---
## Typische Pfade
### Standardfall (kein vorhandenes RAW)
1. Medium erkannt
2. Analyse + Metadaten
3. Rippen
4. Mediainfo-Pruefung
5. Bereit zum Encodieren
6. Encodieren
7. Fertig
### Vorhandenes RAW
`Startbereit` springt direkt zu `Mediainfo-Pruefung` (kein neuer Rip).
### Mehrdeutige Blu-ray-Playlist
`Mediainfo-Pruefung` -> `Warte auf Auswahl` bis Playlist bestätigt wurde.
---
## Queue-Verhalten
Wenn der Wert `Parallele Jobs` erreicht ist:
- neue Starts werden als Queue-Einträge abgelegt
- die Queue kann zusätzlich Nicht-Job-Einträge enthalten (`Skript`, `Kette`, `Warten`)
- Reihenfolge ist per UI/API änderbar
---
## Abbruch, Wiederaufnahme, Neustart
- `Abbrechen`: laufenden Job stoppen oder Queue-Eintrag entfernen
- `Retry Rippen`: Fehler-/Abbruch-Job erneut starten
- `RAW neu encodieren`: aus vorhandenem RAW neu encodieren
- `Review neu starten`: Review aus RAW neu aufbauen
- `Encode neu starten`: Encoding mit letzter bestätigter Auswahl neu starten
+150
View File
@@ -0,0 +1,150 @@
/* Ripster custom styles */
:root {
--md-primary-fg-color: #6a1b9a;
--md-accent-fg-color: #ab47bc;
}
/* Cards grid layout */
.grid.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1rem;
margin: 1.5rem 0;
}
.grid.cards > * {
border-radius: 0.5rem;
border: 1px solid var(--md-default-fg-color--lightest);
padding: 1.25rem;
transition: box-shadow 0.2s ease;
}
.grid.cards > *:hover {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
}
/* Status badge colors */
.status-idle { color: #78909c; }
.status-analyzing { color: #fb8c00; }
.status-ripping { color: #1976d2; }
.status-encoding { color: #7b1fa2; }
.status-finished { color: #388e3c; }
.status-error { color: #d32f2f; }
/* Code blocks */
.md-typeset pre > code {
font-size: 0.85em;
}
/* Mermaid diagrams standard */
.md-typeset .mermaid {
display: flex;
justify-content: center;
margin: 1.5rem 0;
}
/* Large pipeline flowchart: horizontal scroll + min-height */
.pipeline-diagram {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
margin: 1.5rem 0;
padding-bottom: 0.5rem;
}
.pipeline-diagram .mermaid {
min-width: 900px;
justify-content: flex-start;
}
.pipeline-diagram .mermaid svg {
min-width: 900px;
height: auto;
}
/* Pipeline step track */
.pipeline-steps {
display: flex;
flex-wrap: nowrap;
gap: 0;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
padding: 1rem 0 1.5rem 0;
margin: 1.5rem 0;
scrollbar-width: thin;
}
.pipeline-step {
display: flex;
flex-direction: column;
align-items: center;
min-width: 110px;
flex: 0 0 auto;
position: relative;
}
.pipeline-step:not(:last-child)::after {
content: '→';
position: absolute;
right: -14px;
top: 22px;
font-size: 1.2rem;
color: var(--md-default-fg-color--light);
z-index: 1;
}
.pipeline-step-badge {
width: 44px;
height: 44px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.75rem;
font-weight: 700;
margin-bottom: 0.5rem;
border: 2px solid transparent;
}
.pipeline-step-label {
font-size: 0.7rem;
text-align: center;
line-height: 1.2;
max-width: 90px;
color: var(--md-default-fg-color);
}
.pipeline-step-sub {
font-size: 0.6rem;
color: var(--md-default-fg-color--light);
text-align: center;
margin-top: 0.2rem;
}
/* Step colors */
.step-idle { background: #eceff1; color: #546e7a; border-color: #90a4ae; }
.step-user { background: #e8eaf6; color: #3949ab; border-color: #7986cb; }
.step-running { background: #e3f2fd; color: #1565c0; border-color: #42a5f5; }
.step-wait { background: #fff8e1; color: #e65100; border-color: #ffa726; }
.step-encode { background: #f3e5f5; color: #6a1b9a; border-color: #ab47bc; }
.step-done { background: #e8f5e9; color: #2e7d32; border-color: #66bb6a; }
.step-error { background: #ffebee; color: #c62828; border-color: #ef5350; }
/* Table improvements */
.md-typeset table:not([class]) {
width: 100%;
}
.md-typeset table:not([class]) th {
background-color: var(--md-primary-fg-color);
color: white;
}
/* Admonition tweaks */
.md-typeset .admonition.tip {
border-color: #00897b;
}
.md-typeset .admonition.tip > .admonition-title {
background-color: rgba(0, 137, 123, 0.1);
}
+69
View File
@@ -0,0 +1,69 @@
# HandBrake
Ripster verwendet `HandBrakeCLI` für Scan und Encode.
---
## Verwendete Aufrufe
### Scan (Review-Aufbau)
```bash
HandBrakeCLI --scan --json -i <input> -t 0
```
### Encode (vereinfacht)
```bash
HandBrakeCLI \
-i <input> \
-o <output> \
-t <titleId> \
-Z "<preset>" \
<extra-args> \
-a <audioTrackIds|none> \
-s <subtitleTrackIds|none>
```
Optional ergänzt Ripster:
- `--subtitle-burned=<id>`
- `--subtitle-default=<id>`
- `--subtitle-forced=<id>` oder `--subtitle-forced`
---
## Presets auslesen
Ripster liest Presets mit:
```bash
HandBrakeCLI -z
```
---
## Relevante Felder in `Settings`
| Feldname in der GUI | Bedeutung |
|-----|-----------|
| `HandBrake Kommando` | CLI-Binary |
| `HandBrake Preset` (Blu-ray/DVD) | profilspezifisches Preset |
| `HandBrake Extra Args` (Blu-ray/DVD) | profilspezifische Zusatzargumente |
| `Ausgabeformat` (Blu-ray/DVD) | Dateiendung der finalen Datei |
| `Encode-Neustart: unvollständige Ausgabe löschen` | unvollständige Ausgabe bei Neustart löschen |
---
## Fortschritts-Parsing
Ripster parst HandBrake-Stderr (Prozent/ETA/Detail) und sendet WebSocket-Progress (`PIPELINE_PROGRESS`).
---
## Troubleshooting
- Preset nicht gefunden: Preset-Namen mit `HandBrakeCLI -z` prüfen
- sehr langsames Encoding: Preset/Extra-Args prüfen (z. B. `--encoder-preset`)
Das Produktions-Installer-Script `install.sh` bietet eine Option zur Installation eines gebündelten HandBrakeCLI-Binaries mit NVDEC-Unterstützung (NVIDIA GPU-Dekodierung). Diese Option erscheint interaktiv während der Installation.
+31
View File
@@ -0,0 +1,31 @@
# Anhang: Externe Tools
Ripster orchestriert externe CLI-Tools. Dieser Abschnitt erklärt deren Rolle im Gesamtsystem.
<div class="grid cards" markdown>
- :material-disc: **MakeMKV**
---
Disc-Analyse und Ripping.
[:octicons-arrow-right-24: MakeMKV](makemkv.md)
- :material-film: **HandBrake**
---
Video-Encoding inklusive Preset-Logik.
[:octicons-arrow-right-24: HandBrake](handbrake.md)
- :material-information: **MediaInfo**
---
Track-/Containeranalyse für Review und Auswahl.
[:octicons-arrow-right-24: MediaInfo](mediainfo.md)
</div>
+61
View File
@@ -0,0 +1,61 @@
# MakeMKV
Ripster nutzt `makemkvcon` für Disc-Analyse und Rip.
---
## Verwendete Aufrufe
### Analyse
```bash
makemkvcon -r info <source>
```
`<source>` ist typischerweise:
- `disc:<index>` (Auto-Modus)
- `dev:/dev/sr0` (explicit)
- `file:<path>` (Datei/Ordner-Analyse)
### Rip (MKV-Modus)
```bash
makemkvcon mkv <source> <title-or-all> <rawDir> [--minlength=...] [...extraArgs]
```
### Rip (Backup-Modus)
```bash
makemkvcon backup <source> <rawDir> --decrypt
```
---
## Registrierungsschlüssel (optional)
Wenn in `Settings` ein `MakeMKV Key` gesetzt ist, führt Ripster vor Analyse/Rip aus:
```bash
makemkvcon reg <key>
```
---
## Relevante Felder in `Settings`
| Feldname in der GUI | Bedeutung |
|-----|-----------|
| `MakeMKV Kommando` | CLI-Binary |
| `MakeMKV Source Index` | Source-Index im Auto-Modus |
| `Minimale Titellaenge (Minuten)` | Mindestlaufzeitfilter |
| `MakeMKV Rip Modus` (Blu-ray/DVD) | `mkv` oder `backup` |
| `MakeMKV Analyze Extra Args` (Blu-ray/DVD) | Zusatzargumente für Analyse |
| `MakeMKV Rip Extra Args` (Blu-ray/DVD) | Zusatzargumente für Rip |
---
## Hinweise
- Blu-ray-Backups werden oft für robuste Playlist-Analyse genutzt.
- MakeMKV-Ausgaben werden geparst und als `makemkvInfo` im Job gespeichert.
+37
View File
@@ -0,0 +1,37 @@
# MediaInfo
Ripster nutzt `mediainfo` zur JSON-Analyse von Medien-Dateien.
---
## Aufruf
```bash
mediainfo --Output=JSON <input>
```
Der Input ist typischerweise eine RAW-Datei oder ein vom Workflow gewählter Inputpfad.
---
## Verwendung in Ripster
- Track-/Codec-Metadaten für Review-Plan
- Fallback-Informationen in bestimmten Analysepfaden
- Persistenz als `mediainfoInfo` im Job
---
## Relevante Settings
| Key | Bedeutung |
|-----|-----------|
| `mediainfo_command` | CLI-Binary |
| `mediainfo_extra_args_bluray` / `_dvd` | profilspezifische Zusatzargumente |
---
## Troubleshooting
- JSON-Test: `mediainfo --Output=JSON <datei>`
- unbekannte Sprache erscheint oft als `und` (undetermined)
+61
View File
@@ -0,0 +1,61 @@
# Workflows aus Nutzersicht
Diese Seite beschreibt typische Abläufe mit den passenden UI-Aktionen.
## Workflow 1: Standardlauf (Disc -> fertige Datei)
1. `Ripper`: Disc einlegen, `Analyse starten`
2. Metadaten im Dialog übernehmen
3. bei `Bereit zum Encodieren` Titel/Tracks prüfen
4. `Encoding starten`
5. Ergebnis in `Historie` kontrollieren
## Workflow 2: Playlist-Entscheidung bei Blu-ray
1. Job landet in `Warte auf Auswahl`
2. im `Pipeline-Status` Playlist-Kandidaten vergleichen
3. gewünschte Playlist auswählen
4. `Playlist übernehmen`
5. danach normal weiter bis `Bereit zum Encodieren`
## Workflow 3: Mehrere Jobs mit Queue
1. Parallel-Limit in `Settings` über `Parallele Jobs` setzen
2. neue Jobs starten; überschüssige Starts gehen in `Job Queue`
3. Reihenfolge per Drag-and-Drop anpassen
4. bei Bedarf Skript/Kette/Warten als Queue-Eintrag ergänzen
## Workflow 4: Nachbearbeitung eines bestehenden Jobs
In `Historie` -> Detaildialog:
- Metadaten korrigieren: `OMDb neu zuordnen`
- gleiche Einstellungen erneut nutzen: `Encode neu starten`
- Analyse neu aufbauen: `Review neu starten`
- aus RAW erneut encodieren: `RAW neu encodieren`
## Workflow 5: Automatisierung mit Skripten und Cron
1. `Settings` -> `Scripte`: Skripte anlegen und testen
2. `Settings` -> `Skriptketten`: Ketten bauen und testen
3. im Ripper-Review Pre-/Post-Ausführungen pro Job auswählen
4. `Settings` -> `Cronjobs`: zeitgesteuerte Ausführung konfigurieren
5. Status im Ripper (`Skript- / Cron-Status`) überwachen
## Workflow 6: Abbruch und Recovery
### Fall A: Job wurde abgebrochen
- im Ripper optional erzeugte RAW/Movie-Datei bereinigen
- anschließend je nach Ziel: `Retry Rippen` oder `Disk-Analyse neu starten`
### Fall B: Job steht in `Bereit zum Encodieren`, ist aber nicht aktive Session
- in `Historie`: `Im Ripper öffnen`
- im Ripper Review erneut prüfen und starten
### Fall C: RAW ohne Historieneintrag
- `/database` öffnen
- Bereich `Gefundene RAW-Einträge`
- `Job anlegen`