{"config":{"lang":["de"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"","title":"Ripster","text":"

Halbautomatische Disc-Ripping-Plattform f\u00fcr DVDs und Blu-rays

"},{"location":"#was-ist-ripster","title":"Was ist Ripster?","text":"

Ripster ist eine webbasierte Anwendung zur halbautomatischen Digitalisierung von DVDs und Blu-rays. Die Anwendung kombiniert bew\u00e4hrte Open-Source-Tools zu einem durchg\u00e4ngigen, komfortablen Workflow:

Disc einlegen \u2192 Erkennung \u2192 Analyse \u2192 Metadaten w\u00e4hlen \u2192 Rippen \u2192 Encodieren \u2192 Fertig\n
"},{"location":"#kernfunktionen","title":"Kernfunktionen","text":"Feature Beschreibung Echtzeit-Updates WebSocket-basierte Live-Statusanzeige ohne Reload Intelligente Playlist-Analyse Erkennt Blu-ray Playlist-Verschleierung (Fake-Playlists) Track-Auswahl Individuelle Auswahl von Audio- und Untertitelspuren Orphan-Recovery Import von bereits gerippten Dateien als Jobs PushOver-Benachrichtigungen Mobile Alerts bei Fertigstellung oder Fehlern DB-Korruptions-Recovery Automatische Quarant\u00e4ne bei korrupten SQLite-Dateien Re-Encoding Erneutes Encodieren ohne neu rippen"},{"location":"#technologie-stack","title":"Technologie-Stack","text":"BackendFrontendExterne Tools Tool Zweck makemkvcon Disc-Analyse & MKV/Backup-Ripping HandBrakeCLI Video-Encoding mediainfo Track-Informationen aus gerippten Dateien OMDb API Filmmetadaten (Titel, Poster, IMDb-ID)"},{"location":"#schnellstart","title":"Schnellstart","text":"
# 1. Repository klonen\ngit clone https://github.com/YOUR_GITHUB_USERNAME/ripster.git\ncd ripster\n\n# 2. Starten (Node.js >= 20 erforderlich)\n./start.sh\n\n# 3. Browser \u00f6ffnen\nopen http://localhost:5173\n

Erste Schritte

Die vollst\u00e4ndige Installationsanleitung mit allen Voraussetzungen findest du unter Erste Schritte.

"},{"location":"#pipeline-uberblick","title":"Pipeline-\u00dcberblick","text":"
flowchart LR\n    IDLE --> DD[DISC_DETECTED]\n    DD --> META[METADATA\\nSELECTION]\n    META --> RTS[READY_TO\\nSTART]\n    RTS -->|Auto-Start| RIP[RIPPING]\n    RTS -->|Auto-Start mit RAW| MIC\n    RIP --> MIC[MEDIAINFO\\nCHECK]\n    MIC -->|Playlist offen (Backup)| WUD[WAITING_FOR\\nUSER_DECISION]\n    WUD --> MIC\n    MIC --> RTE[READY_TO\\nENCODE]\n    RTE --> ENC[ENCODING]\n    ENC -->|inkl. Post-Skripte| FIN([FINISHED])\n    ENC --> ERR([ERROR])\n    RIP --> ERR\n\n    style FIN fill:#e8f5e9,stroke:#66bb6a,color:#2e7d32\n    style ERR fill:#ffebee,stroke:#ef5350,color:#c62828\n    style WUD fill:#fff8e1,stroke:#ffa726,color:#e65100\n    style ENC fill:#f3e5f5,stroke:#ab47bc,color:#6a1b9a

READY_TO_START ist in der Praxis meist ein kurzer \u00dcbergangszustand: der Job wird nach Metadaten-Auswahl automatisch gestartet oder in die Queue eingeplant.

"},{"location":"api/","title":"API-Referenz","text":"

Ripster bietet eine REST-API f\u00fcr Steuerung/Verwaltung sowie einen WebSocket-Endpunkt f\u00fcr Echtzeit-Updates.

"},{"location":"api/#basis-url","title":"Basis-URL","text":"
http://localhost:3001\n

API-Prefix: /api

Beispiele:

"},{"location":"api/#api-gruppen","title":"API-Gruppen","text":""},{"location":"api/#authentifizierung","title":"Authentifizierung","text":"

Es gibt keine eingebaute Authentifizierung. Ripster ist f\u00fcr lokalen Betrieb gedacht.

"},{"location":"api/#fehlerformat","title":"Fehlerformat","text":"

Fehler werden zentral als JSON geliefert:

{\n  \"error\": {\n    \"message\": \"Job nicht gefunden.\",\n    \"statusCode\": 404,\n    \"reqId\": \"req_...\",\n    \"details\": [\n      {\n        \"field\": \"name\",\n        \"message\": \"Name darf nicht leer sein.\"\n      }\n    ]\n  }\n}\n

details ist optional (z. B. bei Validierungsfehlern).

"},{"location":"api/#haufige-statuscodes","title":"H\u00e4ufige Statuscodes","text":"Code Bedeutung 200 Erfolg 201 Ressource erstellt 400 Ung\u00fcltige Anfrage / Validierungsfehler 404 Ressource nicht gefunden 409 Konflikt (z. B. falscher Pipeline-Zustand, Job l\u00e4uft bereits) 500 Interner Fehler"},{"location":"api/crons/","title":"Cron API","text":"

Ripster enth\u00e4lt ein eingebautes Cron-System f\u00fcr Skripte und Skript-Ketten (sourceType: script|chain).

"},{"location":"api/crons/#get-apicrons","title":"GET /api/crons","text":"

Listet alle Cron-Jobs.

{\n  \"jobs\": [\n    {\n      \"id\": 1,\n      \"name\": \"Nachtlauf Backup\",\n      \"cronExpression\": \"0 2 * * *\",\n      \"sourceType\": \"script\",\n      \"sourceId\": 3,\n      \"sourceName\": \"Backup-Skript\",\n      \"enabled\": true,\n      \"pushoverEnabled\": true,\n      \"lastRunAt\": \"2026-03-10T02:00:00.000Z\",\n      \"lastRunStatus\": \"success\",\n      \"nextRunAt\": \"2026-03-11T02:00:00.000Z\",\n      \"createdAt\": \"2026-03-01T10:00:00.000Z\",\n      \"updatedAt\": \"2026-03-10T02:00:05.000Z\"\n    }\n  ]\n}\n
"},{"location":"api/crons/#post-apicrons","title":"POST /api/crons","text":"

Erstellt Cron-Job.

{\n  \"name\": \"Nachtlauf Backup\",\n  \"cronExpression\": \"0 2 * * *\",\n  \"sourceType\": \"script\",\n  \"sourceId\": 3,\n  \"enabled\": true,\n  \"pushoverEnabled\": true\n}\n

Response: 201 mit { \"job\": { ... } }

"},{"location":"api/crons/#get-apicronsid","title":"GET /api/crons/:id","text":"

Response:

{ \"job\": { \"id\": 1, \"name\": \"...\" } }\n
"},{"location":"api/crons/#put-apicronsid","title":"PUT /api/crons/:id","text":"

Aktualisiert Cron-Job. Felder wie bei POST.

Response:

{ \"job\": { ... } }\n
"},{"location":"api/crons/#delete-apicronsid","title":"DELETE /api/crons/:id","text":"

Response:

{ \"removed\": { \"id\": 1, \"name\": \"Nachtlauf Backup\" } }\n
"},{"location":"api/crons/#get-apicronsidlogs","title":"GET /api/crons/:id/logs","text":"

Liefert Ausf\u00fchrungs-Logs.

Query-Parameter:

Parameter Typ Default Beschreibung limit number 20 Anzahl Eintr\u00e4ge, max. 100

Response:

{\n  \"logs\": [\n    {\n      \"id\": 42,\n      \"cronJobId\": 1,\n      \"startedAt\": \"2026-03-10T02:00:01.000Z\",\n      \"finishedAt\": \"2026-03-10T02:00:05.000Z\",\n      \"status\": \"success\",\n      \"output\": \"Backup abgeschlossen.\",\n      \"errorMessage\": null\n    }\n  ]\n}\n

status: running | success | error

"},{"location":"api/crons/#post-apicronsidrun","title":"POST /api/crons/:id/run","text":"

Triggert Job manuell (asynchron).

Response:

{ \"triggered\": true, \"cronJobId\": 1 }\n

Wenn Job bereits l\u00e4uft: 409.

"},{"location":"api/crons/#post-apicronsvalidate-expression","title":"POST /api/crons/validate-expression","text":"

Validiert 5-Felder-Cron-Ausdruck und berechnet n\u00e4chsten Lauf.

Request:

{ \"cronExpression\": \"*/15 * * * *\" }\n

G\u00fcltige Response:

{\n  \"valid\": true,\n  \"nextRunAt\": \"2026-03-10T14:15:00.000Z\"\n}\n

Ung\u00fcltige Response:

{\n  \"valid\": false,\n  \"error\": \"Cron-Ausdruck muss genau 5 Felder haben (Minute Stunde Tag Monat Wochentag).\",\n  \"nextRunAt\": null\n}\n
"},{"location":"api/crons/#cron-format","title":"Cron-Format","text":"

Ripster unterst\u00fctzt 5 Felder:

Minute Stunde Tag Monat Wochentag\n

Beispiele:

"},{"location":"api/crons/#websocket-events-zu-cron","title":"WebSocket-Events zu Cron","text":""},{"location":"api/history/","title":"History API","text":"

Endpunkte f\u00fcr Job-Historie, Orphan-Import und L\u00f6schoperationen.

"},{"location":"api/history/#get-apihistory","title":"GET /api/history","text":"

Liefert Jobs (optionale Filter).

Query-Parameter:

Parameter Typ Beschreibung status string Filter nach Job-Status search string Suche in Titel-Feldern

Beispiel:

GET /api/history?status=FINISHED&search=Inception\n

Response:

{\n  \"jobs\": [\n    {\n      \"id\": 42,\n      \"status\": \"FINISHED\",\n      \"title\": \"Inception\",\n      \"raw_path\": \"/mnt/raw/Inception - RAW - job-42\",\n      \"output_path\": \"/mnt/movies/Inception (2010)/Inception (2010).mkv\",\n      \"mediaType\": \"bluray\",\n      \"ripSuccessful\": true,\n      \"encodeSuccess\": true,\n      \"created_at\": \"2026-03-10T08:00:00.000Z\",\n      \"updated_at\": \"2026-03-10T10:00:00.000Z\"\n    }\n  ]\n}\n
"},{"location":"api/history/#get-apihistoryid","title":"GET /api/history/:id","text":"

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\u00e4ndiges Log statt Tail logTailLines number 800 Tail-L\u00e4nge falls nicht includeAllLogs

Response:

{\n  \"job\": {\n    \"id\": 42,\n    \"status\": \"FINISHED\",\n    \"makemkvInfo\": {},\n    \"mediainfoInfo\": {},\n    \"handbrakeInfo\": {},\n    \"encodePlan\": {},\n    \"log\": \"...\",\n    \"log_count\": 1,\n    \"logMeta\": {\n      \"loaded\": true,\n      \"total\": 800,\n      \"returned\": 800,\n      \"truncated\": true\n    }\n  }\n}\n
"},{"location":"api/history/#get-apihistorydatabase","title":"GET /api/history/database","text":"

Debug-Ansicht der DB-Zeilen (angereichert).

Response:

{\n  \"rows\": [\n    {\n      \"id\": 42,\n      \"status\": \"FINISHED\",\n      \"rawFolderName\": \"Inception - RAW - job-42\"\n    }\n  ]\n}\n
"},{"location":"api/history/#get-apihistoryorphan-raw","title":"GET /api/history/orphan-raw","text":"

Sucht RAW-Ordner ohne zugeh\u00f6rigen Job.

Response:

{\n  \"rawDir\": \"/mnt/raw\",\n  \"rawDirs\": [\"/mnt/raw\", \"/mnt/raw-bluray\"],\n  \"rows\": [\n    {\n      \"rawPath\": \"/mnt/raw/Inception (2010) [tt1375666] - RAW - job-99\",\n      \"folderName\": \"Inception (2010) [tt1375666] - RAW - job-99\",\n      \"title\": \"Inception\",\n      \"year\": 2010,\n      \"imdbId\": \"tt1375666\",\n      \"folderJobId\": 99,\n      \"entryCount\": 4,\n      \"hasBlurayStructure\": true,\n      \"lastModifiedAt\": \"2026-03-10T09:00:00.000Z\"\n    }\n  ]\n}\n
"},{"location":"api/history/#post-apihistoryorphan-rawimport","title":"POST /api/history/orphan-raw/import","text":"

Importiert RAW-Ordner als FINISHED-Job.

Request:

{ \"rawPath\": \"/mnt/raw/Inception (2010) [tt1375666] - RAW - job-99\" }\n

Response:

{\n  \"job\": { \"id\": 77, \"status\": \"FINISHED\" },\n  \"uiReset\": { \"reset\": true, \"state\": \"IDLE\" }\n}\n
"},{"location":"api/history/#post-apihistoryidomdbassign","title":"POST /api/history/:id/omdb/assign","text":"

Weist OMDb-/Metadaten nachtr\u00e4glich zu.

Request:

{\n  \"imdbId\": \"tt1375666\",\n  \"title\": \"Inception\",\n  \"year\": 2010,\n  \"poster\": \"https://...\",\n  \"fromOmdb\": true\n}\n

Response:

{ \"job\": { \"id\": 42, \"imdb_id\": \"tt1375666\" } }\n
"},{"location":"api/history/#post-apihistoryiddelete-files","title":"POST /api/history/:id/delete-files","text":"

L\u00f6scht Dateien eines Jobs, beh\u00e4lt DB-Eintrag.

Request:

{ \"target\": \"both\" }\n

target: raw | movie | both

Response:

{\n  \"summary\": {\n    \"target\": \"both\",\n    \"raw\": { \"attempted\": true, \"deleted\": true, \"filesDeleted\": 12, \"dirsRemoved\": 3, \"reason\": null },\n    \"movie\": { \"attempted\": true, \"deleted\": false, \"filesDeleted\": 0, \"dirsRemoved\": 0, \"reason\": \"Movie-Datei/Pfad existiert nicht.\" }\n  },\n  \"job\": { \"id\": 42 }\n}\n
"},{"location":"api/history/#post-apihistoryiddelete","title":"POST /api/history/:id/delete","text":"

L\u00f6scht Job aus DB; optional auch Dateien.

Request:

{ \"target\": \"none\" }\n

target: none | raw | movie | both

Response:

{\n  \"deleted\": true,\n  \"jobId\": 42,\n  \"fileTarget\": \"both\",\n  \"fileSummary\": {\n    \"target\": \"both\",\n    \"raw\": { \"filesDeleted\": 10 },\n    \"movie\": { \"filesDeleted\": 1 }\n  },\n  \"uiReset\": {\n    \"reset\": true,\n    \"state\": \"IDLE\"\n  }\n}\n
"},{"location":"api/history/#hinweise","title":"Hinweise","text":""},{"location":"api/pipeline/","title":"Pipeline API","text":"

Endpunkte zur Steuerung des Pipeline-Workflows.

"},{"location":"api/pipeline/#get-apipipelinestate","title":"GET /api/pipeline/state","text":"

Liefert aktuellen Pipeline- und Hardware-Monitoring-Snapshot.

Response (Beispiel):

{\n  \"pipeline\": {\n    \"state\": \"READY_TO_ENCODE\",\n    \"activeJobId\": 42,\n    \"progress\": 0,\n    \"eta\": null,\n    \"statusText\": \"Mediainfo best\u00e4tigt - Encode manuell starten\",\n    \"context\": {\n      \"jobId\": 42\n    },\n    \"jobProgress\": {\n      \"42\": {\n        \"state\": \"MEDIAINFO_CHECK\",\n        \"progress\": 68.5,\n        \"eta\": null,\n        \"statusText\": \"MEDIAINFO_CHECK 68.50%\"\n      }\n    },\n    \"queue\": {\n      \"maxParallelJobs\": 1,\n      \"runningCount\": 1,\n      \"queuedCount\": 2,\n      \"runningJobs\": [],\n      \"queuedJobs\": []\n    }\n  },\n  \"hardwareMonitoring\": {\n    \"enabled\": true,\n    \"intervalMs\": 5000,\n    \"updatedAt\": \"2026-03-10T09:00:00.000Z\",\n    \"sample\": {\n      \"cpu\": {},\n      \"memory\": {},\n      \"gpu\": {},\n      \"storage\": {}\n    },\n    \"error\": null\n  }\n}\n
"},{"location":"api/pipeline/#post-apipipelineanalyze","title":"POST /api/pipeline/analyze","text":"

Startet Disc-Analyse und legt Job an.

Response:

{\n  \"result\": {\n    \"jobId\": 42,\n    \"detectedTitle\": \"INCEPTION\",\n    \"omdbCandidates\": []\n  }\n}\n
"},{"location":"api/pipeline/#post-apipipelinerescan-disc","title":"POST /api/pipeline/rescan-disc","text":"

Erzwingt erneute Laufwerkspr\u00fcfung.

Response (Beispiel):

{\n  \"result\": {\n    \"present\": true,\n    \"changed\": true,\n    \"emitted\": \"discInserted\",\n    \"device\": {\n      \"path\": \"/dev/sr0\",\n      \"discLabel\": \"INCEPTION\",\n      \"mediaProfile\": \"bluray\"\n    }\n  }\n}\n
"},{"location":"api/pipeline/#get-apipipelineomdbsearchq","title":"GET /api/pipeline/omdb/search?q=

OMDb-Titelsuche.

Response:

{\n  \"results\": [\n    {\n      \"imdbId\": \"tt1375666\",\n      \"title\": \"Inception\",\n      \"year\": \"2010\",\n      \"type\": \"movie\",\n      \"poster\": \"https://...\"\n    }\n  ]\n}\n
","text":""},{"location":"api/pipeline/#post-apipipelineselect-metadata","title":"POST /api/pipeline/select-metadata

Setzt Metadaten (und optional Playlist) f\u00fcr einen Job.

Request:

{\n  \"jobId\": 42,\n  \"title\": \"Inception\",\n  \"year\": 2010,\n  \"imdbId\": \"tt1375666\",\n  \"poster\": \"https://...\",\n  \"fromOmdb\": true,\n  \"selectedPlaylist\": \"00800\"\n}\n

Response:

{ \"job\": { \"id\": 42, \"status\": \"READY_TO_START\" } }\n
","text":""},{"location":"api/pipeline/#post-apipipelinestartjobid","title":"POST /api/pipeline/start/:jobId

Startet vorbereiteten Job oder queued ihn (je nach Parallel-Limit).

M\u00f6gliche Responses:

{ \"result\": { \"started\": true, \"stage\": \"RIPPING\" } }\n
{ \"result\": { \"queued\": true, \"started\": false, \"queuePosition\": 2, \"action\": \"START_PREPARED\" } }\n
","text":""},{"location":"api/pipeline/#post-apipipelineconfirm-encodejobid","title":"POST /api/pipeline/confirm-encode/:jobId

Best\u00e4tigt Review-Auswahl (Tracks, Pre/Post-Skripte/Ketten, User-Preset).

Request (typisch):

{\n  \"selectedEncodeTitleId\": 1,\n  \"selectedTrackSelection\": {\n    \"1\": {\n      \"audioTrackIds\": [1, 2],\n      \"subtitleTrackIds\": [3]\n    }\n  },\n  \"selectedPreEncodeScriptIds\": [1],\n  \"selectedPostEncodeScriptIds\": [2, 7],\n  \"selectedPreEncodeChainIds\": [3],\n  \"selectedPostEncodeChainIds\": [4],\n  \"selectedUserPresetId\": 5,\n  \"skipPipelineStateUpdate\": false\n}\n

Response:

{ \"job\": { \"id\": 42, \"encode_review_confirmed\": 1 } }\n
","text":""},{"location":"api/pipeline/#post-apipipelinecancel","title":"POST /api/pipeline/cancel

Bricht laufenden Job ab oder entfernt Queue-Eintrag.

Request (optional):

{ \"jobId\": 42 }\n

M\u00f6gliche Responses:

{ \"result\": { \"cancelled\": true, \"queuedOnly\": true, \"jobId\": 42 } }\n
{ \"result\": { \"cancelled\": true, \"queuedOnly\": false, \"jobId\": 42 } }\n
{ \"result\": { \"cancelled\": true, \"queuedOnly\": false, \"pending\": true, \"jobId\": 42 } }\n
","text":""},{"location":"api/pipeline/#post-apipipelineretryjobid","title":"POST /api/pipeline/retry/:jobId

Retry f\u00fcr ERROR/CANCELLED-Jobs (oder Queue-Einreihung).

","text":""},{"location":"api/pipeline/#post-apipipelinereencodejobid","title":"POST /api/pipeline/reencode/:jobId

Startet Re-Encode aus bestehendem RAW.

","text":""},{"location":"api/pipeline/#post-apipipelinerestart-reviewjobid","title":"POST /api/pipeline/restart-review/:jobId

Berechnet Review aus RAW neu.

","text":""},{"location":"api/pipeline/#post-apipipelinerestart-encodejobid","title":"POST /api/pipeline/restart-encode/:jobId

Startet Encoding mit letzter best\u00e4tigter Review neu.

","text":""},{"location":"api/pipeline/#post-apipipelineresume-readyjobid","title":"POST /api/pipeline/resume-ready/:jobId

L\u00e4dt READY_TO_ENCODE-Job nach Neustart wieder in aktive Session.

Alle Endpunkte liefern { result: ... } bzw. { job: ... }.

","text":""},{"location":"api/pipeline/#queue-endpunkte","title":"Queue-Endpunkte","text":""},{"location":"api/pipeline/#get-apipipelinequeue","title":"GET /api/pipeline/queue","text":"

Liefert Queue-Snapshot.

{\n  \"queue\": {\n    \"maxParallelJobs\": 1,\n    \"runningCount\": 1,\n    \"queuedCount\": 3,\n    \"runningJobs\": [\n      {\n        \"jobId\": 41,\n        \"title\": \"Inception\",\n        \"status\": \"ENCODING\",\n        \"lastState\": \"ENCODING\"\n      }\n    ],\n    \"queuedJobs\": [\n      {\n        \"entryId\": 11,\n        \"position\": 1,\n        \"type\": \"job\",\n        \"jobId\": 42,\n        \"action\": \"START_PREPARED\",\n        \"actionLabel\": \"Start\",\n        \"title\": \"Matrix\",\n        \"status\": \"READY_TO_ENCODE\",\n        \"lastState\": \"READY_TO_ENCODE\",\n        \"hasScripts\": true,\n        \"hasChains\": false,\n        \"enqueuedAt\": \"2026-03-10T09:00:00.000Z\"\n      },\n      {\n        \"entryId\": 12,\n        \"position\": 2,\n        \"type\": \"wait\",\n        \"waitSeconds\": 30,\n        \"title\": \"Warten 30s\",\n        \"status\": \"QUEUED\",\n        \"enqueuedAt\": \"2026-03-10T09:01:00.000Z\"\n      }\n    ],\n    \"updatedAt\": \"2026-03-10T09:01:02.000Z\"\n  }\n}\n
"},{"location":"api/pipeline/#post-apipipelinequeuereorder","title":"POST /api/pipeline/queue/reorder","text":"

Sortiert Queue-Eintr\u00e4ge neu.

Request:

{\n  \"orderedEntryIds\": [12, 11]\n}\n

Legacy fallback wird akzeptiert:

{\n  \"orderedJobIds\": [42, 43]\n}\n
"},{"location":"api/pipeline/#post-apipipelinequeueentry","title":"POST /api/pipeline/queue/entry","text":"

F\u00fcgt Nicht-Job-Queue-Eintrag hinzu (script, chain, wait).

Request-Beispiele:

{ \"type\": \"script\", \"scriptId\": 3 }\n
{ \"type\": \"chain\", \"chainId\": 2, \"insertAfterEntryId\": 11 }\n
{ \"type\": \"wait\", \"waitSeconds\": 45 }\n

Response:

{\n  \"result\": { \"entryId\": 12, \"type\": \"wait\", \"position\": 2 },\n  \"queue\": { \"...\": \"...\" }\n}\n
"},{"location":"api/pipeline/#delete-apipipelinequeueentryentryid","title":"DELETE /api/pipeline/queue/entry/:entryId","text":"

Entfernt Queue-Eintrag.

Response:

{ \"queue\": { \"...\": \"...\" } }\n
"},{"location":"api/pipeline/#pipeline-zustande","title":"Pipeline-Zust\u00e4nde State Bedeutung IDLE Wartet auf Medium DISC_DETECTED Medium erkannt ANALYZING MakeMKV-Analyse l\u00e4uft METADATA_SELECTION Metadaten-Auswahl WAITING_FOR_USER_DECISION Playlist-Entscheidung erforderlich READY_TO_START \u00dcbergang vor Start RIPPING MakeMKV-Rip l\u00e4uft MEDIAINFO_CHECK Titel-/Track-Auswertung READY_TO_ENCODE Review bereit ENCODING HandBrake-Encoding l\u00e4uft FINISHED Abgeschlossen CANCELLED Abgebrochen ERROR Fehler","text":""},{"location":"api/settings/","title":"Settings API","text":"

Endpunkte f\u00fcr Einstellungen, Skripte, Skript-Ketten und User-Presets.

"},{"location":"api/settings/#get-apisettings","title":"GET /api/settings","text":"

Liefert alle Einstellungen kategorisiert.

Response (Struktur):

{\n  \"categories\": [\n    {\n      \"category\": \"Pfade\",\n      \"settings\": [\n        {\n          \"key\": \"raw_dir\",\n          \"label\": \"Raw Ausgabeordner\",\n          \"type\": \"path\",\n          \"required\": true,\n          \"description\": \"...\",\n          \"defaultValue\": \"data/output/raw\",\n          \"options\": [],\n          \"validation\": { \"minLength\": 1 },\n          \"value\": \"data/output/raw\",\n          \"orderIndex\": 100\n        }\n      ]\n    }\n  ]\n}\n
"},{"location":"api/settings/#put-apisettingskey","title":"PUT /api/settings/:key","text":"

Aktualisiert eine einzelne Einstellung.

Request:

{ \"value\": \"/mnt/storage/raw\" }\n

Response:

{\n  \"setting\": {\n    \"key\": \"raw_dir\",\n    \"value\": \"/mnt/storage/raw\"\n  },\n  \"reviewRefresh\": {\n    \"triggered\": false,\n    \"reason\": \"not_ready\"\n  }\n}\n

reviewRefresh ist null oder ein Objekt mit Status der optionalen Review-Neuberechnung.

"},{"location":"api/settings/#put-apisettings","title":"PUT /api/settings","text":"

Aktualisiert mehrere Einstellungen atomar.

Request:

{\n  \"settings\": {\n    \"raw_dir\": \"/mnt/storage/raw\",\n    \"movie_dir\": \"/mnt/storage/movies\",\n    \"handbrake_preset_bluray\": \"H.264 MKV 1080p30\"\n  }\n}\n

Response:

{\n  \"changes\": [\n    { \"key\": \"raw_dir\", \"value\": \"/mnt/storage/raw\" },\n    { \"key\": \"movie_dir\", \"value\": \"/mnt/storage/movies\" }\n  ],\n  \"reviewRefresh\": {\n    \"triggered\": true,\n    \"jobId\": 42,\n    \"relevantKeys\": [\"handbrake_preset_bluray\"]\n  }\n}\n

Bei Validierungsfehlern kommt 400 mit error.details[].

"},{"location":"api/settings/#get-apisettingshandbrake-presets","title":"GET /api/settings/handbrake-presets","text":"

Liest Preset-Liste via HandBrakeCLI -z (mit Fallback auf konfigurierte Presets).

Response (Beispiel):

{\n  \"source\": \"handbrake-cli\",\n  \"message\": null,\n  \"options\": [\n    { \"label\": \"General/\", \"value\": \"__group__general\", \"disabled\": true, \"category\": \"General\" },\n    { \"label\": \"   Fast 1080p30\", \"value\": \"Fast 1080p30\", \"category\": \"General\" }\n  ]\n}\n
"},{"location":"api/settings/#post-apisettingspushovertest","title":"POST /api/settings/pushover/test","text":"

Sendet Testnachricht \u00fcber aktuelle PushOver-Settings.

Request (optional):

{\n  \"title\": \"Test\",\n  \"message\": \"Ripster Test\"\n}\n

Response:

{\n  \"result\": {\n    \"sent\": true,\n    \"eventKey\": \"test\",\n    \"requestId\": \"...\"\n  }\n}\n

Wenn PushOver deaktiviert ist oder Credentials fehlen, kommt i. d. R. ebenfalls 200 mit sent: false + reason.

"},{"location":"api/settings/#skripte","title":"Skripte","text":"

Basis: /api/settings/scripts

"},{"location":"api/settings/#get-apisettingsscripts","title":"GET /api/settings/scripts","text":"
{ \"scripts\": [ { \"id\": 1, \"name\": \"...\", \"scriptBody\": \"...\", \"orderIndex\": 1, \"createdAt\": \"...\", \"updatedAt\": \"...\" } ] }\n
"},{"location":"api/settings/#post-apisettingsscripts","title":"POST /api/settings/scripts","text":"
{ \"name\": \"Move\", \"scriptBody\": \"mv \\\"$RIPSTER_OUTPUT_PATH\\\" /mnt/movies/\" }\n

Response: 201 mit { \"script\": { ... } }

"},{"location":"api/settings/#put-apisettingsscriptsid","title":"PUT /api/settings/scripts/:id","text":"

Body wie POST, Response { \"script\": { ... } }.

"},{"location":"api/settings/#delete-apisettingsscriptsid","title":"DELETE /api/settings/scripts/:id","text":"

Response { \"removed\": { ... } }.

"},{"location":"api/settings/#post-apisettingsscriptsreorder","title":"POST /api/settings/scripts/reorder","text":"
{ \"orderedScriptIds\": [3, 1, 2] }\n

Response { \"scripts\": [ ... ] }.

"},{"location":"api/settings/#post-apisettingsscriptsidtest","title":"POST /api/settings/scripts/:id/test","text":"

F\u00fchrt Skript als Testlauf aus.

{\n  \"result\": {\n    \"scriptId\": 1,\n    \"scriptName\": \"Move\",\n    \"success\": true,\n    \"exitCode\": 0,\n    \"signal\": null,\n    \"timedOut\": false,\n    \"durationMs\": 120,\n    \"stdout\": \"...\",\n    \"stderr\": \"...\",\n    \"stdoutTruncated\": false,\n    \"stderrTruncated\": false\n  }\n}\n
"},{"location":"api/settings/#umgebungsvariablen-fur-skripte","title":"Umgebungsvariablen f\u00fcr Skripte","text":"

Diese Variablen werden beim Ausf\u00fchren gesetzt:

"},{"location":"api/settings/#skript-ketten","title":"Skript-Ketten","text":"

Basis: /api/settings/script-chains

Eine Kette hat Schritte vom Typ:

"},{"location":"api/settings/#get-apisettingsscript-chains","title":"GET /api/settings/script-chains","text":"

Response { \"chains\": [ ... ] } (inkl. steps[]).

"},{"location":"api/settings/#get-apisettingsscript-chainsid","title":"GET /api/settings/script-chains/:id","text":"

Response { \"chain\": { ... } }.

"},{"location":"api/settings/#post-apisettingsscript-chains","title":"POST /api/settings/script-chains","text":"
{\n  \"name\": \"After Encode\",\n  \"steps\": [\n    { \"stepType\": \"script\", \"scriptId\": 1 },\n    { \"stepType\": \"wait\", \"waitSeconds\": 15 },\n    { \"stepType\": \"script\", \"scriptId\": 2 }\n  ]\n}\n

Response: 201 mit { \"chain\": { ... } }

"},{"location":"api/settings/#put-apisettingsscript-chainsid","title":"PUT /api/settings/script-chains/:id","text":"

Body wie POST, Response { \"chain\": { ... } }.

"},{"location":"api/settings/#delete-apisettingsscript-chainsid","title":"DELETE /api/settings/script-chains/:id","text":"

Response { \"removed\": { ... } }.

"},{"location":"api/settings/#post-apisettingsscript-chainsreorder","title":"POST /api/settings/script-chains/reorder","text":"
{ \"orderedChainIds\": [2, 1, 3] }\n

Response { \"chains\": [ ... ] }.

"},{"location":"api/settings/#post-apisettingsscript-chainsidtest","title":"POST /api/settings/script-chains/:id/test","text":"

Response:

{\n  \"result\": {\n    \"chainId\": 2,\n    \"chainName\": \"After Encode\",\n    \"steps\": 3,\n    \"succeeded\": 3,\n    \"failed\": 0,\n    \"aborted\": false,\n    \"results\": []\n  }\n}\n
"},{"location":"api/settings/#user-presets","title":"User-Presets","text":"

Basis: /api/settings/user-presets

"},{"location":"api/settings/#get-apisettingsuser-presets","title":"GET /api/settings/user-presets","text":"

Optionaler Query-Parameter: media_type=bluray|dvd|other|all

{\n  \"presets\": [\n    {\n      \"id\": 1,\n      \"name\": \"Blu-ray HQ\",\n      \"mediaType\": \"bluray\",\n      \"handbrakePreset\": \"H.264 MKV 1080p30\",\n      \"extraArgs\": \"--encoder-preset slow\",\n      \"description\": \"...\",\n      \"createdAt\": \"...\",\n      \"updatedAt\": \"...\"\n    }\n  ]\n}\n
"},{"location":"api/settings/#post-apisettingsuser-presets","title":"POST /api/settings/user-presets","text":"
{\n  \"name\": \"Blu-ray HQ\",\n  \"mediaType\": \"bluray\",\n  \"handbrakePreset\": \"H.264 MKV 1080p30\",\n  \"extraArgs\": \"--encoder-preset slow\",\n  \"description\": \"optional\"\n}\n

Response: 201 mit { \"preset\": { ... } }

"},{"location":"api/settings/#put-apisettingsuser-presetsid","title":"PUT /api/settings/user-presets/:id","text":"

Body mit beliebigen Feldern aus POST, Response { \"preset\": { ... } }.

"},{"location":"api/settings/#delete-apisettingsuser-presetsid","title":"DELETE /api/settings/user-presets/:id","text":"

Response { \"removed\": { ... } }.

"},{"location":"api/websocket/","title":"WebSocket Events","text":"

Ripster sendet Echtzeit-Updates \u00fcber /ws.

"},{"location":"api/websocket/#verbindung","title":"Verbindung","text":"
const ws = new WebSocket('ws://localhost:3001/ws');\n\nws.onmessage = (event) => {\n  const msg = JSON.parse(event.data);\n  console.log(msg.type, msg.payload);\n};\n
"},{"location":"api/websocket/#nachrichtenformat","title":"Nachrichtenformat","text":"

Die meisten Broadcasts haben dieses Schema:

{\n  \"type\": \"EVENT_TYPE\",\n  \"payload\": {},\n  \"timestamp\": \"2026-03-10T09:00:00.000Z\"\n}\n

Ausnahme: WS_CONNECTED beim Verbindungsaufbau enth\u00e4lt kein timestamp.

"},{"location":"api/websocket/#event-typen","title":"Event-Typen","text":""},{"location":"api/websocket/#ws_connected","title":"WS_CONNECTED","text":"

Sofort nach erfolgreicher Verbindung.

{\n  \"type\": \"WS_CONNECTED\",\n  \"payload\": {\n    \"connectedAt\": \"2026-03-10T09:00:00.000Z\"\n  }\n}\n
"},{"location":"api/websocket/#pipeline_state_changed","title":"PIPELINE_STATE_CHANGED","text":"

Neuer Pipeline-Snapshot.

{\n  \"type\": \"PIPELINE_STATE_CHANGED\",\n  \"payload\": {\n    \"state\": \"ENCODING\",\n    \"activeJobId\": 42,\n    \"progress\": 62.5,\n    \"eta\": \"00:12:34\",\n    \"statusText\": \"ENCODING 62.50%\",\n    \"context\": {},\n    \"jobProgress\": {\n      \"42\": {\n        \"state\": \"ENCODING\",\n        \"progress\": 62.5,\n        \"eta\": \"00:12:34\",\n        \"statusText\": \"ENCODING 62.50%\"\n      }\n    },\n    \"queue\": {\n      \"maxParallelJobs\": 1,\n      \"runningCount\": 1,\n      \"queuedCount\": 2,\n      \"runningJobs\": [],\n      \"queuedJobs\": []\n    }\n  }\n}\n
"},{"location":"api/websocket/#pipeline_progress","title":"PIPELINE_PROGRESS","text":"

Laufende Fortschrittsupdates.

{\n  \"type\": \"PIPELINE_PROGRESS\",\n  \"payload\": {\n    \"state\": \"ENCODING\",\n    \"activeJobId\": 42,\n    \"progress\": 62.5,\n    \"eta\": \"00:12:34\",\n    \"statusText\": \"ENCODING 62.50%\"\n  }\n}\n
"},{"location":"api/websocket/#pipeline_queue_changed","title":"PIPELINE_QUEUE_CHANGED","text":"

Queue-Snapshot aktualisiert.

"},{"location":"api/websocket/#disc_detected-disc_removed","title":"DISC_DETECTED / DISC_REMOVED","text":"

Disc-Insertion/-Removal.

{\n  \"type\": \"DISC_DETECTED\",\n  \"payload\": {\n    \"device\": {\n      \"path\": \"/dev/sr0\",\n      \"discLabel\": \"INCEPTION\",\n      \"model\": \"ASUS BW-16D1HT\",\n      \"fstype\": \"udf\",\n      \"mountpoint\": null,\n      \"mediaProfile\": \"bluray\"\n    }\n  }\n}\n

mediaProfile: bluray | dvd | other | null

"},{"location":"api/websocket/#hardware_monitor_update","title":"HARDWARE_MONITOR_UPDATE","text":"

Snapshot aus Hardware-Monitoring.

{\n  \"type\": \"HARDWARE_MONITOR_UPDATE\",\n  \"payload\": {\n    \"enabled\": true,\n    \"intervalMs\": 5000,\n    \"updatedAt\": \"2026-03-10T09:00:00.000Z\",\n    \"sample\": {\n      \"cpu\": {},\n      \"memory\": {},\n      \"gpu\": {},\n      \"storage\": {}\n    },\n    \"error\": null\n  }\n}\n
"},{"location":"api/websocket/#pipeline_error","title":"PIPELINE_ERROR","text":"

Fehler bei Disc-Event-Verarbeitung in Pipeline.

"},{"location":"api/websocket/#disk_detection_error","title":"DISK_DETECTION_ERROR","text":"

Fehler in Laufwerkserkennung.

"},{"location":"api/websocket/#settings_updated","title":"SETTINGS_UPDATED","text":"

Einzelnes Setting wurde gespeichert.

"},{"location":"api/websocket/#settings_bulk_updated","title":"SETTINGS_BULK_UPDATED","text":"

Bulk-Settings gespeichert.

{\n  \"type\": \"SETTINGS_BULK_UPDATED\",\n  \"payload\": {\n    \"count\": 3,\n    \"keys\": [\"raw_dir\", \"movie_dir\", \"handbrake_preset_bluray\"]\n  }\n}\n
"},{"location":"api/websocket/#settings_scripts_updated","title":"SETTINGS_SCRIPTS_UPDATED","text":"

Skript ge\u00e4ndert (created|updated|deleted|reordered).

"},{"location":"api/websocket/#settings_script_chains_updated","title":"SETTINGS_SCRIPT_CHAINS_UPDATED","text":"

Skript-Kette ge\u00e4ndert (created|updated|deleted|reordered).

"},{"location":"api/websocket/#user_presets_updated","title":"USER_PRESETS_UPDATED","text":"

User-Preset ge\u00e4ndert (created|updated|deleted).

"},{"location":"api/websocket/#cron_jobs_updated","title":"CRON_JOBS_UPDATED","text":"

Cron-Config ge\u00e4ndert (created|updated|deleted).

"},{"location":"api/websocket/#cron_job_updated","title":"CRON_JOB_UPDATED","text":"

Laufzeitstatus eines Cron-Jobs ge\u00e4ndert.

{\n  \"type\": \"CRON_JOB_UPDATED\",\n  \"payload\": {\n    \"id\": 1,\n    \"lastRunStatus\": \"running\",\n    \"lastRunAt\": \"2026-03-10T10:00:00.000Z\",\n    \"nextRunAt\": null\n  }\n}\n
"},{"location":"api/websocket/#reconnect-verhalten","title":"Reconnect-Verhalten","text":"

useWebSocket verbindet bei Abbruch automatisch neu:

"},{"location":"architecture/","title":"Architektur","text":"

Ripster ist eine Client-Server-Anwendung mit REST + WebSocket.

"},{"location":"architecture/#systemuberblick","title":"System\u00fcberblick","text":"
graph TB\n    subgraph Browser[\"Browser (React)\"]\n        Dashboard[Dashboard]\n        Settings[Einstellungen]\n        History[Historie]\n    end\n\n    subgraph Backend[\"Node.js Backend\"]\n        API[REST API\\nExpress]\n        WS[WebSocket\\n/ws]\n        Pipeline[pipelineService]\n        Cron[cronService]\n        DB[(SQLite)]\n    end\n\n    subgraph Tools[\"Externe Tools\"]\n        MakeMKV[makemkvcon]\n        HandBrake[HandBrakeCLI]\n        MediaInfo[mediainfo]\n    end\n\n    Browser <-->|HTTP| API\n    Browser <-->|WebSocket| WS\n    Pipeline --> MakeMKV\n    Pipeline --> HandBrake\n    Pipeline --> MediaInfo\n    API --> DB\n    Pipeline --> DB\n    Cron --> DB
"},{"location":"architecture/#schichten","title":"Schichten","text":""},{"location":"architecture/#backend","title":"Backend","text":""},{"location":"architecture/#frontend","title":"Frontend","text":""},{"location":"architecture/#weiterfuhrend","title":"Weiterf\u00fchrend","text":""},{"location":"architecture/backend/","title":"Backend-Services","text":"

Das Backend ist in Services aufgeteilt, die von Express-Routen orchestriert werden.

"},{"location":"architecture/backend/#pipelineservicejs","title":"pipelineService.js","text":"

Zentrale Workflow-Orchestrierung.

Aufgaben:

Wichtige Methoden:

"},{"location":"architecture/backend/#diskdetectionservicejs","title":"diskDetectionService.js","text":"

Pollt Laufwerk(e) und emittiert:

Zusatz:

"},{"location":"architecture/backend/#settingsservicejs","title":"settingsService.js","text":"

Settings-Layer mit Validation/Serialisierung.

Features:

"},{"location":"architecture/backend/#historyservicejs","title":"historyService.js","text":"

Historie + Dateioperationen.

Features:

"},{"location":"architecture/backend/#cronservicejs","title":"cronService.js","text":"

Integriertes Cron-System ohne externe Parser-Library.

Features:

"},{"location":"architecture/backend/#weitere-services","title":"Weitere Services","text":""},{"location":"architecture/backend/#bootstrapping-srcindexjs","title":"Bootstrapping (src/index.js)","text":"

Beim Start:

  1. DB init/migrate
  2. Pipeline-Init
  3. Cron-Init
  4. Express-Routes + Error-Handler
  5. WebSocket-Server auf /ws
  6. Hardware-Monitoring-Init
  7. Disk-Detection-Start
"},{"location":"architecture/database/","title":"Datenbank","text":"

Ripster verwendet SQLite (backend/data/ripster.db).

"},{"location":"architecture/database/#tabellen","title":"Tabellen","text":"
settings_schema\nsettings_values\njobs\npipeline_state\nscripts\nscript_chains\nscript_chain_steps\nuser_presets\ncron_jobs\ncron_run_logs\n
"},{"location":"architecture/database/#jobs","title":"jobs","text":"

Speichert Pipeline-Lifecycle und Artefakte pro Job.

Zentrale Felder:

"},{"location":"architecture/database/#pipeline_state","title":"pipeline_state","text":"

Singleton-Tabelle (id = 1) f\u00fcr aktiven Snapshot:

"},{"location":"architecture/database/#settings_schema-settings_values","title":"settings_schema + settings_values","text":""},{"location":"architecture/database/#scripts-script_chains-script_chain_steps","title":"scripts, script_chains, script_chain_steps","text":""},{"location":"architecture/database/#user_presets","title":"user_presets","text":"

Benannte HandBrake-Preset-Sets:

"},{"location":"architecture/database/#cron_jobs-cron_run_logs","title":"cron_jobs + cron_run_logs","text":""},{"location":"architecture/database/#migrationrecovery","title":"Migration/Recovery","text":"

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
"},{"location":"architecture/database/#direkte-inspektion","title":"Direkte Inspektion","text":"
sqlite3 backend/data/ripster.db\n\n.mode table\nSELECT id, status, title, created_at FROM jobs ORDER BY created_at DESC;\nSELECT key, value FROM settings_values ORDER BY key;\n
"},{"location":"architecture/frontend/","title":"Frontend-Komponenten","text":"

Frontend: React + PrimeReact + Vite.

"},{"location":"architecture/frontend/#hauptseiten","title":"Hauptseiten","text":""},{"location":"architecture/frontend/#dashboardpagejsx","title":"DashboardPage.jsx","text":"

Pipeline-Steuerung:

"},{"location":"architecture/frontend/#settingspagejsx","title":"SettingsPage.jsx","text":"

Konfiguration:

"},{"location":"architecture/frontend/#historypagejsx","title":"HistoryPage.jsx","text":"

Historie:

"},{"location":"architecture/frontend/#wichtige-komponenten","title":"Wichtige Komponenten","text":""},{"location":"architecture/frontend/#api-client-apiclientjs","title":"API-Client (api/client.js)","text":""},{"location":"architecture/frontend/#websocket-hooksusewebsocketjs","title":"WebSocket (hooks/useWebSocket.js)","text":"

In App.jsx werden u. a. verarbeitet:

"},{"location":"architecture/frontend/#buildrun","title":"Build/Run","text":"
# dev\nnpm run dev --prefix frontend\n\n# prod build\nnpm run build --prefix frontend\n
"},{"location":"architecture/overview/","title":"Architektur-\u00dcbersicht","text":""},{"location":"architecture/overview/#kernprinzipien","title":"Kernprinzipien","text":""},{"location":"architecture/overview/#event-getriebene-pipeline","title":"Event-getriebene Pipeline","text":"

pipelineService h\u00e4lt einen Snapshot der State-Machine und broadcastet \u00c4nderungen sofort via WebSocket.

State-\u00c4nderung -> PIPELINE_STATE_CHANGED/PIPELINE_PROGRESS -> Frontend-Update\n
"},{"location":"architecture/overview/#service-layer","title":"Service-Layer","text":"
Route -> Service -> DB/Tool-Execution\n

Routes enthalten kaum Business-Logik.

"},{"location":"architecture/overview/#schema-getriebene-settings","title":"Schema-getriebene Settings","text":"

Settings sind DB-schema-getrieben (settings_schema + settings_values), UI rendert dynamisch aus diesen Daten.

"},{"location":"architecture/overview/#echtzeit-kommunikation","title":"Echtzeit-Kommunikation","text":"

WebSocket l\u00e4uft auf /ws.

Wichtige Events:

"},{"location":"architecture/overview/#prozessausfuhrung","title":"Prozessausf\u00fchrung","text":"

Externe Tools werden als Child-Processes gestartet (processRunner):

"},{"location":"architecture/overview/#persistenz","title":"Persistenz","text":"

SQLite-Datei: backend/data/ripster.db

Kern-Tabellen:

Beim Start werden Schema und Settings-Migrationen automatisch ausgef\u00fchrt.

"},{"location":"architecture/overview/#fehlerbehandlung","title":"Fehlerbehandlung","text":"

Zentrales Error-Handling liefert:

{\n  \"error\": {\n    \"message\": \"...\",\n    \"statusCode\": 400,\n    \"reqId\": \"...\",\n    \"details\": []\n  }\n}\n

Fehlgeschlagene Jobs bleiben in der Historie (ERROR oder CANCELLED) und k\u00f6nnen erneut gestartet werden.

"},{"location":"architecture/overview/#cors-runtime-konfig","title":"CORS & Runtime-Konfig","text":""},{"location":"configuration/","title":"Konfiguration","text":""},{"location":"configuration/environment/","title":"Umgebungsvariablen","text":"

Umgebungsvariablen steuern Backend/Vite au\u00dferhalb der DB-basierten UI-Settings.

"},{"location":"configuration/environment/#backend-backendenv","title":"Backend (backend/.env)","text":"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\u00fcr API LOG_LEVEL info debug, info, warn, error

Beispiel:

PORT=3001\nDB_PATH=/var/lib/ripster/ripster.db\nLOG_DIR=/var/log/ripster\nCORS_ORIGIN=http://192.168.1.50:5173\nLOG_LEVEL=info\n

Hinweis: backend/.env.example enth\u00e4lt bewusst dev-freundliche Werte (z. B. lokaler CORS_ORIGIN).

"},{"location":"configuration/environment/#frontend-frontendenv","title":"Frontend (frontend/.env)","text":"Variable Default Beschreibung VITE_API_BASE /api API-Basis f\u00fcr Fetch-Client VITE_WS_URL automatisch aus window.location + /ws Optional explizite WebSocket-URL VITE_PUBLIC_ORIGIN leer \u00d6ffentliche Vite-Origin (Remote-Dev) VITE_ALLOWED_HOSTS true Komma-separierte Hostliste f\u00fcr 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:

# lokal (mit Vite-Proxy)\nVITE_API_BASE=/api\n
# remote dev\nVITE_API_BASE=http://192.168.1.50:3001/api\nVITE_WS_URL=ws://192.168.1.50:3001/ws\nVITE_PUBLIC_ORIGIN=http://192.168.1.50:5173\nVITE_ALLOWED_HOSTS=192.168.1.50,ripster.local\nVITE_HMR_PROTOCOL=ws\nVITE_HMR_HOST=192.168.1.50\nVITE_HMR_CLIENT_PORT=5173\n
"},{"location":"configuration/environment/#prioritat","title":"Priorit\u00e4t","text":"
  1. Prozess-Umgebungsvariablen
  2. .env
  3. Code-Defaults
"},{"location":"configuration/settings-reference/","title":"Einstellungsreferenz","text":"

Alle Settings liegen in settings_schema/settings_values und werden \u00fcber die UI verwaltet.

"},{"location":"configuration/settings-reference/#profil-system","title":"Profil-System","text":"

Ripster arbeitet mit Media-Profilen:

Viele Tool-/Pfad-Settings existieren als Profil-Varianten (*_bluray, *_dvd, *_other).

Wichtig:

"},{"location":"configuration/settings-reference/#template-platzhalter","title":"Template-Platzhalter","text":"

Datei-/Ordner-Templates unterst\u00fctzen:

Nicht gesetzte Werte werden zu unknown.

"},{"location":"configuration/settings-reference/#kategorie-pfade","title":"Kategorie: Pfade","text":"Key Typ Default raw_dir path data/output/raw raw_dir_bluray path null raw_dir_dvd path null raw_dir_other path null raw_dir_bluray_owner string null raw_dir_dvd_owner string null raw_dir_other_owner string null movie_dir path data/output/movies movie_dir_bluray path null movie_dir_dvd path null movie_dir_other path null movie_dir_bluray_owner string null movie_dir_dvd_owner string null movie_dir_other_owner string null log_dir path data/logs"},{"location":"configuration/settings-reference/#kategorie-laufwerk","title":"Kategorie: Laufwerk","text":"Key Typ Default Hinweis drive_mode select auto auto oder explicit drive_device path /dev/sr0 bei explicit relevant makemkv_source_index number 0 MakeMKV Source-Index disc_poll_interval_ms number 4000 1000..60000"},{"location":"configuration/settings-reference/#kategorie-monitoring","title":"Kategorie: Monitoring","text":"Key Typ Default hardware_monitoring_enabled boolean true hardware_monitoring_interval_ms number 5000"},{"location":"configuration/settings-reference/#kategorie-tools-global","title":"Kategorie: Tools (global)","text":"Key Typ Default makemkv_command string makemkvcon makemkv_registration_key string null mediainfo_command string mediainfo makemkv_min_length_minutes number 60 handbrake_command string HandBrakeCLI handbrake_restart_delete_incomplete_output boolean true pipeline_max_parallel_jobs number 1"},{"location":"configuration/settings-reference/#blu-ray-spezifisch","title":"Blu-ray-spezifisch","text":"Key Typ Default mediainfo_extra_args_bluray string null makemkv_rip_mode_bluray select backup makemkv_analyze_extra_args_bluray string null makemkv_rip_extra_args_bluray string null handbrake_preset_bluray string H.264 MKV 1080p30 handbrake_extra_args_bluray string null output_extension_bluray select mkv filename_template_bluray string ${title} (${year}) output_folder_template_bluray string null"},{"location":"configuration/settings-reference/#dvd-spezifisch","title":"DVD-spezifisch","text":"Key Typ Default mediainfo_extra_args_dvd string null makemkv_rip_mode_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 output_extension_dvd select mkv filename_template_dvd string ${title} (${year}) output_folder_template_dvd string null"},{"location":"configuration/settings-reference/#kategorie-metadaten","title":"Kategorie: Metadaten","text":"Key Typ Default omdb_api_key string null omdb_default_type select movie"},{"location":"configuration/settings-reference/#kategorie-benachrichtigungen-pushover","title":"Kategorie: Benachrichtigungen (PushOver)","text":"Key Typ Default pushover_enabled boolean false pushover_token string null pushover_user string null pushover_device string null pushover_title_prefix string Ripster pushover_priority number 0 pushover_timeout_ms number 7000 pushover_notify_metadata_ready boolean true pushover_notify_rip_started boolean true pushover_notify_encoding_started boolean true pushover_notify_job_finished boolean true pushover_notify_job_error boolean true pushover_notify_job_cancelled boolean true pushover_notify_reencode_started boolean true pushover_notify_reencode_finished boolean true"},{"location":"configuration/settings-reference/#entfernte-legacy-keys","title":"Entfernte Legacy-Keys","text":"

Diese Legacy-Keys werden bei Migration entfernt und sollten nicht mehr genutzt werden:

"},{"location":"deployment/","title":"Deployment","text":""},{"location":"deployment/development/","title":"Entwicklungsumgebung","text":""},{"location":"deployment/development/#voraussetzungen","title":"Voraussetzungen","text":""},{"location":"deployment/development/#schnellstart","title":"Schnellstart","text":"
./start.sh\n

Startet:

Stoppen: Ctrl+C.

"},{"location":"deployment/development/#manuell","title":"Manuell","text":""},{"location":"deployment/development/#backend","title":"Backend","text":"
cd backend\nnpm install\nnpm run dev\n
"},{"location":"deployment/development/#frontend","title":"Frontend","text":"
cd frontend\nnpm install\nnpm run dev\n
"},{"location":"deployment/development/#vite-proxy-dev","title":"Vite-Proxy (Dev)","text":"

frontend/vite.config.js proxied standardm\u00e4\u00dfig:

"},{"location":"deployment/development/#remote-dev-optional","title":"Remote-Dev (optional)","text":"

Beispiel frontend/.env.local:

VITE_API_BASE=http://192.168.1.100:3001/api\nVITE_WS_URL=ws://192.168.1.100:3001/ws\nVITE_PUBLIC_ORIGIN=http://192.168.1.100:5173\nVITE_ALLOWED_HOSTS=192.168.1.100,ripster.local\nVITE_HMR_PROTOCOL=ws\nVITE_HMR_HOST=192.168.1.100\nVITE_HMR_CLIENT_PORT=5173\n
"},{"location":"deployment/development/#nutzliche-kommandos","title":"N\u00fctzliche Kommandos","text":"
# Root dev (backend + frontend)\nnpm run dev\n\n# einzeln\nnpm run dev:backend\nnpm run dev:frontend\n\n# Frontend Build\nnpm run build:frontend\n
"},{"location":"deployment/development/#deploy-script-optional","title":"Deploy-Script (optional)","text":"

deploy-ripster.sh synchronisiert den lokalen Stand auf einen Remote-Host per rsync/SSH und sch\u00fctzt backend/data.

"},{"location":"deployment/production/","title":"Produktions-Deployment","text":""},{"location":"deployment/production/#empfohlene-architektur","title":"Empfohlene Architektur","text":"
Client\n  -> nginx (Reverse Proxy + statisches Frontend)\n    -> Backend API/WebSocket (Node.js, Port 3001)\n

Wichtig: Das Backend serviert im aktuellen Stand keine frontend/dist-Dateien automatisch.

"},{"location":"deployment/production/#1-frontend-builden","title":"1) Frontend builden","text":"
cd frontend\nnpm install\nnpm run build\n

Artefakte liegen in frontend/dist/.

"},{"location":"deployment/production/#2-backend-als-systemd-service","title":"2) Backend als systemd-Service","text":"

Beispiel /etc/systemd/system/ripster-backend.service:

[Unit]\nDescription=Ripster Backend\nAfter=network.target\n\n[Service]\nType=simple\nUser=ripster\nWorkingDirectory=/opt/ripster/backend\nExecStart=/usr/bin/env node src/index.js\nRestart=on-failure\nRestartSec=5\nEnvironment=NODE_ENV=production\nEnvironment=PORT=3001\nEnvironment=LOG_LEVEL=info\n\n[Install]\nWantedBy=multi-user.target\n

Aktivieren:

sudo systemctl daemon-reload\nsudo systemctl enable --now ripster-backend\nsudo systemctl status ripster-backend\n
"},{"location":"deployment/production/#3-nginx-konfigurieren","title":"3) nginx konfigurieren","text":"

Beispiel /etc/nginx/sites-available/ripster:

server {\n    listen 80;\n    server_name ripster.local;\n\n    root /opt/ripster/frontend/dist;\n    index index.html;\n\n    location / {\n        try_files $uri $uri/ /index.html;\n    }\n\n    location /api/ {\n        proxy_pass http://127.0.0.1:3001;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n    }\n\n    location /ws {\n        proxy_pass http://127.0.0.1:3001;\n        proxy_http_version 1.1;\n        proxy_set_header Upgrade $http_upgrade;\n        proxy_set_header Connection \"upgrade\";\n        proxy_set_header Host $host;\n    }\n}\n

Aktivieren:

sudo ln -s /etc/nginx/sites-available/ripster /etc/nginx/sites-enabled/\nsudo nginx -t\nsudo systemctl reload nginx\n
"},{"location":"deployment/production/#datenbank-backup","title":"Datenbank-Backup","text":"
sqlite3 /opt/ripster/backend/data/ripster.db \\\n  \".backup '/var/backups/ripster-$(date +%Y%m%d).db'\"\n
"},{"location":"deployment/production/#sicherheit","title":"Sicherheit","text":""},{"location":"getting-started/","title":"Erste Schritte","text":"

Dieser Abschnitt f\u00fchrt dich durch die Installation und Einrichtung von Ripster.

"},{"location":"getting-started/#uberblick","title":"\u00dcberblick","text":""},{"location":"getting-started/configuration/","title":"Konfiguration","text":"

Die Hauptkonfiguration erfolgt \u00fcber die UI (Settings) und wird in SQLite gespeichert.

"},{"location":"getting-started/configuration/#pflichteinstellungen-vor-dem-ersten-rip","title":"Pflichteinstellungen vor dem ersten Rip","text":""},{"location":"getting-started/configuration/#1-pfade","title":"1) Pfade","text":"Einstellung Beschreibung Beispiel raw_dir Basisverzeichnis f\u00fcr RAW-Rips /mnt/ripster/raw movie_dir Basisverzeichnis f\u00fcr finale Encodes /mnt/ripster/movies log_dir Verzeichnis f\u00fcr Prozess-/Backend-Logs /mnt/ripster/logs

Optional profilspezifisch:

"},{"location":"getting-started/configuration/#2-tools","title":"2) Tools","text":"Einstellung Standard makemkv_command makemkvcon handbrake_command HandBrakeCLI mediainfo_command mediainfo"},{"location":"getting-started/configuration/#3-omdb","title":"3) OMDb","text":"Einstellung Beschreibung omdb_api_key API-Key von omdbapi.com omdb_default_type movie, series, episode"},{"location":"getting-started/configuration/#encode-konfiguration-wichtig","title":"Encode-Konfiguration (wichtig)","text":"

Ripster arbeitet profilspezifisch, typischerweise \u00fcber:

"},{"location":"getting-started/configuration/#template-platzhalter","title":"Template-Platzhalter","text":"

Verf\u00fcgbar in filename_template_* und output_folder_template_*:

Beispiel:

${title} (${year})\n-> Inception (2010).mkv\n
"},{"location":"getting-started/configuration/#makemkv-spezifisch","title":"MakeMKV-spezifisch","text":"Einstellung Standard Hinweis makemkv_min_length_minutes 60 Kandidaten-Filter makemkv_rip_mode_bluray backup mkv oder backup makemkv_rip_mode_dvd mkv mkv oder backup makemkv_registration_key leer optional, wird via makemkvcon reg gesetzt"},{"location":"getting-started/configuration/#monitoring-queue","title":"Monitoring & Queue","text":"Einstellung Standard hardware_monitoring_enabled true hardware_monitoring_interval_ms 5000 pipeline_max_parallel_jobs 1"},{"location":"getting-started/configuration/#pushover-optional","title":"PushOver (optional)","text":"

Basis:

Zus\u00e4tzlich pro Event ein/aus (z. B. pushover_notify_job_finished).

"},{"location":"getting-started/configuration/#verwandte-doku","title":"Verwandte Doku","text":""},{"location":"getting-started/installation/","title":"Installation","text":""},{"location":"getting-started/installation/#repository-klonen","title":"Repository klonen","text":"
git clone https://github.com/YOUR_GITHUB_USERNAME/ripster.git\ncd ripster\n
"},{"location":"getting-started/installation/#dev-start-empfohlen","title":"Dev-Start (empfohlen)","text":"
./start.sh\n

start.sh:

  1. pr\u00fcft Node-Version (>= 20.19.0)
  2. installiert Dependencies (Root/Backend/Frontend)
  3. startet Backend + Frontend parallel

Danach:

Stoppen: mit Ctrl+C im laufenden Terminal.

"},{"location":"getting-started/installation/#manuell-starten","title":"Manuell starten","text":"
npm install\nnpm --prefix backend install\nnpm --prefix frontend install\nnpm run dev\n

Oder getrennt:

npm run dev:backend\nnpm run dev:frontend\n
"},{"location":"getting-started/installation/#optional-env-dateien-anlegen","title":"Optional: .env-Dateien anlegen","text":""},{"location":"getting-started/installation/#backend","title":"Backend","text":"
cp backend/.env.example backend/.env\n

Beispiel:

PORT=3001\nDB_PATH=./data/ripster.db\nLOG_DIR=./logs\nCORS_ORIGIN=http://localhost:5173\nLOG_LEVEL=info\n
"},{"location":"getting-started/installation/#frontend","title":"Frontend","text":"
cp frontend/.env.example frontend/.env\n

Beispiel:

VITE_API_BASE=/api\n# optional:\n# VITE_WS_URL=ws://localhost:3001/ws\n
"},{"location":"getting-started/installation/#datenbank","title":"Datenbank","text":"

SQLite wird automatisch beim Backend-Start initialisiert:

backend/data/ripster.db\n

Schema-Quelle: db/schema.sql

"},{"location":"getting-started/installation/#nachste-schritte","title":"N\u00e4chste Schritte","text":"
  1. Browser \u00f6ffnen: http://localhost:5173
  2. In Settings Pfade/Tools/API-Keys pr\u00fcfen
  3. Erste Disc einlegen und Workflow starten
"},{"location":"getting-started/prerequisites/","title":"Voraussetzungen","text":"

Bevor du Ripster installierst, stelle sicher, dass folgende Software auf deinem System verf\u00fcgbar ist.

"},{"location":"getting-started/prerequisites/#system-anforderungen","title":"System-Anforderungen","text":"Anforderung Mindestversion Empfohlen Betriebssystem Linux / macOS Ubuntu 22.04+ Node.js 20.19.0 20.x LTS RAM 4 GB 8 GB+ Festplatte 50 GB frei 500 GB+ (f\u00fcr Roh-MKVs)"},{"location":"getting-started/prerequisites/#nodejs","title":"Node.js","text":"

Ripster ben\u00f6tigt Node.js >= 20.19.0.

nvm (empfohlen)Ubuntu/DebianmacOS (Homebrew)
# nvm installieren\ncurl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash\n\n# Node.js 20 installieren\nnvm install 20\nnvm use 20\n\n# Version pr\u00fcfen\nnode --version  # v20.x.x\n
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -\nsudo apt-get install -y nodejs\n\nnode --version  # v20.x.x\n
brew install node@20\nnode --version  # v20.x.x\n
"},{"location":"getting-started/prerequisites/#externe-tools","title":"Externe Tools","text":""},{"location":"getting-started/prerequisites/#makemkv","title":"MakeMKV","text":"

Lizenz erforderlich

MakeMKV ist f\u00fcr den pers\u00f6nlichen Gebrauch kostenlos (Beta-Lizenz), ben\u00f6tigt aber eine g\u00fcltige Lizenz.

# Ubuntu/Debian - PPA verwenden\nsudo add-apt-repository ppa:heyarje/makemkv-beta\nsudo apt-get update\nsudo apt-get install makemkv-bin makemkv-oss\n\n# Installierte Version pr\u00fcfen\nmakemkvcon --version\n

MakeMKV Download

"},{"location":"getting-started/prerequisites/#handbrake-cli","title":"HandBrake CLI","text":"
# Ubuntu/Debian\nsudo add-apt-repository ppa:stebbins/handbrake-releases\nsudo apt-get update\nsudo apt-get install handbrake-cli\n\n# Version pr\u00fcfen\nHandBrakeCLI --version\n\n# macOS\nbrew install handbrake\n

HandBrake Download

"},{"location":"getting-started/prerequisites/#mediainfo","title":"MediaInfo","text":"
# Ubuntu/Debian\nsudo apt-get install mediainfo\n\n# macOS\nbrew install mediainfo\n\n# Version pr\u00fcfen\nmediainfo --Version\n
"},{"location":"getting-started/prerequisites/#disc-laufwerk","title":"Disc-Laufwerk","text":"

Ripster ben\u00f6tigt ein physisches DVD- oder Blu-ray-Laufwerk.

LibDriveIO-Modus erforderlich

Das Laufwerk muss im LibDriveIO-Modus betrieben werden \u2013 MakeMKV greift direkt auf Rohdaten des Laufwerks zu. Ohne diesen Modus k\u00f6nnen verschl\u00fcsselte Blu-rays (insbesondere UHD) nicht gelesen werden.

Nicht alle Laufwerke unterst\u00fctzen den direkten Zugriff. Eine Anleitung zur Einrichtung und Liste kompatibler Laufwerke findet sich im MakeMKV-Forum.

# Laufwerk pr\u00fcfen\nls /dev/sr*\n# oder\nlsblk | grep rom\n\n# Laufwerk-Berechtigungen setzen (erforderlich f\u00fcr LibDriveIO)\nsudo chmod a+rw /dev/sr0\n

Blu-ray unter Linux

MakeMKV bringt mit LibDriveIO eine eigene Entschl\u00fcsselung mit \u2013 externe Bibliotheken wie libaacs sind in der Regel nicht erforderlich.

"},{"location":"getting-started/prerequisites/#omdb-api-key","title":"OMDb API-Key","text":"

Ripster verwendet die OMDb API f\u00fcr Filmmetadaten.

  1. Registriere dich kostenlos auf omdbapi.com
  2. Best\u00e4tige deine E-Mail-Adresse
  3. Notiere deinen API-Key \u2013 du gibst ihn sp\u00e4ter in den Einstellungen ein
"},{"location":"getting-started/prerequisites/#optionale-voraussetzungen","title":"Optionale Voraussetzungen","text":""},{"location":"getting-started/prerequisites/#pushover-benachrichtigungen","title":"PushOver (Benachrichtigungen)","text":"

F\u00fcr mobile Push-Benachrichtigungen bei Fertigstellung oder Fehlern:

"},{"location":"getting-started/prerequisites/#ssh-zugang-deployment","title":"SSH-Zugang (Deployment)","text":"

F\u00fcr Remote-Deployment via deploy-ripster.sh:

# sshpass installieren\nsudo apt-get install sshpass\n
"},{"location":"getting-started/prerequisites/#checkliste","title":"Checkliste","text":""},{"location":"getting-started/quickstart/","title":"Schnellstart \u2013 Erster kompletter Job","text":"

Diese Seite f\u00fchrt durch den typischen ersten Lauf.

"},{"location":"getting-started/quickstart/#1-starten","title":"1) Starten","text":"
cd ripster\n./start.sh\n

\u00d6ffne http://localhost:5173.

"},{"location":"getting-started/quickstart/#2-disc-einlegen","title":"2) Disc einlegen","text":"

Pipeline wechselt auf DISC_DETECTED.

Falls n\u00f6tig manuell neu scannen:

curl -X POST http://localhost:3001/api/pipeline/rescan-disc\n
"},{"location":"getting-started/quickstart/#3-analyse-starten","title":"3) Analyse starten","text":"

Klicke im Dashboard auf Analyse starten.

Intern:

"},{"location":"getting-started/quickstart/#4-metadaten-bestatigen","title":"4) Metadaten best\u00e4tigen","text":"

Im Dialog:

Nach Best\u00e4tigung startet Ripster automatisch weiter.

"},{"location":"getting-started/quickstart/#5-pipeline-pfade","title":"5) Pipeline-Pfade","text":"

Abh\u00e4ngig von Job/RAW-Situation:

Wenn Parallel-Limit erreicht ist, wird der Job in die Queue eingereiht.

"},{"location":"getting-started/quickstart/#6-review-ready_to_encode","title":"6) Review (READY_TO_ENCODE)","text":"

Im Review-Panel:

Mit Encoding starten wird confirm-encode + Start ausgel\u00f6st.

"},{"location":"getting-started/quickstart/#7-encoding-encoding","title":"7) Encoding (ENCODING)","text":"

W\u00e4hrend Encoding:

Wichtig:

"},{"location":"getting-started/quickstart/#8-abschluss-finished","title":"8) Abschluss (FINISHED)","text":"

Ergebnis:

"},{"location":"getting-started/quickstart/#nutzliche-api-shortcuts","title":"N\u00fctzliche API-Shortcuts","text":"
# Pipeline-Snapshot\ncurl http://localhost:3001/api/pipeline/state\n\n# Queue-Snapshot\ncurl http://localhost:3001/api/pipeline/queue\n\n# Jobs\ncurl http://localhost:3001/api/history\n
"},{"location":"pipeline/","title":"Pipeline","text":"

Der Pipeline-Bereich beschreibt den Kern-Workflow von Ripster.

"},{"location":"pipeline/encoding/","title":"Encode-Planung & Track-Auswahl","text":"

Ripster erzeugt vor dem Encode einen encodePlan und l\u00e4sst ihn im Review-Panel best\u00e4tigen.

"},{"location":"pipeline/encoding/#ablauf","title":"Ablauf","text":"
Quelle bestimmen (Disc/RAW)\n  -> HandBrake-Scan (--scan --json)\n  -> Plan erstellen (Titel, Audio, Untertitel)\n  -> READY_TO_ENCODE\n  -> Benutzer best\u00e4tigt Auswahl\n  -> finaler HandBrake-Aufruf\n
"},{"location":"pipeline/encoding/#review-inhalt-ready_to_encode","title":"Review-Inhalt (READY_TO_ENCODE)","text":""},{"location":"pipeline/encoding/#bestatigung-confirm-encode","title":"Best\u00e4tigung (confirm-encode)","text":"

Typischer Payload:

{\n  \"selectedEncodeTitleId\": 1,\n  \"selectedTrackSelection\": {\n    \"1\": {\n      \"audioTrackIds\": [1, 2],\n      \"subtitleTrackIds\": [3]\n    }\n  },\n  \"selectedPreEncodeScriptIds\": [1],\n  \"selectedPostEncodeScriptIds\": [2],\n  \"selectedPreEncodeChainIds\": [3],\n  \"selectedPostEncodeChainIds\": [4],\n  \"selectedUserPresetId\": 5\n}\n

Ripster speichert die best\u00e4tigte Auswahl in jobs.encode_plan_json und markiert encode_review_confirmed = 1.

"},{"location":"pipeline/encoding/#handbrake-aufruf","title":"HandBrake-Aufruf","text":"

Grundstruktur:

HandBrakeCLI \\\n  -i <input> \\\n  -o <output> \\\n  -t <titleId> \\\n  -Z \"<preset>\" \\\n  <extra-args> \\\n  -a <audioTrackIds|none> \\\n  -s <subtitleTrackIds|none>\n

Untertitel-Flags werden bei Bedarf erg\u00e4nzt:

"},{"location":"pipeline/encoding/#pre-post-encode-ausfuhrungen","title":"Pre-/Post-Encode-Ausf\u00fchrungen","text":"

Verhalten bei Fehlern:

"},{"location":"pipeline/encoding/#dateinamenordner","title":"Dateinamen/Ordner","text":"

Der finale Outputpfad wird aus Settings-Templates aufgebaut.

Platzhalter:

Ung\u00fcltige Dateizeichen werden sanitisiert.

"},{"location":"pipeline/playlist-analysis/","title":"Playlist-Analyse","text":"

Ripster analysiert bei Blu-ray-\u00e4hnlichen Quellen Playlists und fordert bei Mehrdeutigkeit eine manuelle Auswahl an.

"},{"location":"pipeline/playlist-analysis/#ziel","title":"Ziel","text":"

Erkennen, welche Playlist wahrscheinlich der Hauptfilm ist, statt versehentlich eine Fake-/Dummy-Playlist zu verwenden.

"},{"location":"pipeline/playlist-analysis/#eingabedaten","title":"Eingabedaten","text":"

Die Analyse basiert auf MakeMKV-Infos (u. a. Playlist-/Segment-Struktur, Laufzeiten, Titelzuordnung).

"},{"location":"pipeline/playlist-analysis/#auswertung-vereinfacht","title":"Auswertung (vereinfacht)","text":"

F\u00fcr Kandidaten werden u. a. ber\u00fccksichtigt:

Daraus entstehen:

"},{"location":"pipeline/playlist-analysis/#wann-muss-der-benutzer-entscheiden","title":"Wann muss der Benutzer entscheiden?","text":"

Wenn nach Filterung mehr als ein relevanter Kandidat \u00fcbrig bleibt, setzt Ripster manualDecisionRequired = true und wechselt auf:

Dann muss eine Playlist best\u00e4tigt werden, bevor der Workflow weiterl\u00e4uft.

"},{"location":"pipeline/playlist-analysis/#konfigurationseinfluss","title":"Konfigurationseinfluss","text":"Key Wirkung makemkv_min_length_minutes Mindestlaufzeit f\u00fcr Kandidaten

Default ist aktuell 60 Minuten.

"},{"location":"pipeline/playlist-analysis/#ui-verhalten","title":"UI-Verhalten","text":"

Bei manueller Entscheidung zeigt das Dashboard Kandidaten inkl. Score/Bewertung und markiert eine Empfehlung.

Nach Best\u00e4tigung:

"},{"location":"pipeline/post-encode-scripts/","title":"Encode-Skripte (Pre & Post)","text":"

Ripster kann Skripte und Skript-Ketten vor und nach dem Encode ausf\u00fchren.

"},{"location":"pipeline/post-encode-scripts/#ablauf","title":"Ablauf","text":"
READY_TO_ENCODE\n  -> Pre-Encode Skripte/Ketten\n  -> HandBrake Encoding\n  -> Post-Encode Skripte/Ketten\n  -> FINISHED oder ERROR\n
"},{"location":"pipeline/post-encode-scripts/#auswahl-im-review","title":"Auswahl im Review","text":"

Im Review-Panel kannst du getrennt w\u00e4hlen:

"},{"location":"pipeline/post-encode-scripts/#fehlerverhalten","title":"Fehlerverhalten","text":""},{"location":"pipeline/post-encode-scripts/#verfugbare-umgebungsvariablen","title":"Verf\u00fcgbare Umgebungsvariablen","text":"

Beim Script-Run werden gesetzt:

"},{"location":"pipeline/post-encode-scripts/#skript-ketten","title":"Skript-Ketten","text":"

Ketten unterst\u00fctzen zwei Step-Typen:

Bei Fehler in einem Script-Step wird die Kette abgebrochen.

"},{"location":"pipeline/post-encode-scripts/#testlaufe","title":"Testl\u00e4ufe","text":"

Ergebnisse enthalten Erfolg/Exit-Code, Laufzeit und stdout/stderr.

"},{"location":"pipeline/workflow/","title":"Workflow & Zust\u00e4nde","text":"

Ripster steuert den Ablauf als State-Machine im pipelineService.

"},{"location":"pipeline/workflow/#zustandsdiagramm-vereinfacht","title":"Zustandsdiagramm (vereinfacht)","text":"
flowchart LR\n    IDLE --> DISC_DETECTED\n    DISC_DETECTED --> ANALYZING\n    ANALYZING --> METADATA_SELECTION\n    METADATA_SELECTION --> READY_TO_START\n    READY_TO_START --> RIPPING\n    READY_TO_START --> MEDIAINFO_CHECK\n    MEDIAINFO_CHECK --> WAITING_FOR_USER_DECISION\n    WAITING_FOR_USER_DECISION --> MEDIAINFO_CHECK\n    MEDIAINFO_CHECK --> READY_TO_ENCODE\n    READY_TO_ENCODE --> ENCODING\n    ENCODING --> FINISHED\n    ENCODING --> ERROR\n    RIPPING --> ERROR\n    RIPPING --> CANCELLED
"},{"location":"pipeline/workflow/#state-liste","title":"State-Liste","text":"State Bedeutung IDLE Wartet auf Disc DISC_DETECTED Disc erkannt ANALYZING MakeMKV-Analyse l\u00e4uft METADATA_SELECTION Benutzer w\u00e4hlt Metadaten WAITING_FOR_USER_DECISION Playlist-Auswahl n\u00f6tig READY_TO_START \u00dcbergangszustand vor Start RIPPING MakeMKV-Rip l\u00e4uft MEDIAINFO_CHECK Quelle/Tracks werden ausgewertet READY_TO_ENCODE Review ist bereit ENCODING HandBrake l\u00e4uft FINISHED erfolgreich abgeschlossen CANCELLED abgebrochen ERROR fehlgeschlagen"},{"location":"pipeline/workflow/#typische-pfade","title":"Typische Pfade","text":""},{"location":"pipeline/workflow/#standardfall-kein-vorhandenes-raw","title":"Standardfall (kein vorhandenes RAW)","text":"
  1. Disc erkannt
  2. Analyse + Metadaten
  3. RIPPING
  4. MEDIAINFO_CHECK
  5. READY_TO_ENCODE
  6. ENCODING
  7. FINISHED
"},{"location":"pipeline/workflow/#vorhandenes-raw","title":"Vorhandenes RAW","text":"

READY_TO_START springt direkt zu MEDIAINFO_CHECK (kein neuer Rip).

"},{"location":"pipeline/workflow/#mehrdeutige-blu-ray-playlist","title":"Mehrdeutige Blu-ray-Playlist","text":"

MEDIAINFO_CHECK -> WAITING_FOR_USER_DECISION bis Benutzer Playlist best\u00e4tigt.

"},{"location":"pipeline/workflow/#queue-verhalten","title":"Queue-Verhalten","text":"

Wenn pipeline_max_parallel_jobs erreicht ist:

"},{"location":"pipeline/workflow/#abbruch-retry-restart","title":"Abbruch, Retry, Restart","text":""},{"location":"tools/","title":"Externe Tools","text":"

Ripster ist ein Orchestrator \u2013 die eigentliche Arbeit erledigen diese bew\u00e4hrten Open-Source-Tools:

"},{"location":"tools/handbrake/","title":"HandBrake","text":"

Ripster verwendet HandBrakeCLI f\u00fcr Scan und Encode.

"},{"location":"tools/handbrake/#verwendete-aufrufe","title":"Verwendete Aufrufe","text":""},{"location":"tools/handbrake/#scan-review-aufbau","title":"Scan (Review-Aufbau)","text":"
HandBrakeCLI --scan --json -i <input> -t 0\n
"},{"location":"tools/handbrake/#encode-vereinfacht","title":"Encode (vereinfacht)","text":"
HandBrakeCLI \\\n  -i <input> \\\n  -o <output> \\\n  -t <titleId> \\\n  -Z \"<preset>\" \\\n  <extra-args> \\\n  -a <audioTrackIds|none> \\\n  -s <subtitleTrackIds|none>\n

Optional erg\u00e4nzt Ripster:

"},{"location":"tools/handbrake/#presets-auslesen","title":"Presets auslesen","text":"

Ripster liest Presets mit:

HandBrakeCLI -z\n
"},{"location":"tools/handbrake/#relevante-settings","title":"Relevante Settings","text":"Key Bedeutung handbrake_command CLI-Binary handbrake_preset_bluray / handbrake_preset_dvd profilspezifisches Preset handbrake_extra_args_bluray / handbrake_extra_args_dvd profilspezifische Zusatzargumente output_extension_bluray / output_extension_dvd Ausgabeformat handbrake_restart_delete_incomplete_output unvollst\u00e4ndige Ausgabe bei Neustart l\u00f6schen"},{"location":"tools/handbrake/#fortschritts-parsing","title":"Fortschritts-Parsing","text":"

Ripster parst HandBrake-Stderr (Prozent/ETA/Detail) und sendet WebSocket-Progress (PIPELINE_PROGRESS).

"},{"location":"tools/handbrake/#troubleshooting","title":"Troubleshooting","text":""},{"location":"tools/makemkv/","title":"MakeMKV","text":"

Ripster nutzt makemkvcon f\u00fcr Disc-Analyse und Rip.

"},{"location":"tools/makemkv/#verwendete-aufrufe","title":"Verwendete Aufrufe","text":""},{"location":"tools/makemkv/#analyse","title":"Analyse","text":"
makemkvcon -r info <source>\n

<source> ist typischerweise:

"},{"location":"tools/makemkv/#rip-mkv-modus","title":"Rip (MKV-Modus)","text":"
makemkvcon mkv <source> <title-or-all> <rawDir> [--minlength=...] [...extraArgs]\n
"},{"location":"tools/makemkv/#rip-backup-modus","title":"Rip (Backup-Modus)","text":"
makemkvcon backup <source> <rawDir> --decrypt\n
"},{"location":"tools/makemkv/#registrierungsschlussel-optional","title":"Registrierungsschl\u00fcssel (optional)","text":"

Wenn makemkv_registration_key gesetzt ist, f\u00fchrt Ripster vor Analyse/Rip aus:

makemkvcon reg <key>\n
"},{"location":"tools/makemkv/#relevante-settings","title":"Relevante Settings","text":"Key Bedeutung makemkv_command CLI-Binary makemkv_source_index Source-Index im Auto-Modus makemkv_min_length_minutes Mindestlaufzeitfilter makemkv_rip_mode_bluray / makemkv_rip_mode_dvd mkv oder backup makemkv_analyze_extra_args_bluray / _dvd Zusatzargs Analyse makemkv_rip_extra_args_bluray / _dvd Zusatzargs Rip"},{"location":"tools/makemkv/#hinweise","title":"Hinweise","text":""},{"location":"tools/mediainfo/","title":"MediaInfo","text":"

Ripster nutzt mediainfo zur JSON-Analyse von Medien-Dateien.

"},{"location":"tools/mediainfo/#aufruf","title":"Aufruf","text":"
mediainfo --Output=JSON <input>\n

Der Input ist typischerweise eine RAW-Datei oder ein vom Workflow gew\u00e4hlter Inputpfad.

"},{"location":"tools/mediainfo/#verwendung-in-ripster","title":"Verwendung in Ripster","text":""},{"location":"tools/mediainfo/#relevante-settings","title":"Relevante Settings","text":"Key Bedeutung mediainfo_command CLI-Binary mediainfo_extra_args_bluray / _dvd profilspezifische Zusatzargumente"},{"location":"tools/mediainfo/#troubleshooting","title":"Troubleshooting","text":""}]}