Files
ripster/site/search/search_index.json
T
michael ccd6c3ab2a
Deploy Docs to GitHub Pages / Build Documentation (push) Has been cancelled
Deploy Docs to GitHub Pages / Deploy to GitHub Pages (push) Has been cancelled
0.14.0-4 Docs
2026-04-21 14:52:30 +00:00

1 line
209 KiB
JSON

{"config":{"lang":["de"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"","title":"Ripster Handbuch","text":"<p>Diese Dokumentation ist als vollst\u00e4ndiges Benutzerhandbuch aufgebaut: t\u00e4gliche Bedienung zuerst, technische Referenz danach.</p>"},{"location":"#einstieg-in-3-schritten","title":"Einstieg in 3 Schritten","text":"<ol> <li>Installation: Installation</li> <li>Betriebskonfiguration: Ersteinrichtung</li> <li>erster End-to-End-Lauf: Erster Lauf</li> </ol>"},{"location":"#was-das-handbuch-abdeckt","title":"Was das Handbuch abdeckt","text":"<ul> <li>alle GUI-Seiten mit Aktionen und Wirkung</li> <li>vollst\u00e4ndige Workflows f\u00fcr Film, Serie und Multipart</li> <li>Queue-, Job-, Container- und Recovery-Verhalten</li> <li>detaillierte Settings-Wirkung inklusive Fallbacks</li> </ul>"},{"location":"#empfohlene-lesereihenfolge","title":"Empfohlene Lesereihenfolge","text":"<ol> <li>Benutzerhandbuch \u00dcberblick</li> <li>GUI-Seiten</li> <li>Workflows aus Nutzersicht</li> <li>Einstellungsreferenz</li> <li>bei Bedarf technischer Tiefgang im Anhang</li> </ol>"},{"location":"api/","title":"Anhang: API-Referenz","text":"<p>REST- und WebSocket-Schnittstellen f\u00fcr Integration, Automatisierung und Debugging.</p>"},{"location":"api/#basis-url","title":"Basis-URL","text":"<pre><code>http://localhost:3001\n</code></pre> <p>API-Prefix: <code>/api</code></p>"},{"location":"api/#api-gruppen","title":"API-Gruppen","text":"<ul> <li> <p> Health</p> <p>Service-Liveness.</p> <p><code>GET /api/health</code></p> </li> <li> <p> Pipeline API</p> <p>Analyse, Start/Retry/Cancel, Queue, Re-Encode.</p> <p> Pipeline API</p> </li> <li> <p> Settings API</p> <p>Einstellungen, Skripte/Ketten, User-Presets.</p> <p> Settings API</p> </li> <li> <p> History API</p> <p>Job-Historie, Orphan-Import, L\u00f6schoperationen.</p> <p> History API</p> </li> <li> <p> Cron API</p> <p>Zeitgesteuerte Skript-/Kettenausf\u00fchrung.</p> <p> Cron API</p> </li> <li> <p> Converter API</p> <p>Datei-Explorer, Upload, Job-Verwaltung f\u00fcr den Converter.</p> <p> Converter API</p> </li> <li> <p> Downloads API</p> <p>Download-Queue f\u00fcr Ausgabedateien aus der Job-Historie.</p> <p> Downloads API</p> </li> <li> <p> Runtime Activities API</p> <p>Laufende und abgeschlossene Aktivit\u00e4ten (Skripte, Ketten, Cron, Tasks).</p> <p> Runtime Activities</p> </li> <li> <p>:material-websocket: WebSocket Events</p> <p>Pipeline-, Queue-, Disk-, Settings-, Cron-, Converter- und Download-Events.</p> <p> WebSocket</p> </li> </ul>"},{"location":"api/#hinweis","title":"Hinweis","text":"<p>Ripster hat keine eingebaute Authentifizierung und ist f\u00fcr lokalen, gesch\u00fctzten Betrieb gedacht.</p>"},{"location":"api/converter/","title":"Converter API","text":"<p>Endpunkte f\u00fcr den Datei-Converter: Datei-Explorer, Upload, Job-Verwaltung.</p> <p>Basis-Pfad: <code>/api/converter</code></p>"},{"location":"api/converter/#scan-explorer","title":"Scan &amp; Explorer","text":""},{"location":"api/converter/#get-apiconvertertree","title":"<code>GET /api/converter/tree</code>","text":"<p>Vollst\u00e4ndiger Verzeichnisbaum des <code>converter_raw_dir</code> (FS-basiert, keine DB).</p> <p>Response:</p> <pre><code>{\n \"tree\": [\n {\n \"name\": \"filme\",\n \"relPath\": \"filme\",\n \"isDir\": true,\n \"children\": [\n {\n \"name\": \"film.mkv\",\n \"relPath\": \"filme/film.mkv\",\n \"isDir\": false,\n \"size\": 10485760\n }\n ]\n }\n ]\n}\n</code></pre>"},{"location":"api/converter/#get-apiconverterbrowseparentrelpath","title":"<code>GET /api/converter/browse?parent=relPath</code>","text":"<p>DB-basierter Datei-Explorer. Gibt Eintr\u00e4ge f\u00fcr einen Unterordner zur\u00fcck.</p> <p>Query-Parameter:</p> Parameter Typ Beschreibung <code>parent</code> string Relativer Pfad zum Unterordner (leer = Root) <p>Response:</p> <pre><code>{\n \"entries\": [\n {\n \"id\": 1,\n \"rel_path\": \"filme/film.mkv\",\n \"is_dir\": false,\n \"size\": 10485760,\n \"job_id\": null\n }\n ],\n \"rawDir\": \"/data/output/converter-raw\"\n}\n</code></pre>"},{"location":"api/converter/#post-apiconverterscan","title":"<code>POST /api/converter/scan</code>","text":"<p>Manuellen Scan des <code>converter_raw_dir</code> ausl\u00f6sen. Aktualisiert <code>converter_scan_entries</code> in der DB.</p> <p>Response:</p> <pre><code>{ \"result\": { \"added\": 3, \"removed\": 1 } }\n</code></pre>"},{"location":"api/converter/#jobs-erstellen","title":"Jobs erstellen","text":""},{"location":"api/converter/#post-apiconverterjobsfrom-selection","title":"<code>POST /api/converter/jobs/from-selection</code>","text":"<p>Jobs aus im Datei-Explorer ausgew\u00e4hlten Dateien erstellen.</p> <p>Body:</p> <pre><code>{\n \"relPaths\": [\"filme/film.mkv\", \"musik/track.flac\"],\n \"audioMode\": \"individual\"\n}\n</code></pre> Feld Typ Beschreibung <code>relPaths</code> string[] Relative Pfade der ausgew\u00e4hlten Dateien <code>audioMode</code> string <code>individual</code> (ein Job pro Datei) oder <code>shared</code> (ein gemeinsamer Job) <p>Response:</p> <pre><code>{ \"jobs\": [{ \"id\": 42, \"status\": \"READY_TO_START\" }] }\n</code></pre>"},{"location":"api/converter/#post-apiconvertercreate-jobs","title":"<code>POST /api/converter/create-jobs</code>","text":"<p>Jobs aus DB-Scan-Eintr\u00e4gen erstellen.</p> <p>Body:</p> <pre><code>{\n \"entries\": [\n { \"relPath\": \"filme/film.mkv\", \"converterMediaType\": \"video\" }\n ]\n}\n</code></pre>"},{"location":"api/converter/#post-apiconverterupload","title":"<code>POST /api/converter/upload</code>","text":"<p>Dateien hochladen (Multipart, max. 50 Dateien).</p> <p>Form-Felder:</p> Feld Typ Beschreibung <code>files</code> File[] Hochzuladende Dateien <code>folderName</code> string (Optional) Ziel-Unterordner <p>Response:</p> <pre><code>{\n \"folders\": [{ \"folderRelPath\": \"upload-2026-03-30\", \"fileCount\": 2 }]\n}\n</code></pre>"},{"location":"api/converter/#job-verwaltung","title":"Job-Verwaltung","text":""},{"location":"api/converter/#get-apiconverterjobs","title":"<code>GET /api/converter/jobs</code>","text":"<p>Alle Converter-Jobs zur\u00fcckgeben.</p> <p>Response:</p> <pre><code>{ \"jobs\": [{ \"id\": 42, \"status\": \"READY_TO_START\", \"title\": \"film.mkv\" }] }\n</code></pre>"},{"location":"api/converter/#get-apiconverterjobsjobid","title":"<code>GET /api/converter/jobs/:jobId</code>","text":"<p>Einzelnen Converter-Job abrufen.</p>"},{"location":"api/converter/#post-apiconverterjobsjobidconfig","title":"<code>POST /api/converter/jobs/:jobId/config</code>","text":"<p>Konfigurationsentwurf f\u00fcr einen Job speichern (persistiert in <code>encode_plan_json</code>).</p> <p>Body: Partielles Konfig-Objekt (Ausgabeformat, Presets, Metadaten, Track-Auswahl).</p>"},{"location":"api/converter/#post-apiconverterjobsjobidassign-files","title":"<code>POST /api/converter/jobs/:jobId/assign-files</code>","text":"<p>Dateien einem bestehenden (noch nicht gestarteten) Job hinzuf\u00fcgen.</p> <p>Body:</p> <pre><code>{ \"relPaths\": [\"musik/track2.flac\"] }\n</code></pre>"},{"location":"api/converter/#post-apiconverterjobsjobidremove-file","title":"<code>POST /api/converter/jobs/:jobId/remove-file</code>","text":"<p>Datei aus einem Job entfernen (per relativer Pfad).</p> <p>Body:</p> <pre><code>{ \"relPath\": \"musik/track2.flac\" }\n</code></pre>"},{"location":"api/converter/#post-apiconverterjobsjobidremove-input","title":"<code>POST /api/converter/jobs/:jobId/remove-input</code>","text":"<p>Datei aus einem Job entfernen (per absolutem Eingabepfad).</p> <p>Body:</p> <pre><code>{ \"inputPath\": \"/data/output/converter-raw/musik/track2.flac\" }\n</code></pre>"},{"location":"api/converter/#post-apiconverterjobsjobidstart","title":"<code>POST /api/converter/jobs/:jobId/start</code>","text":"<p>Job mit finaler Konfiguration starten.</p> <p>Body:</p> <pre><code>{\n \"converterMediaType\": \"video\",\n \"outputFormat\": \"mkv\",\n \"userPreset\": \"H.264 MKV 1080p30\",\n \"trackSelection\": {},\n \"handBrakeTitleId\": 1,\n \"audioFormatOptions\": {}\n}\n</code></pre>"},{"location":"api/converter/#post-apiconverterjobsjobidcancel","title":"<code>POST /api/converter/jobs/:jobId/cancel</code>","text":"<p>Laufenden Job abbrechen.</p>"},{"location":"api/converter/#delete-apiconverterjobsjobid","title":"<code>DELETE /api/converter/jobs/:jobId</code>","text":"<p>Job aus der DB l\u00f6schen.</p>"},{"location":"api/converter/#datei-operationen","title":"Datei-Operationen","text":"<p>Alle Datei-Operationen arbeiten direkt auf dem Dateisystem (ohne DB-Aktualisierung). Ein anschlie\u00dfender Scan-Aufruf synchronisiert die DB.</p>"},{"location":"api/converter/#delete-apiconverterfiles","title":"<code>DELETE /api/converter/files</code>","text":"<p>Datei oder Ordner l\u00f6schen.</p> <p>Body:</p> <pre><code>{ \"relPath\": \"filme/film.mkv\" }\n</code></pre>"},{"location":"api/converter/#post-apiconverterfilesrename","title":"<code>POST /api/converter/files/rename</code>","text":"<p>Datei oder Ordner umbenennen.</p> <p>Body:</p> <pre><code>{ \"relPath\": \"filme/film.mkv\", \"newName\": \"film-neu.mkv\" }\n</code></pre>"},{"location":"api/converter/#post-apiconverterfilesmove","title":"<code>POST /api/converter/files/move</code>","text":"<p>Datei oder Ordner verschieben.</p> <p>Body:</p> <pre><code>{ \"relPath\": \"filme/film.mkv\", \"targetParentRelPath\": \"archiv\" }\n</code></pre> <p><code>targetParentRelPath = \"\"</code> verschiebt in das Root-Verzeichnis.</p>"},{"location":"api/converter/#post-apiconverterfilesfolder","title":"<code>POST /api/converter/files/folder</code>","text":"<p>Neuen Ordner anlegen.</p> <p>Body:</p> <pre><code>{ \"parentRelPath\": \"filme\", \"name\": \"neu\" }\n</code></pre>"},{"location":"api/crons/","title":"Cron API","text":"<p>Ripster enth\u00e4lt ein eingebautes Cron-System f\u00fcr Skripte und Skript-Ketten (<code>sourceType: script|chain</code>).</p>"},{"location":"api/crons/#get-apicrons","title":"GET /api/crons","text":"<p>Listet alle Cron-Jobs.</p> <pre><code>{\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</code></pre>"},{"location":"api/crons/#post-apicrons","title":"POST /api/crons","text":"<p>Erstellt Cron-Job.</p> <pre><code>{\n \"name\": \"Nachtlauf Backup\",\n \"cronExpression\": \"0 2 * * *\",\n \"sourceType\": \"script\",\n \"sourceId\": 3,\n \"enabled\": true,\n \"pushoverEnabled\": true\n}\n</code></pre> <p>Response: <code>201</code> mit <code>{ \"job\": { ... } }</code></p>"},{"location":"api/crons/#get-apicronsid","title":"GET /api/crons/:id","text":"<p>Response:</p> <pre><code>{ \"job\": { \"id\": 1, \"name\": \"...\" } }\n</code></pre>"},{"location":"api/crons/#put-apicronsid","title":"PUT /api/crons/:id","text":"<p>Aktualisiert Cron-Job. Felder wie bei <code>POST</code>.</p> <p>Response:</p> <pre><code>{ \"job\": { ... } }\n</code></pre>"},{"location":"api/crons/#delete-apicronsid","title":"DELETE /api/crons/:id","text":"<p>Response:</p> <pre><code>{ \"removed\": { \"id\": 1, \"name\": \"Nachtlauf Backup\" } }\n</code></pre>"},{"location":"api/crons/#get-apicronsidlogs","title":"GET /api/crons/:id/logs","text":"<p>Liefert Ausf\u00fchrungs-Logs.</p> <p>Query-Parameter:</p> Parameter Typ Default Beschreibung <code>limit</code> number <code>20</code> Anzahl Eintr\u00e4ge, max. <code>100</code> <p>Response:</p> <pre><code>{\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</code></pre> <p><code>status</code>: <code>running</code> | <code>success</code> | <code>error</code></p>"},{"location":"api/crons/#post-apicronsidrun","title":"POST /api/crons/:id/run","text":"<p>Triggert Job manuell (asynchron).</p> <p>Response:</p> <pre><code>{ \"triggered\": true, \"cronJobId\": 1 }\n</code></pre> <p>Wenn Job bereits l\u00e4uft: <code>409</code>.</p>"},{"location":"api/crons/#post-apicronsvalidate-expression","title":"POST /api/crons/validate-expression","text":"<p>Validiert 5-Felder-Cron-Ausdruck und berechnet n\u00e4chsten Lauf.</p> <p>Request:</p> <pre><code>{ \"cronExpression\": \"*/15 * * * *\" }\n</code></pre> <p>G\u00fcltige Response:</p> <pre><code>{\n \"valid\": true,\n \"nextRunAt\": \"2026-03-10T14:15:00.000Z\"\n}\n</code></pre> <p>Ung\u00fcltige Response:</p> <pre><code>{\n \"valid\": false,\n \"error\": \"Cron-Ausdruck muss genau 5 Felder haben (Minute Stunde Tag Monat Wochentag).\",\n \"nextRunAt\": null\n}\n</code></pre>"},{"location":"api/crons/#cron-format","title":"Cron-Format","text":"<p>Ripster unterst\u00fctzt 5 Felder:</p> <pre><code>Minute Stunde Tag Monat Wochentag\n</code></pre> <p>Beispiele:</p> <ul> <li><code>0 2 * * *</code> t\u00e4glich 02:00</li> <li><code>*/15 * * * *</code> alle 15 Minuten</li> <li><code>0 6 * * 1-5</code> Mo-Fr 06:00</li> </ul>"},{"location":"api/crons/#websocket-events-zu-cron","title":"WebSocket-Events zu Cron","text":"<ul> <li><code>CRON_JOBS_UPDATED</code> bei Create/Update/Delete</li> <li><code>CRON_JOB_UPDATED</code> bei Laufzeitstatus (<code>running</code> -&gt; <code>success|error</code>)</li> </ul>"},{"location":"api/downloads/","title":"Downloads API","text":"<p>Endpunkte f\u00fcr die Download-Queue. Ausgabedateien aus der Job-Historie k\u00f6nnen als ZIP heruntergeladen werden.</p> <p>Basis-Pfad: <code>/api/downloads</code></p>"},{"location":"api/downloads/#get-apidownloads","title":"<code>GET /api/downloads</code>","text":"<p>Liste aller Download-Eintr\u00e4ge plus Zusammenfassung.</p> <p>Response:</p> <pre><code>{\n \"items\": [\n {\n \"id\": \"dl-42-raw\",\n \"jobId\": 42,\n \"target\": \"raw\",\n \"status\": \"ready\",\n \"archiveName\": \"Job_42_raw.zip\",\n \"outputPath\": null,\n \"createdAt\": \"2026-03-30T10:00:00.000Z\",\n \"updatedAt\": \"2026-03-30T10:01:00.000Z\",\n \"errorMessage\": null\n }\n ],\n \"summary\": {\n \"total\": 3,\n \"pending\": 1,\n \"processing\": 0,\n \"ready\": 1,\n \"failed\": 1\n }\n}\n</code></pre> <p>Status-Werte:</p> Status Beschreibung <code>pending</code> In der Queue, noch nicht gestartet <code>processing</code> ZIP wird gerade erstellt <code>ready</code> ZIP fertig, Download verf\u00fcgbar <code>failed</code> Fehler bei der Erstellung"},{"location":"api/downloads/#get-apidownloadssummary","title":"<code>GET /api/downloads/summary</code>","text":"<p>Nur die Zusammenfassung der Download-Queue (ohne Item-Liste).</p> <p>Response:</p> <pre><code>{\n \"summary\": {\n \"total\": 3,\n \"pending\": 1,\n \"processing\": 0,\n \"ready\": 1,\n \"failed\": 1\n }\n}\n</code></pre>"},{"location":"api/downloads/#post-apidownloadshistoryjobid","title":"<code>POST /api/downloads/history/:jobId</code>","text":"<p>Job-Ausgabe in die Download-Queue einreihen.</p> <p>Parameter:</p> Parameter Typ Beschreibung <code>jobId</code> number ID des Jobs in der Historie <p>Body:</p> <pre><code>{\n \"target\": \"raw\",\n \"outputPath\": null\n}\n</code></pre> Feld Typ Default Beschreibung <code>target</code> string <code>raw</code> <code>raw</code> (RAW-Dateien) oder <code>movie</code> (Ausgabedateien) <code>outputPath</code> string <code>null</code> Optionaler expliziter Ausgabepfad <p>Response (201 Created):</p> <pre><code>{\n \"created\": true,\n \"id\": \"dl-42-raw\",\n \"status\": \"pending\",\n \"summary\": { \"total\": 1, \"pending\": 1, \"processing\": 0, \"ready\": 0, \"failed\": 0 }\n}\n</code></pre> <p>Ist der Eintrag bereits vorhanden, wird <code>201</code> durch <code>200</code> ersetzt und <code>created: false</code> zur\u00fcckgegeben.</p>"},{"location":"api/downloads/#get-apidownloadsidfile","title":"<code>GET /api/downloads/:id/file</code>","text":"<p>Fertige ZIP-Datei herunterladen.</p> <p>Parameter:</p> Parameter Typ Beschreibung <code>id</code> string Download-ID (z. B. <code>dl-42-raw</code>) <p>Response: Datei-Download (<code>Content-Disposition: attachment</code>).</p> <p>Schl\u00e4gt fehl mit <code>404</code>, wenn kein Download-Eintrag mit dieser ID existiert oder die Datei noch nicht bereit ist.</p>"},{"location":"api/downloads/#delete-apidownloadsid","title":"<code>DELETE /api/downloads/:id</code>","text":"<p>Download-Eintrag l\u00f6schen (auch fertige ZIP-Dateien werden vom Dateisystem entfernt).</p> <p>Response:</p> <pre><code>{\n \"ok\": true,\n \"summary\": { \"total\": 2, \"pending\": 0, \"processing\": 0, \"ready\": 2, \"failed\": 0 }\n}\n</code></pre>"},{"location":"api/downloads/#websocket","title":"WebSocket","text":"<p>Status\u00e4nderungen werden \u00fcber <code>DOWNLOADS_UPDATED</code> in Echtzeit gemeldet. Vollst\u00e4ndige Dokumentation: WebSocket Events.</p>"},{"location":"api/history/","title":"History API","text":"<p>Endpunkte f\u00fcr Job-Historie, Metadaten-Nachpflege, Orphan-Import und L\u00f6schoperationen.</p>"},{"location":"api/history/#get-apihistory","title":"GET /api/history","text":"<p>Liefert Jobs mit optionalen Filtern.</p> <p>Query-Parameter:</p> Parameter Typ Beschreibung <code>status</code> string Einzelstatus-Filter (legacy-kompatibel). <code>statuses</code> string Mehrfachstatus als CSV, z. B. <code>FINISHED,ERROR</code>. <code>search</code> string Suche in Titel-/IMDb-Feldern. <code>limit</code> number Maximale Anzahl Ergebnisse. <code>lite</code> bool Ohne Dateisystem-Checks (schneller). <code>includeChildren</code> bool Child-Jobs (z. B. Serien-/Multipart-Kinder) explizit einbeziehen. <p>Beispiel:</p> <pre><code>GET /api/history?statuses=FINISHED,ERROR&amp;search=Inception&amp;limit=50&amp;lite=1\n</code></pre> <p>Response (Beispiel):</p> <pre><code>{\n \"jobs\": [\n {\n \"id\": 42,\n \"status\": \"FINISHED\",\n \"title\": \"Inception\",\n \"job_kind\": \"bluray\",\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</code></pre>"},{"location":"api/history/#get-apihistoryid","title":"GET /api/history/:id","text":"<p>Liefert Job-Detail.</p> <p>Query-Parameter:</p> Parameter Typ Standard Beschreibung <code>includeLogs</code> bool <code>false</code> Prozesslog laden. <code>includeLiveLog</code> bool <code>false</code> Live-/Tail-Log laden. <code>includeAllLogs</code> bool <code>false</code> vollst\u00e4ndiges Log statt Tail. <code>logTailLines</code> number <code>800</code> Tail-L\u00e4nge falls nicht <code>includeAllLogs</code>. <code>lite</code> bool <code>false</code> Detailantwort ohne FS-Checks. <p>Response (Beispiel):</p> <pre><code>{\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</code></pre>"},{"location":"api/history/#get-apihistoryorphan-raw","title":"GET /api/history/orphan-raw","text":"<p>Sucht RAW-Ordner ohne zugeh\u00f6rigen Job.</p>"},{"location":"api/history/#post-apihistoryorphan-rawimport","title":"POST /api/history/orphan-raw/import","text":"<p>Importiert einen RAW-Ordner als Job und triggert optional die Analyse des Imports.</p> <p>Request:</p> <pre><code>{ \"rawPath\": \"/mnt/raw/Inception (2010) [tt1375666] - RAW - job-99\" }\n</code></pre> <p>Response (Beispiel):</p> <pre><code>{\n \"job\": { \"id\": 77, \"status\": \"FINISHED\" },\n \"activation\": { \"started\": true },\n \"activationError\": null\n}\n</code></pre>"},{"location":"api/history/#post-apihistoryidomdbassign","title":"POST /api/history/:id/omdb/assign","text":"<p>Weist OMDb-/Filmdaten nachtr\u00e4glich zu.</p>"},{"location":"api/history/#post-apihistoryidcdassign","title":"POST /api/history/:id/cd/assign","text":"<p>Weist CD-Metadaten (z. B. MusicBrainz) nachtr\u00e4glich zu.</p>"},{"location":"api/history/#post-apihistoryiderrorack","title":"POST /api/history/:id/error/ack","text":"<p>Quittiert einen Fehlerzustand im Historieneintrag.</p>"},{"location":"api/history/#get-apihistoryiddelete-preview","title":"GET /api/history/:id/delete-preview","text":"<p>Liefert eine Vorschau der l\u00f6schbaren Pfade (inkl. Scope auf verkn\u00fcpfte Jobs).</p> <p>Query-Parameter:</p> Parameter Typ Standard Beschreibung <code>includeRelated</code> bool <code>true</code> Verkn\u00fcpfte Jobs in die Vorschau einbeziehen. <p>Response (Beispiel):</p> <pre><code>{\n \"preview\": {\n \"jobId\": 42,\n \"relatedJobs\": [{ \"id\": 42 }, { \"id\": 73 }],\n \"pathCandidates\": {\n \"raw\": [{ \"path\": \"/mnt/raw/...\", \"exists\": true, \"isDirectory\": true }],\n \"movie\": [{ \"path\": \"/mnt/movies/...\", \"exists\": true, \"isDirectory\": true }]\n }\n }\n}\n</code></pre>"},{"location":"api/history/#post-apihistoryiddelete-files","title":"POST /api/history/:id/delete-files","text":"<p>L\u00f6scht Dateien eines Jobs, beh\u00e4lt DB-Eintrag.</p> <p>Request:</p> <pre><code>{\n \"target\": \"both\",\n \"includeRelated\": true,\n \"selectedRawPaths\": [\"/mnt/raw/Inception - RAW - Disc1\"],\n \"selectedMoviePaths\": [\"/mnt/movies/Inception (2010)\"]\n}\n</code></pre> <p><code>target</code>: <code>raw</code> | <code>movie</code> | <code>both</code></p> <p><code>selectedRawPaths</code> / <code>selectedMoviePaths</code> sind optional und begrenzen die L\u00f6schung auf ausgew\u00e4hlte Pfade aus der Vorschau.</p>"},{"location":"api/history/#post-apihistoryiddelete","title":"POST /api/history/:id/delete","text":"<p>L\u00f6scht Job aus DB; optional auch Dateien.</p> <p>Request:</p> <pre><code>{\n \"target\": \"both\",\n \"includeRelated\": true,\n \"resetDriveState\": false,\n \"keepDetectedDevice\": true,\n \"preserveRawForImportJobs\": false,\n \"selectedRawPaths\": [\"/mnt/raw/Inception - RAW - Disc1\"],\n \"selectedMoviePaths\": [\"/mnt/movies/Inception (2010)\"]\n}\n</code></pre> <p><code>target</code>: <code>none</code> | <code>raw</code> | <code>movie</code> | <code>both</code></p> <p>Response (Beispiel):</p> <pre><code>{\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 \"safeguards\": {\n \"containsOrphanRawImportJob\": false,\n \"resetDriveStateApplied\": false,\n \"keepDetectedDeviceApplied\": true\n }\n}\n</code></pre>"},{"location":"api/history/#hinweise","title":"Hinweise","text":"<ul> <li>Ein aktiver Pipeline-Job kann nicht gel\u00f6scht werden (<code>409</code>).</li> <li>L\u00f6schoperationen sind irreversibel.</li> <li>Bei Serien-/Multipart-Containern steuern <code>includeRelated</code> und Pfadselektionen den genauen Scope.</li> </ul>"},{"location":"api/pipeline/","title":"Pipeline API","text":"<p>Endpunkte zur Steuerung des Pipeline-Workflows.</p>"},{"location":"api/pipeline/#get-apipipelinestate","title":"GET /api/pipeline/state","text":"<p>Liefert aktuellen Pipeline- und Hardware-Monitoring-Snapshot.</p> <p>Response (Beispiel):</p> <pre><code>{\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</code></pre>"},{"location":"api/pipeline/#post-apipipelineanalyze","title":"POST /api/pipeline/analyze","text":"<p>Startet Disc-Analyse und legt Job an.</p> <p>Response:</p> <pre><code>{\n \"result\": {\n \"jobId\": 42,\n \"detectedTitle\": \"INCEPTION\",\n \"omdbCandidates\": []\n }\n}\n</code></pre>"},{"location":"api/pipeline/#post-apipipelinerescan-disc","title":"POST /api/pipeline/rescan-disc","text":"<p>Erzwingt erneute Laufwerkspr\u00fcfung.</p> <p>Response (Beispiel):</p> <pre><code>{\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</code></pre>"},{"location":"api/pipeline/#post-apipipelinerescan-drive","title":"POST /api/pipeline/rescan-drive","text":"<p>Erzwingt Rescan f\u00fcr ein konkretes Laufwerk.</p> <p>Request:</p> <pre><code>{ \"devicePath\": \"/dev/sr0\" }\n</code></pre>"},{"location":"api/pipeline/#get-apipipelineomdbsearchq","title":"GET /api/pipeline/omdb/search?q= <p>OMDb-Titelsuche.</p> <p>Response:</p> <pre><code>{\n \"results\": [\n {\n \"imdbId\": \"tt1375666\",\n \"title\": \"Inception\",\n \"year\": \"2010\",\n \"type\": \"movie\",\n \"poster\": \"https://...\"\n }\n ]\n}\n</code></pre>","text":""},{"location":"api/pipeline/#get-apipipelinetmdbseriessearchqseason","title":"GET /api/pipeline/tmdb/series/search?q=&amp;season= <p>TMDb-Seriensuche (f\u00fcr Serien-Workflow bei DVD/Blu-ray).</p>","text":""},{"location":"api/pipeline/#get-apipipelinecddrives","title":"GET /api/pipeline/cd/drives <p>Liefert Snapshot der aktuell bekannten CD-Laufwerke.</p>","text":""},{"location":"api/pipeline/#get-apipipelinecdmusicbrainzsearchq","title":"GET /api/pipeline/cd/musicbrainz/search?q= <p>MusicBrainz-Suche f\u00fcr CD-Metadaten.</p>","text":""},{"location":"api/pipeline/#get-apipipelinecdmusicbrainzreleasembid","title":"GET /api/pipeline/cd/musicbrainz/release/:mbId <p>L\u00e4dt Release-Details zu einer MusicBrainz-ID.</p>","text":""},{"location":"api/pipeline/#post-apipipelinecdselect-metadata","title":"POST /api/pipeline/cd/select-metadata <p>\u00dcbernimmt CD-Metadaten f\u00fcr einen Job.</p> <p>Request (Beispiel):</p> <pre><code>{\n \"jobId\": 91,\n \"title\": \"Album\",\n \"artist\": \"Artist\",\n \"year\": 2020,\n \"mbId\": \"f5093c06-23e3-404f-aeaa-40f72885ee3a\",\n \"coverUrl\": \"https://...\",\n \"tracks\": []\n}\n</code></pre>","text":""},{"location":"api/pipeline/#post-apipipelinecdstartjobid","title":"POST /api/pipeline/cd/start/:jobId <p>Startet/queued CD-Rip mit Format-/Script-/Chain-Konfiguration.</p>","text":""},{"location":"api/pipeline/#post-apipipelineaudiobookupload","title":"POST /api/pipeline/audiobook/upload <p>Upload von AAX-Datei (Multipart/FormData).</p> <p>FormData-Felder:</p> <ul> <li><code>file</code> (Pflicht)</li> <li><code>format</code> (optional)</li> <li><code>startImmediately</code> (optional)</li> </ul>","text":""},{"location":"api/pipeline/#get-apipipelineaudiobookpending-activation","title":"GET /api/pipeline/audiobook/pending-activation <p>Zeigt AAX-Jobs mit fehlenden Activation-Bytes.</p>","text":""},{"location":"api/pipeline/#post-apipipelineaudiobookstartjobid","title":"POST /api/pipeline/audiobook/start/:jobId <p>Startet Audiobook-Job mit optionaler Konfiguration.</p>","text":""},{"location":"api/pipeline/#get-apipipelineaudiobookjobs","title":"GET /api/pipeline/audiobook/jobs <p>Liefert Audiobook-Jobliste.</p>","text":""},{"location":"api/pipeline/#get-apipipelineaudiobookoutput-tree","title":"GET /api/pipeline/audiobook/output-tree <p>Read-only Baumansicht des Audiobook-Ausgabeordners.</p>","text":""},{"location":"api/pipeline/#post-apipipelineselect-metadata","title":"POST /api/pipeline/select-metadata <p>Setzt Metadaten (und optional Playlist) f\u00fcr einen Job.</p> <p>Request:</p> <pre><code>{\n \"jobId\": 42,\n \"title\": \"Inception\",\n \"year\": 2010,\n \"imdbId\": \"tt1375666\",\n \"poster\": \"https://...\",\n \"fromOmdb\": true,\n \"selectedPlaylist\": \"00800\"\n}\n</code></pre> <p>Wichtige optionale Felder:</p> <ul> <li><code>selectedHandBrakeTitleId</code> / <code>selectedHandBrakeTitleIds</code>: Titel-Auswahl f\u00fcr Review/MediaInfo.</li> <li><code>metadataProvider</code>: <code>omdb</code> oder <code>tmdb</code>.</li> <li><code>workflowKind</code>: bei Disc-Jobs typischerweise <code>film</code> oder <code>series</code>.</li> <li><code>discNumber</code>: Pflicht f\u00fcr Serien-Disc-Zuordnung (<code>workflowKind=series</code>).</li> <li><code>duplicateAction</code>: Duplikatverhalten bei Film-Metadaten (<code>allow_new</code> oder <code>multipart_movie</code>).</li> <li><code>existingJobId</code>: optionaler Referenzjob f\u00fcr <code>multipart_movie</code>.</li> <li><code>existingDiscNumber</code>: Disc-Nummer des bestehenden Jobs f\u00fcr <code>multipart_movie</code>.</li> </ul> <p>Response:</p> <pre><code>{ \"job\": { \"id\": 42, \"status\": \"READY_TO_START\" } }\n</code></pre> <p>Konflikt-Response (Beispiel, HTTP 409):</p> <pre><code>{\n \"message\": \"Metadaten bereits in der Historie gefunden. Bitte Auswahl \u00fcbernehmen oder Multipart movie w\u00e4hlen.\",\n \"details\": [\n {\n \"code\": \"METADATA_DUPLICATE_FOUND\",\n \"mediaProfile\": \"bluray\",\n \"existingJob\": {\n \"id\": 17,\n \"title\": \"Inception\",\n \"year\": 2010,\n \"status\": \"FINISHED\",\n \"jobKind\": \"bluray\",\n \"discNumber\": 1,\n \"isMultipartMovie\": false\n }\n }\n ]\n}\n</code></pre> <p>Relevante Fehlercodes (<code>POST /api/pipeline/select-metadata</code>):</p> Code Bedeutung <code>METADATA_DUPLICATE_FOUND</code> Film-Metadaten existieren bereits in der Historie (Konfliktdialog im UI). <code>MULTIPART_DISC_REQUIRED</code> F\u00fcr Multipart fehlen Disc-Nummern (<code>discNumber</code>/<code>existingDiscNumber</code>). <code>MULTIPART_DISC_ALREADY_EXISTS</code> Disc-Nummer im Multipart-Container bereits vergeben oder doppelt. <code>MULTIPART_MEDIA_MISMATCH</code> Multipart nur bei gleichem Medientyp (DVD oder Blu-ray). <code>MULTIPART_SERIES_NOT_ALLOWED</code> Multipart ist nur f\u00fcr Film-Jobs erlaubt, nicht f\u00fcr Serien-Workflow. <code>MULTIPART_METADATA_MISMATCH</code> Ausgew\u00e4hlter bestehender Job passt nicht zu den Film-Metadaten. <code>SERIES_DISC_ALREADY_EXISTS</code> Serien-Disc-Nummer innerhalb einer Staffel bereits vorhanden.","text":""},{"location":"api/pipeline/#post-apipipelinejobsjobidraw-decision","title":"POST /api/pipeline/jobs/:jobId/raw-decision <p>Trifft Entscheidung bei vorhandenem RAW (<code>continue</code> oder <code>restart</code> je nach Dialogfluss).</p> <p>Request:</p> <pre><code>{ \"decision\": \"continue\" }\n</code></pre>","text":""},{"location":"api/pipeline/#post-apipipelinestartjobid","title":"POST /api/pipeline/start/:jobId <p>Startet vorbereiteten Job oder queued ihn (je nach Parallel-Limit).</p> <p>M\u00f6gliche Responses:</p> <pre><code>{ \"result\": { \"started\": true, \"stage\": \"RIPPING\" } }\n</code></pre> <pre><code>{ \"result\": { \"queued\": true, \"started\": false, \"queuePosition\": 2, \"action\": \"START_PREPARED\" } }\n</code></pre>","text":""},{"location":"api/pipeline/#post-apipipelineconfirm-encodejobid","title":"POST /api/pipeline/confirm-encode/:jobId <p>Best\u00e4tigt Review-Auswahl (Tracks, Pre/Post-Skripte/Ketten, User-Preset).</p> <p>Request (typisch):</p> <pre><code>{\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</code></pre> <p>Response:</p> <pre><code>{ \"job\": { \"id\": 42, \"encode_review_confirmed\": 1 } }\n</code></pre>","text":""},{"location":"api/pipeline/#post-apipipelinecancel","title":"POST /api/pipeline/cancel <p>Bricht laufenden Job ab oder entfernt Queue-Eintrag.</p> <p>Request (optional):</p> <pre><code>{ \"jobId\": 42 }\n</code></pre> <p>M\u00f6gliche Responses:</p> <pre><code>{ \"result\": { \"cancelled\": true, \"queuedOnly\": true, \"jobId\": 42 } }\n</code></pre> <pre><code>{ \"result\": { \"cancelled\": true, \"queuedOnly\": false, \"jobId\": 42 } }\n</code></pre> <pre><code>{ \"result\": { \"cancelled\": true, \"queuedOnly\": false, \"pending\": true, \"jobId\": 42 } }\n</code></pre>","text":""},{"location":"api/pipeline/#post-apipipelineretryjobid","title":"POST /api/pipeline/retry/:jobId <p>Retry f\u00fcr <code>ERROR</code>/<code>CANCELLED</code>-Jobs (oder Queue-Einreihung).</p>","text":""},{"location":"api/pipeline/#post-apipipelinereencodejobid","title":"POST /api/pipeline/reencode/:jobId <p>Startet Re-Encode aus bestehendem RAW.</p>","text":""},{"location":"api/pipeline/#post-apipipelinerestart-reviewjobid","title":"POST /api/pipeline/restart-review/:jobId <p>Berechnet Review aus RAW neu.</p>","text":""},{"location":"api/pipeline/#post-apipipelinerestart-encodejobid","title":"POST /api/pipeline/restart-encode/:jobId <p>Startet Encoding mit letzter best\u00e4tigter Review neu.</p>","text":""},{"location":"api/pipeline/#post-apipipelinerestart-cd-reviewjobid","title":"POST /api/pipeline/restart-cd-review/:jobId <p>Berechnet CD-Review aus vorhandenem RAW neu.</p>","text":""},{"location":"api/pipeline/#post-apipipelineresume-readyjobid","title":"POST /api/pipeline/resume-ready/:jobId <p>L\u00e4dt <code>READY_TO_ENCODE</code>-Job nach Neustart wieder in aktive Session.</p>","text":""},{"location":"api/pipeline/#get-apipipelineoutput-foldersjobid","title":"GET /api/pipeline/output-folders/:jobId <p>Liefert bekannte Output-Ordner entlang der Job-Lineage.</p>","text":""},{"location":"api/pipeline/#post-apipipelinedelete-output-foldersjobid","title":"POST /api/pipeline/delete-output-folders/:jobId <p>L\u00f6scht ausgew\u00e4hlte Output-Ordner.</p> <p>Request:</p> <pre><code>{ \"folderPaths\": [\"/mnt/movies/Inception (2010)\"] }\n</code></pre> <p>Alle Endpunkte liefern <code>{ result: ... }</code> bzw. <code>{ job: ... }</code>.</p>","text":""},{"location":"api/pipeline/#queue-endpunkte","title":"Queue-Endpunkte","text":""},{"location":"api/pipeline/#get-apipipelinequeue","title":"GET /api/pipeline/queue","text":"<p>Liefert Queue-Snapshot.</p> <pre><code>{\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</code></pre>"},{"location":"api/pipeline/#post-apipipelinequeuereorder","title":"POST /api/pipeline/queue/reorder","text":"<p>Sortiert Queue-Eintr\u00e4ge neu.</p> <p>Request:</p> <pre><code>{\n \"orderedEntryIds\": [12, 11]\n}\n</code></pre> <p>Legacy fallback wird akzeptiert:</p> <pre><code>{\n \"orderedJobIds\": [42, 43]\n}\n</code></pre>"},{"location":"api/pipeline/#post-apipipelinequeueentry","title":"POST /api/pipeline/queue/entry","text":"<p>F\u00fcgt Nicht-Job-Queue-Eintrag hinzu (<code>script</code>, <code>chain</code>, <code>wait</code>).</p> <p>Request-Beispiele:</p> <pre><code>{ \"type\": \"script\", \"scriptId\": 3 }\n</code></pre> <pre><code>{ \"type\": \"chain\", \"chainId\": 2, \"insertAfterEntryId\": 11 }\n</code></pre> <pre><code>{ \"type\": \"wait\", \"waitSeconds\": 45 }\n</code></pre> <p>Response:</p> <pre><code>{\n \"result\": { \"entryId\": 12, \"type\": \"wait\", \"position\": 2 },\n \"queue\": { \"...\": \"...\" }\n}\n</code></pre>"},{"location":"api/pipeline/#delete-apipipelinequeueentryentryid","title":"DELETE /api/pipeline/queue/entry/:entryId","text":"<p>Entfernt Queue-Eintrag.</p> <p>Response:</p> <pre><code>{ \"queue\": { \"...\": \"...\" } }\n</code></pre>"},{"location":"api/pipeline/#pipeline-zustande","title":"Pipeline-Zust\u00e4nde State Bedeutung <code>IDLE</code> Wartet auf Medium <code>DISC_DETECTED</code> Medium erkannt <code>ANALYZING</code> MakeMKV-Analyse l\u00e4uft <code>METADATA_SELECTION</code> Metadaten-Auswahl <code>WAITING_FOR_USER_DECISION</code> Nutzerentscheidung erforderlich (Playlist-Auswahl oder RAW-Entscheidung) <code>READY_TO_START</code> \u00dcbergang vor Start <code>RIPPING</code> MakeMKV-Rip l\u00e4uft <code>MEDIAINFO_CHECK</code> Titel-/Track-Auswertung <code>READY_TO_ENCODE</code> Review bereit <code>ENCODING</code> HandBrake-Encoding l\u00e4uft <code>CD_METADATA_SELECTION</code> CD-Metadatenauswahl aktiv <code>CD_READY_TO_RIP</code> CD-Job ist startbereit <code>CD_ANALYZING</code> CD-Struktur/Tracks werden vorbereitet <code>CD_RIPPING</code> CD-Ripping l\u00e4uft <code>CD_ENCODING</code> CD-Encode l\u00e4uft <code>FINISHED</code> Abgeschlossen <code>DONE</code> Abgeschlossen (v. a. Audiobook/Converter/CD-Varianten) <code>CANCELLED</code> Abgebrochen <code>ERROR</code> Fehler","text":""},{"location":"api/runtime-activities/","title":"Runtime Activities API","text":"<p>Ripster verfolgt alle laufenden und k\u00fcrzlich abgeschlossenen Aktivit\u00e4ten (Skripte, Skript-Ketten, Cron-Jobs, interne Tasks) in Echtzeit \u00fcber den <code>RuntimeActivityService</code>.</p>"},{"location":"api/runtime-activities/#ubersicht","title":"\u00dcbersicht","text":"<p>Aktivit\u00e4ten entstehen, wenn Ripster intern Aktionen ausf\u00fchrt \u2013 z. B. beim Start eines Cron-Jobs, beim Ausf\u00fchren einer Skript-Kette oder beim Durchlaufen von Pipeline-Schritten. Sie sind nicht persistent (kein DB-Speicher) und werden nur im Arbeitsspeicher gehalten.</p> <ul> <li>Aktive Aktivit\u00e4ten (<code>active</code>): Laufen gerade.</li> <li>Letzte Aktivit\u00e4ten (<code>recent</code>): Abgeschlossen, max. 120 Eintr\u00e4ge.</li> </ul> <p>\u00c4nderungen werden \u00fcber WebSocket (<code>RUNTIME_ACTIVITY_CHANGED</code>) in Echtzeit gesendet.</p>"},{"location":"api/runtime-activities/#aktivitats-objekt","title":"Aktivit\u00e4ts-Objekt","text":"<pre><code>{\n \"id\": 7,\n \"type\": \"chain\",\n \"name\": \"Post-Encode Aufr\u00e4umen\",\n \"status\": \"running\",\n \"source\": \"cron\",\n \"message\": \"Schritt 2 von 3\",\n \"currentStep\": \"cleanup.sh\",\n \"currentStepType\": \"script\",\n \"currentScriptName\": \"cleanup.sh\",\n \"stepIndex\": 2,\n \"stepTotal\": 3,\n \"parentActivityId\": null,\n \"jobId\": 42,\n \"cronJobId\": 3,\n \"chainId\": 5,\n \"scriptId\": null,\n \"canCancel\": true,\n \"canNextStep\": false,\n \"outcome\": \"running\",\n \"errorMessage\": null,\n \"output\": null,\n \"stdout\": null,\n \"stderr\": null,\n \"stdoutTruncated\": false,\n \"stderrTruncated\": false,\n \"startedAt\": \"2026-03-10T10:00:00.000Z\",\n \"finishedAt\": null,\n \"durationMs\": null,\n \"exitCode\": null,\n \"success\": null\n}\n</code></pre>"},{"location":"api/runtime-activities/#felder","title":"Felder","text":"Feld Typ Beschreibung <code>id</code> <code>number</code> Eindeutige ID (Laufz\u00e4hler, nicht persistent) <code>type</code> <code>string</code> Art der Aktivit\u00e4t: <code>script</code> | <code>chain</code> | <code>cron</code> | <code>task</code> <code>name</code> <code>string \\| null</code> Anzeigename der Aktivit\u00e4t <code>status</code> <code>string</code> Aktueller Status: <code>running</code> | <code>success</code> | <code>error</code> <code>source</code> <code>string \\| null</code> Ausl\u00f6ser (z. B. <code>cron</code>, <code>pipeline</code>, <code>manual</code>) <code>message</code> <code>string \\| null</code> Kurztext zum aktuellen Zustand <code>currentStep</code> <code>string \\| null</code> Name des aktuell ausgef\u00fchrten Schritts <code>currentStepType</code> <code>string \\| null</code> Typ des Schritts (z. B. <code>script</code>, <code>wait</code>) <code>currentScriptName</code> <code>string \\| null</code> Name des Skripts im aktuellen Schritt <code>stepIndex</code> <code>number \\| null</code> Aktueller Schritt (1-basiert) <code>stepTotal</code> <code>number \\| null</code> Gesamtanzahl Schritte <code>parentActivityId</code> <code>number \\| null</code> ID der \u00fcbergeordneten Aktivit\u00e4t <code>jobId</code> <code>number \\| null</code> Verkn\u00fcpfte Job-ID <code>cronJobId</code> <code>number \\| null</code> Verkn\u00fcpfte Cron-Job-ID <code>chainId</code> <code>number \\| null</code> Verkn\u00fcpfte Skript-Ketten-ID <code>scriptId</code> <code>number \\| null</code> Verkn\u00fcpfte Skript-ID <code>canCancel</code> <code>boolean</code> Abbrechen \u00fcber API m\u00f6glich <code>canNextStep</code> <code>boolean</code> N\u00e4chster Schritt \u00fcber API ausl\u00f6sbar <code>outcome</code> <code>string \\| null</code> Abschluss-Ergebnis: <code>success</code> | <code>error</code> | <code>cancelled</code> | <code>skipped</code> | <code>running</code> <code>errorMessage</code> <code>string \\| null</code> Fehlermeldung (max. 2.000 Zeichen) <code>output</code> <code>string \\| null</code> Allgemeine Ausgabe (max. 12.000 Zeichen) <code>stdout</code> <code>string \\| null</code> Standardausgabe des Prozesses (max. 12.000 Zeichen) <code>stderr</code> <code>string \\| null</code> Fehlerausgabe des Prozesses (max. 12.000 Zeichen) <code>stdoutTruncated</code> <code>boolean</code> <code>true</code>, wenn <code>stdout</code> gek\u00fcrzt wurde <code>stderrTruncated</code> <code>boolean</code> <code>true</code>, wenn <code>stderr</code> gek\u00fcrzt wurde <code>startedAt</code> <code>string</code> ISO-8601-Zeitstempel des Starts <code>finishedAt</code> <code>string \\| null</code> ISO-8601-Zeitstempel des Endes <code>durationMs</code> <code>number \\| null</code> Laufzeit in Millisekunden <code>exitCode</code> <code>number \\| null</code> Exit-Code des Prozesses <code>success</code> <code>boolean \\| null</code> Erfolgsstatus (<code>null</code> bei laufender Aktivit\u00e4t)"},{"location":"api/runtime-activities/#snapshot-objekt","title":"Snapshot-Objekt","text":"<p>Alle Aktivit\u00e4ts-Endpunkte geben einen Snapshot zur\u00fcck:</p> <pre><code>{\n \"active\": [ /* laufende Aktivit\u00e4ten, nach startedAt absteigend */ ],\n \"recent\": [ /* abgeschlossene Aktivit\u00e4ten, nach finishedAt absteigend, max. 120 */ ],\n \"updatedAt\": \"2026-03-10T10:05:00.000Z\"\n}\n</code></pre>"},{"location":"api/runtime-activities/#endpunkte","title":"Endpunkte","text":""},{"location":"api/runtime-activities/#get-apiruntimeactivities","title":"GET <code>/api/runtime/activities</code>","text":"<p>Aktuellen Aktivit\u00e4ts-Snapshot abrufen.</p> <p>Antwort:</p> <pre><code>{\n \"active\": [],\n \"recent\": [\n {\n \"id\": 5,\n \"type\": \"script\",\n \"name\": \"notify.sh\",\n \"status\": \"success\",\n \"outcome\": \"success\",\n \"startedAt\": \"2026-03-10T09:58:00.000Z\",\n \"finishedAt\": \"2026-03-10T09:58:02.000Z\",\n \"durationMs\": 2100,\n \"exitCode\": 0,\n \"success\": true,\n \"canCancel\": false,\n \"canNextStep\": false\n }\n ],\n \"updatedAt\": \"2026-03-10T10:05:00.000Z\"\n}\n</code></pre>"},{"location":"api/runtime-activities/#post-apiruntimeactivitiesidcancel","title":"POST <code>/api/runtime/activities/:id/cancel</code>","text":"<p>Aktive Aktivit\u00e4t abbrechen (nur wenn <code>canCancel: true</code>).</p> <p>Parameter:</p> Name In Typ Beschreibung <code>id</code> path <code>number</code> Aktivit\u00e4ts-ID <code>reason</code> body <code>string</code> Optionaler Abbruchgrund <p>Request Body:</p> <pre><code>{ \"reason\": \"Manueller Abbruch durch Benutzer\" }\n</code></pre> <p>Antwort (Erfolg):</p> <pre><code>{\n \"ok\": true,\n \"action\": null,\n \"snapshot\": { \"active\": [], \"recent\": [], \"updatedAt\": \"...\" }\n}\n</code></pre> <p>Fehlercodes:</p> HTTP Bedeutung <code>404</code> Aktivit\u00e4t nicht gefunden oder bereits abgeschlossen <code>409</code> Abbrechen wird von dieser Aktivit\u00e4t nicht unterst\u00fctzt"},{"location":"api/runtime-activities/#post-apiruntimeactivitiesidnext-step","title":"POST <code>/api/runtime/activities/:id/next-step</code>","text":"<p>N\u00e4chsten Schritt einer Aktivit\u00e4t ausl\u00f6sen (nur wenn <code>canNextStep: true</code>).</p> <p>Parameter:</p> Name In Typ Beschreibung <code>id</code> path <code>number</code> Aktivit\u00e4ts-ID <p>Antwort (Erfolg):</p> <pre><code>{\n \"ok\": true,\n \"action\": null,\n \"snapshot\": { \"active\": [], \"recent\": [], \"updatedAt\": \"...\" }\n}\n</code></pre> <p>Fehlercodes:</p> HTTP Bedeutung <code>404</code> Aktivit\u00e4t nicht gefunden <code>409</code> N\u00e4chster Schritt wird von dieser Aktivit\u00e4t nicht unterst\u00fctzt"},{"location":"api/runtime-activities/#post-apiruntimeactivitiesclear-recent","title":"POST <code>/api/runtime/activities/clear-recent</code>","text":"<p>Alle abgeschlossenen Aktivit\u00e4ten aus <code>recent</code> l\u00f6schen.</p> <p>Antwort:</p> <pre><code>{\n \"ok\": true,\n \"removed\": 14,\n \"snapshot\": { \"active\": [], \"recent\": [], \"updatedAt\": \"...\" }\n}\n</code></pre>"},{"location":"api/runtime-activities/#grenzwerte","title":"Grenzwerte","text":"Wert Limit Maximale <code>recent</code>-Eintr\u00e4ge 120 Maximale L\u00e4nge <code>stdout</code> / <code>stderr</code> / <code>output</code> 12.000 Zeichen Maximale L\u00e4nge <code>errorMessage</code> / <code>message</code> 2.000 Zeichen Maximale L\u00e4nge <code>outcome</code> 40 Zeichen <p>Gek\u00fcrzte Ausgaben erhalten den Suffix <code>...[gek\u00fcrzt]</code> (bei Inline-Text) bzw. <code>\\n...[gek\u00fcrzt]</code> (bei mehrzeiligem Output).</p>"},{"location":"api/runtime-activities/#echtzeit-updates","title":"Echtzeit-Updates","text":"<p>\u00c4nderungen werden automatisch als <code>RUNTIME_ACTIVITY_CHANGED</code> WebSocket-Event gesendet. Die Frontend-Komponente braucht <code>GET /api/runtime/activities</code> nur beim initialen Laden aufzurufen.</p>"},{"location":"api/settings/","title":"Settings API","text":"<p>Endpunkte f\u00fcr Einstellungen, Skripte, Skript-Ketten und User-Presets.</p>"},{"location":"api/settings/#get-apisettings","title":"GET /api/settings","text":"<p>Liefert alle Einstellungen kategorisiert.</p> <p>Response (Struktur):</p> <pre><code>{\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</code></pre>"},{"location":"api/settings/#put-apisettingskey","title":"PUT /api/settings/:key","text":"<p>Aktualisiert eine einzelne Einstellung.</p> <p>Request:</p> <pre><code>{ \"value\": \"/mnt/storage/raw\" }\n</code></pre> <p>Response:</p> <pre><code>{\n \"setting\": {\n \"key\": \"raw_dir\",\n \"value\": \"/mnt/storage/raw\"\n },\n \"reviewRefresh\": {\n \"triggered\": false,\n \"reason\": \"not_ready\"\n }\n}\n</code></pre> <p><code>reviewRefresh</code> ist <code>null</code> oder ein Objekt mit Status der optionalen Review-Neuberechnung.</p>"},{"location":"api/settings/#put-apisettings","title":"PUT /api/settings","text":"<p>Aktualisiert mehrere Einstellungen atomar.</p> <p>Request:</p> <pre><code>{\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</code></pre> <p>Response:</p> <pre><code>{\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</code></pre> <p>Bei Validierungsfehlern kommt <code>400</code> mit <code>error.details[]</code>.</p>"},{"location":"api/settings/#get-apisettingshandbrake-presets","title":"GET /api/settings/handbrake-presets","text":"<p>Liest Preset-Liste via <code>HandBrakeCLI -z</code> (mit Fallback auf konfigurierte Presets).</p> <p>Response (Beispiel):</p> <pre><code>{\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</code></pre>"},{"location":"api/settings/#post-apisettingspushovertest","title":"POST /api/settings/pushover/test","text":"<p>Sendet Testnachricht \u00fcber aktuelle PushOver-Settings.</p> <p>Request (optional):</p> <pre><code>{\n \"title\": \"Test\",\n \"message\": \"Ripster Test\"\n}\n</code></pre> <p>Response:</p> <pre><code>{\n \"result\": {\n \"sent\": true,\n \"eventKey\": \"test\",\n \"requestId\": \"...\"\n }\n}\n</code></pre> <p>Wenn PushOver deaktiviert ist oder Credentials fehlen, kommt i. d. R. ebenfalls <code>200</code> mit <code>sent: false</code> + <code>reason</code>.</p>"},{"location":"api/settings/#skripte","title":"Skripte","text":"<p>Basis: <code>/api/settings/scripts</code></p>"},{"location":"api/settings/#get-apisettingsscripts","title":"GET /api/settings/scripts","text":"<pre><code>{ \"scripts\": [ { \"id\": 1, \"name\": \"...\", \"scriptBody\": \"...\", \"orderIndex\": 1, \"createdAt\": \"...\", \"updatedAt\": \"...\" } ] }\n</code></pre>"},{"location":"api/settings/#post-apisettingsscripts","title":"POST /api/settings/scripts","text":"<pre><code>{ \"name\": \"Move\", \"scriptBody\": \"mv \\\"$RIPSTER_OUTPUT_PATH\\\" /mnt/movies/\" }\n</code></pre> <p>Response: <code>201</code> mit <code>{ \"script\": { ... } }</code></p>"},{"location":"api/settings/#put-apisettingsscriptsid","title":"PUT /api/settings/scripts/:id","text":"<p>Body wie <code>POST</code>, Response <code>{ \"script\": { ... } }</code>.</p>"},{"location":"api/settings/#delete-apisettingsscriptsid","title":"DELETE /api/settings/scripts/:id","text":"<p>Response <code>{ \"removed\": { ... } }</code>.</p>"},{"location":"api/settings/#post-apisettingsscriptsreorder","title":"POST /api/settings/scripts/reorder","text":"<pre><code>{ \"orderedScriptIds\": [3, 1, 2] }\n</code></pre> <p>Response <code>{ \"scripts\": [ ... ] }</code>.</p>"},{"location":"api/settings/#post-apisettingsscriptsidtest","title":"POST /api/settings/scripts/:id/test","text":"<p>F\u00fchrt Skript als Testlauf aus.</p> <pre><code>{\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</code></pre>"},{"location":"api/settings/#umgebungsvariablen-fur-skripte","title":"Umgebungsvariablen f\u00fcr Skripte","text":"<p>Diese Variablen werden beim Ausf\u00fchren gesetzt:</p> <ul> <li><code>RIPSTER_SCRIPT_RUN_AT</code></li> <li><code>RIPSTER_JOB_ID</code></li> <li><code>RIPSTER_JOB_TITLE</code></li> <li><code>RIPSTER_MODE</code></li> <li><code>RIPSTER_INPUT_PATH</code></li> <li><code>RIPSTER_OUTPUT_PATH</code></li> <li><code>RIPSTER_RAW_PATH</code></li> <li><code>RIPSTER_SCRIPT_ID</code></li> <li><code>RIPSTER_SCRIPT_NAME</code></li> <li><code>RIPSTER_SCRIPT_SOURCE</code></li> </ul>"},{"location":"api/settings/#skript-ketten","title":"Skript-Ketten","text":"<p>Basis: <code>/api/settings/script-chains</code></p> <p>Eine Kette hat Schritte vom Typ:</p> <ul> <li><code>script</code> (<code>scriptId</code> erforderlich)</li> <li><code>wait</code> (<code>waitSeconds</code> 1..3600)</li> </ul>"},{"location":"api/settings/#get-apisettingsscript-chains","title":"GET /api/settings/script-chains","text":"<p>Response <code>{ \"chains\": [ ... ] }</code> (inkl. <code>steps[]</code>).</p>"},{"location":"api/settings/#get-apisettingsscript-chainsid","title":"GET /api/settings/script-chains/:id","text":"<p>Response <code>{ \"chain\": { ... } }</code>.</p>"},{"location":"api/settings/#post-apisettingsscript-chains","title":"POST /api/settings/script-chains","text":"<pre><code>{\n \"name\": \"After Encode\",\n \"steps\": [\n { \"stepType\": \"script\", \"scriptId\": 1 },\n { \"stepType\": \"wait\", \"waitSeconds\": 15 },\n { \"stepType\": \"script\", \"scriptId\": 2 }\n ]\n}\n</code></pre> <p>Response: <code>201</code> mit <code>{ \"chain\": { ... } }</code></p>"},{"location":"api/settings/#put-apisettingsscript-chainsid","title":"PUT /api/settings/script-chains/:id","text":"<p>Body wie <code>POST</code>, Response <code>{ \"chain\": { ... } }</code>.</p>"},{"location":"api/settings/#delete-apisettingsscript-chainsid","title":"DELETE /api/settings/script-chains/:id","text":"<p>Response <code>{ \"removed\": { ... } }</code>.</p>"},{"location":"api/settings/#post-apisettingsscript-chainsreorder","title":"POST /api/settings/script-chains/reorder","text":"<pre><code>{ \"orderedChainIds\": [2, 1, 3] }\n</code></pre> <p>Response <code>{ \"chains\": [ ... ] }</code>.</p>"},{"location":"api/settings/#post-apisettingsscript-chainsidtest","title":"POST /api/settings/script-chains/:id/test","text":"<p>Response:</p> <pre><code>{\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</code></pre>"},{"location":"api/settings/#user-presets","title":"User-Presets","text":"<p>Basis: <code>/api/settings/user-presets</code></p>"},{"location":"api/settings/#get-apisettingsuser-presets","title":"GET /api/settings/user-presets","text":"<p>Optionaler Query-Parameter: <code>media_type=bluray|dvd|other|all</code></p> <pre><code>{\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</code></pre>"},{"location":"api/settings/#post-apisettingsuser-presets","title":"POST /api/settings/user-presets","text":"<pre><code>{\n \"name\": \"Blu-ray HQ\",\n \"mediaType\": \"bluray\",\n \"handbrakePreset\": \"H.264 MKV 1080p30\",\n \"extraArgs\": \"--encoder-preset slow\",\n \"description\": \"optional\"\n}\n</code></pre> <p>Response: <code>201</code> mit <code>{ \"preset\": { ... } }</code></p>"},{"location":"api/settings/#put-apisettingsuser-presetsid","title":"PUT /api/settings/user-presets/:id","text":"<p>Body mit beliebigen Feldern aus <code>POST</code>, Response <code>{ \"preset\": { ... } }</code>.</p>"},{"location":"api/settings/#delete-apisettingsuser-presetsid","title":"DELETE /api/settings/user-presets/:id","text":"<p>Response <code>{ \"removed\": { ... } }</code>.</p>"},{"location":"api/settings/#weitere-endpunkte","title":"Weitere Endpunkte","text":""},{"location":"api/settings/#get-apisettingseffective-paths","title":"GET /api/settings/effective-paths","text":"<p>Liefert aufgel\u00f6ste, effektiv genutzte Pfade aus den Settings (f\u00fcr UI- und Diagnosezwecke).</p>"},{"location":"api/settings/#post-apisettingscoverartrecover","title":"POST /api/settings/coverart/recover","text":"<p>Startet eine manuelle Coverart-Recovery.</p> <p>Response (Beispiel):</p> <pre><code>{\n \"result\": { \"started\": true },\n \"scheduler\": { \"enabled\": true }\n}\n</code></pre>"},{"location":"api/settings/#activation-bytes-aax","title":"Activation Bytes (AAX)","text":""},{"location":"api/settings/#get-apisettingsactivation-bytes","title":"GET /api/settings/activation-bytes","text":"<p>Liest gecachte AAX-Activation-Bytes.</p>"},{"location":"api/settings/#post-apisettingsactivation-bytes","title":"POST /api/settings/activation-bytes","text":"<p>Speichert Activation-Bytes.</p> <p>Request:</p> <pre><code>{\n \"checksum\": \"abc123...\",\n \"activationBytes\": \"1a2b3c4d\"\n}\n</code></pre>"},{"location":"api/settings/#laufwerke","title":"Laufwerke","text":""},{"location":"api/settings/#get-apisettingsdrives","title":"GET /api/settings/drives","text":"<p>Liefert erkannte optische Laufwerke (<code>/dev/...</code>) inkl. MakeMKV-Disc-Index.</p>"},{"location":"api/settings/#post-apisettingsdrivesforce-unlock","title":"POST /api/settings/drives/force-unlock","text":"<p>Erzwingt Entsperren aktiver Laufwerkslocks.</p> <p>Hinweis: Expertenmodus (<code>ui_expert_mode</code>) ist erforderlich, sonst <code>403</code>.</p> <p>Request (ein Laufwerk):</p> <pre><code>{ \"devicePath\": \"/dev/sr0\" }\n</code></pre> <p>Request (alle):</p> <pre><code>{ \"all\": true }\n</code></pre>"},{"location":"api/settings/#ui-preferences","title":"UI Preferences","text":""},{"location":"api/settings/#get-apisettingsprefskey","title":"GET /api/settings/prefs/:key","text":"<p>Liest einen persistierten UI-Preference-Wert.</p>"},{"location":"api/settings/#put-apisettingsprefskey","title":"PUT /api/settings/prefs/:key","text":"<p>Speichert einen persistierten UI-Preference-Wert.</p> <p>Request:</p> <pre><code>{ \"value\": \"{\\\"columns\\\":[\\\"id\\\",\\\"status\\\"]}\" }\n</code></pre>"},{"location":"api/websocket/","title":"WebSocket Events","text":"<p>Ripster sendet Echtzeit-Updates \u00fcber <code>/ws</code>.</p>"},{"location":"api/websocket/#verbindung","title":"Verbindung","text":"<pre><code>const ws = new WebSocket('ws://localhost:3001/ws');\n\nws.onmessage = (event) =&gt; {\n const msg = JSON.parse(event.data);\n console.log(msg.type, msg.payload);\n};\n</code></pre>"},{"location":"api/websocket/#nachrichtenformat","title":"Nachrichtenformat","text":"<p>Die meisten Broadcasts haben dieses Schema:</p> <pre><code>{\n \"type\": \"EVENT_TYPE\",\n \"payload\": {},\n \"timestamp\": \"2026-03-10T09:00:00.000Z\"\n}\n</code></pre> <p>Ausnahme: <code>WS_CONNECTED</code> beim Verbindungsaufbau enth\u00e4lt kein <code>timestamp</code>.</p>"},{"location":"api/websocket/#event-typen","title":"Event-Typen","text":""},{"location":"api/websocket/#ws_connected","title":"WS_CONNECTED","text":"<p>Sofort nach erfolgreicher Verbindung.</p> <pre><code>{\n \"type\": \"WS_CONNECTED\",\n \"payload\": {\n \"connectedAt\": \"2026-03-10T09:00:00.000Z\"\n }\n}\n</code></pre>"},{"location":"api/websocket/#pipeline_state_changed","title":"PIPELINE_STATE_CHANGED","text":"<p>Neuer Pipeline-Snapshot.</p> <pre><code>{\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</code></pre>"},{"location":"api/websocket/#pipeline_progress","title":"PIPELINE_PROGRESS","text":"<p>Laufende Fortschrittsupdates.</p> <pre><code>{\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</code></pre>"},{"location":"api/websocket/#pipeline_queue_changed","title":"PIPELINE_QUEUE_CHANGED","text":"<p>Queue-Snapshot aktualisiert.</p>"},{"location":"api/websocket/#disc_detected-disc_removed","title":"DISC_DETECTED / DISC_REMOVED","text":"<p>Disc-Insertion/-Removal.</p> <pre><code>{\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</code></pre> <p><code>mediaProfile</code>: <code>bluray</code> | <code>dvd</code> | <code>other</code> | <code>null</code></p>"},{"location":"api/websocket/#hardware_monitor_update","title":"HARDWARE_MONITOR_UPDATE","text":"<p>Snapshot aus Hardware-Monitoring.</p> <pre><code>{\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</code></pre>"},{"location":"api/websocket/#pipeline_error","title":"PIPELINE_ERROR","text":"<p>Fehler bei Disc-Event-Verarbeitung in Pipeline.</p>"},{"location":"api/websocket/#disk_detection_error","title":"DISK_DETECTION_ERROR","text":"<p>Fehler in Laufwerkserkennung.</p>"},{"location":"api/websocket/#settings_updated","title":"SETTINGS_UPDATED","text":"<p>Einzelnes Setting wurde gespeichert.</p>"},{"location":"api/websocket/#settings_bulk_updated","title":"SETTINGS_BULK_UPDATED","text":"<p>Bulk-Settings gespeichert.</p> <pre><code>{\n \"type\": \"SETTINGS_BULK_UPDATED\",\n \"payload\": {\n \"count\": 3,\n \"keys\": [\"raw_dir\", \"movie_dir\", \"handbrake_preset_bluray\"]\n }\n}\n</code></pre>"},{"location":"api/websocket/#settings_scripts_updated","title":"SETTINGS_SCRIPTS_UPDATED","text":"<p>Skript ge\u00e4ndert (<code>created|updated|deleted|reordered</code>).</p>"},{"location":"api/websocket/#settings_script_chains_updated","title":"SETTINGS_SCRIPT_CHAINS_UPDATED","text":"<p>Skript-Kette ge\u00e4ndert (<code>created|updated|deleted|reordered</code>).</p>"},{"location":"api/websocket/#user_presets_updated","title":"USER_PRESETS_UPDATED","text":"<p>User-Preset ge\u00e4ndert (<code>created|updated|deleted</code>).</p>"},{"location":"api/websocket/#cron_jobs_updated","title":"CRON_JOBS_UPDATED","text":"<p>Cron-Config ge\u00e4ndert (<code>created|updated|deleted</code>).</p>"},{"location":"api/websocket/#cron_job_updated","title":"CRON_JOB_UPDATED","text":"<p>Laufzeitstatus eines Cron-Jobs ge\u00e4ndert.</p> <pre><code>{\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</code></pre>"},{"location":"api/websocket/#runtime_activity_changed","title":"RUNTIME_ACTIVITY_CHANGED","text":"<p>Vollst\u00e4ndiger Snapshot aller laufenden und k\u00fcrzlich abgeschlossenen Aktivit\u00e4ten.</p> <p>Wird ausgel\u00f6st, wenn eine Aktivit\u00e4t gestartet, aktualisiert oder abgeschlossen wird sowie nach <code>clear-recent</code>.</p> <pre><code>{\n \"type\": \"RUNTIME_ACTIVITY_CHANGED\",\n \"payload\": {\n \"active\": [\n {\n \"id\": 7,\n \"type\": \"chain\",\n \"name\": \"Post-Encode Aufr\u00e4umen\",\n \"status\": \"running\",\n \"source\": \"cron\",\n \"message\": \"Schritt 2 von 3\",\n \"currentStep\": \"cleanup.sh\",\n \"currentStepType\": \"script\",\n \"stepIndex\": 2,\n \"stepTotal\": 3,\n \"canCancel\": true,\n \"canNextStep\": false,\n \"outcome\": \"running\",\n \"startedAt\": \"2026-03-10T10:00:00.000Z\",\n \"finishedAt\": null,\n \"durationMs\": null\n }\n ],\n \"recent\": [],\n \"updatedAt\": \"2026-03-10T10:00:05.000Z\"\n }\n}\n</code></pre> <p>Vollst\u00e4ndige Feldbeschreibung: Runtime Activities API.</p>"},{"location":"api/websocket/#converter_scan_update","title":"CONVERTER_SCAN_UPDATE","text":"<p>Wird nach einem Scan des Converter-Eingangsordners gesendet (manuell oder per Polling).</p> <pre><code>{\n \"type\": \"CONVERTER_SCAN_UPDATE\",\n \"payload\": {\n \"entryCount\": 12\n }\n}\n</code></pre>"},{"location":"api/websocket/#downloads_updated","title":"DOWNLOADS_UPDATED","text":"<p>Wird bei jeder Status\u00e4nderung in der Download-Queue gesendet.</p> <pre><code>{\n \"type\": \"DOWNLOADS_UPDATED\",\n \"payload\": {\n \"reason\": \"ready\",\n \"item\": {\n \"id\": \"dl-1\",\n \"jobId\": 42,\n \"status\": \"ready\",\n \"archiveName\": \"Job_42_movie.zip\",\n \"createdAt\": \"2026-03-30T10:00:00.000Z\"\n },\n \"summary\": {\n \"total\": 3,\n \"pending\": 1,\n \"processing\": 0,\n \"ready\": 1,\n \"failed\": 1\n }\n }\n}\n</code></pre> <p>M\u00f6gliche <code>reason</code>-Werte: <code>queued</code>, <code>processing</code>, <code>ready</code>, <code>failed</code>, <code>deleted</code>.</p>"},{"location":"api/websocket/#reconnect-verhalten","title":"Reconnect-Verhalten","text":"<p><code>useWebSocket</code> verbindet bei Abbruch automatisch neu:</p> <ul> <li>Retry-Intervall: <code>1500ms</code></li> <li>Wiederverbindung bis Komponente unmounted wird</li> </ul>"},{"location":"appendix/","title":"Technischer Anhang","text":"<p>Dieser Bereich enth\u00e4lt die technische Referenz hinter dem Benutzerhandbuch.</p>"},{"location":"appendix/#inhalt","title":"Inhalt","text":"<ul> <li>Konfiguration</li> <li>komplette Feldreferenz</li> <li>Umgebungsvariablen</li> <li>Pipeline intern</li> <li>Zustandsmodell</li> <li>Encode-Planung</li> <li>Playlist-Analyse</li> <li>Pre-/Post-Encode-Ausf\u00fchrungen</li> <li>API-Referenz</li> <li>REST-Endpunkte</li> <li>WebSocket-Events</li> <li>Architektur</li> <li>Backend-/Frontend-Aufbau</li> <li>Datenbank</li> <li>Deployment</li> <li>Betrieb in Entwicklung und Produktion</li> <li>Externe Tools</li> <li>MakeMKV, HandBrake, MediaInfo</li> </ul>"},{"location":"appendix/#wann-du-in-den-anhang-wechselst","title":"Wann du in den Anhang wechselst","text":"<ul> <li>du integrierst Ripster mit anderen Systemen</li> <li>du betreibst mehrere Instanzen oder willst tiefer debuggen</li> <li>du brauchst Feld-/API-/Schema-Details f\u00fcr Automatisierung</li> </ul>"},{"location":"architecture/","title":"Anhang: Architektur","text":"<p>Ripster ist eine Client-Server-Anwendung mit REST + WebSocket und externen CLI-Tools.</p>"},{"location":"architecture/#systemuberblick","title":"System\u00fcberblick","text":"<pre><code>graph TB\n subgraph Browser[\"Browser (React)\"]\n Ripper[Ripper]\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 &lt;--&gt;|HTTP| API\n Browser &lt;--&gt;|WebSocket| WS\n Pipeline --&gt; MakeMKV\n Pipeline --&gt; HandBrake\n Pipeline --&gt; MediaInfo\n API --&gt; DB\n Pipeline --&gt; DB\n Cron --&gt; DB</code></pre>"},{"location":"architecture/#details","title":"Details","text":"<ul> <li> \u00dcbersicht</li> <li> Backend-Services</li> <li> Frontend-Komponenten</li> <li> Datenbank</li> </ul>"},{"location":"architecture/backend/","title":"Backend-Services","text":"<p>Das Backend ist in Services aufgeteilt, die von Express-Routen orchestriert werden. Seit v0.12.0 erfolgt die Medienverarbeitung \u00fcber ein Plugin-System.</p>"},{"location":"architecture/backend/#plugin-system-srcplugins","title":"Plugin-System (<code>src/plugins/</code>)","text":"<p>Ab v0.12.0 ist die Pipeline modular aufgebaut. Jeder Medientyp wird von einem eigenen Plugin verwaltet.</p>"},{"location":"architecture/backend/#basisklassen","title":"Basisklassen","text":"<ul> <li><code>PluginBase.js</code> \u2014 Abstrakte Basisklasse <code>SourcePlugin</code> mit Lifecycle-Methoden</li> <li><code>PluginRegistry.js</code> \u2014 Dynamische Plugin-Erkennung, priorit\u00e4tsbasierte Auswahl, Settings-Schema-Aggregation</li> <li><code>PluginContext.js</code> \u2014 Ausf\u00fchrungskontext (Logger, Callbacks, Signals) f\u00fcr Plugin-Aufrufe</li> </ul>"},{"location":"architecture/backend/#plugin-lifecycle","title":"Plugin-Lifecycle","text":"<p>Jedes Plugin implementiert die folgenden Methoden:</p> Methode Beschreibung <code>detect(discInfo)</code> Medientyp-Erkennung <code>analyze(devicePath, job, ctx)</code> Analyse / Metadaten-Extraktion <code>rip(job, ctx)</code> Rohdaten-Extraktion <code>review(job, ctx)</code> Optionale Vorschau-Vorbereitung <code>encode(job, ctx)</code> Finales Encoding/Konvertierung <code>finalize(job, ctx)</code> Nachbearbeitung <code>onCancel(job, ctx)</code> Bereinigung bei Abbruch <code>onRetry(job, ctx)</code> Zustand-Reset vor Retry"},{"location":"architecture/backend/#implementierte-plugins","title":"Implementierte Plugins","text":"<ul> <li><code>BluRayPlugin.js</code> \u2014 Blu-ray via MakeMKV (Backup-Modus)</li> <li><code>DVDPlugin.js</code> \u2014 DVD via MakeMKV (MKV-Modus)</li> <li><code>CdPlugin.js</code> \u2014 Audio-CD via cdparanoia + FFmpeg (experimentell)</li> <li><code>AudiobookPlugin.js</code> \u2014 AAX/M4B via FFmpeg mit Kapitel-Splitting</li> <li><code>ConverterPlugin.js</code> \u2014 Generische Audio/Video-Konvertierung via FFmpeg</li> <li><code>VideoDiscPlugin.js</code> \u2014 Gemeinsame Basis f\u00fcr Blu-ray/DVD (MediaInfo-Parsing)</li> </ul>"},{"location":"architecture/backend/#pipelineservicejs","title":"<code>pipelineService.js</code>","text":"<p>Zentrale Workflow-Orchestrierung.</p> <p>Aufgaben:</p> <ul> <li>Pipeline-State-Machine + Persistenz (<code>pipeline_state</code>)</li> <li>Disc-Analyse/Rip/Review/Encode via Plugin-Delegation</li> <li>Queue-Management (Jobs + <code>script|chain|wait</code> Eintr\u00e4ge)</li> <li>Retry/Re-Encode/Restart-Flows</li> <li>WebSocket-Broadcasts f\u00fcr State/Progress/Queue</li> <li>Converter-Job-Verwaltung (<code>createConverterJobFromEntry</code>, <code>uploadConverterFiles</code>, <code>startConverterJob</code>)</li> </ul> <p>Wichtige Methoden:</p> <ul> <li><code>analyzeDisc()</code></li> <li><code>selectMetadata()</code></li> <li><code>startPreparedJob()</code></li> <li><code>confirmEncodeReview()</code></li> <li><code>cancel()</code></li> <li><code>retry()</code></li> <li><code>reencodeFromRaw()</code></li> <li><code>restartReviewFromRaw()</code></li> <li><code>restartEncodeWithLastSettings()</code></li> <li><code>resumeReadyToEncodeJob()</code></li> <li><code>enqueueNonJobEntry()</code>, <code>reorderQueue()</code>, <code>removeQueueEntry()</code></li> <li><code>createConverterJobFromEntry()</code>, <code>createConverterJobsFromSelection()</code></li> <li><code>uploadConverterFiles()</code>, <code>startConverterJob()</code></li> </ul>"},{"location":"architecture/backend/#diskdetectionservicejs","title":"<code>diskDetectionService.js</code>","text":"<p>Pollt Laufwerk(e) und emittiert:</p> <ul> <li><code>discInserted</code></li> <li><code>discRemoved</code></li> <li><code>error</code></li> </ul> <p>Zusatz:</p> <ul> <li>Modus <code>auto</code> oder <code>explicit</code></li> <li>heuristische <code>mediaProfile</code>-Erkennung (<code>bluray</code>/<code>dvd</code>/<code>other</code>)</li> <li><code>rescanAndEmit()</code> f\u00fcr manuellen Trigger</li> </ul>"},{"location":"architecture/backend/#settingsservicejs","title":"<code>settingsService.js</code>","text":"<p>Settings-Layer mit Validation/Serialisierung.</p> <p>Features:</p> <ul> <li><code>getCategorizedSettings()</code> f\u00fcr UI-Form</li> <li><code>setSettingValue()</code> / <code>setSettingsBulk()</code></li> <li>profilspezifische Aufl\u00f6sung (<code>resolveEffectiveToolSettings</code>)</li> <li>CLI-Config-Building f\u00fcr MakeMKV/HandBrake/MediaInfo/FFmpeg</li> <li>HandBrake-Preset-Liste via <code>HandBrakeCLI -z</code></li> <li>MakeMKV-Registration-Command aus <code>makemkv_registration_key</code></li> <li>Pfadaufl\u00f6sung f\u00fcr alle Medienprofile inkl. Audiobook und Converter</li> </ul>"},{"location":"architecture/backend/#historyservicejs","title":"<code>historyService.js</code>","text":"<p>Historie + Dateioperationen.</p> <p>Features:</p> <ul> <li>Job-Liste/Detail inkl. Log-Tail</li> <li>Orphan-RAW-Erkennung und Import</li> <li>OMDb-Nachzuweisung</li> <li>Dateil\u00f6schung (<code>raw|movie|both</code>)</li> <li>Job-L\u00f6schung (<code>none|raw|movie|both</code>)</li> </ul>"},{"location":"architecture/backend/#audiobookservicejs","title":"<code>audiobookService.js</code>","text":"<p>Audiobook-Verarbeitung (AAX/M4B).</p> <p>Features:</p> <ul> <li>FFprobe-basierte Analyse (Kapitel, Metadaten)</li> <li>FFmpeg-basiertes Encoding mit Kapitel-Splitting</li> <li>Ausgabeformate: M4B, MP3, FLAC</li> <li>Template-basierte Pfade (<code>{author}</code>, <code>{title}</code>, <code>{year}</code>, <code>{narrator}</code>, <code>{series}</code>, <code>{part}</code>)</li> </ul>"},{"location":"architecture/backend/#activationbytesservicejs","title":"<code>activationBytesService.js</code>","text":"<p>Verwaltung von Audible-Activation-Bytes f\u00fcr AAX-DRM-Handling.</p> <p>Features:</p> <ul> <li>SHA-1-Pr\u00fcfsumme der AAX-Datei berechnen</li> <li>Activation Bytes in <code>aax_activation_bytes</code>-Tabelle cachen</li> <li>Lookup via <code>audnexService</code> (wenn konfiguriert)</li> </ul>"},{"location":"architecture/backend/#converterscanservicejs","title":"<code>converterScanService.js</code>","text":"<p>Dateisystem-\u00dcberwachung des Converter-Eingangsordners.</p> <p>Features:</p> <ul> <li>Vollst\u00e4ndiger FS-Baum (<code>getTree()</code>)</li> <li>DB-basierter Datei-Explorer (<code>getEntries()</code>)</li> <li>Manueller und automatischer Scan (Polling)</li> <li>Pfad-Normalisierung mit Traversal-Schutz</li> <li>WebSocket-Broadcast: <code>CONVERTER_SCAN_UPDATE</code></li> </ul>"},{"location":"architecture/backend/#downloadservicejs","title":"<code>downloadService.js</code>","text":"<p>Download-Queue f\u00fcr Ausgabedateien aus der Historie.</p> <p>Features:</p> <ul> <li>Job in Download-Queue einreihen (<code>enqueueHistoryJob</code>)</li> <li>ZIP-Archiv aus Ausgabedateien erstellen (<code>archiver</code>)</li> <li>Datei-Streaming via <code>res.download()</code></li> <li>WebSocket-Broadcast: <code>DOWNLOADS_UPDATED</code> bei Status-\u00c4nderungen</li> <li>Download-Item l\u00f6schen</li> </ul>"},{"location":"architecture/backend/#cronservicejs","title":"<code>cronService.js</code>","text":"<p>Integriertes Cron-System ohne externe Parser-Library.</p> <p>Features:</p> <ul> <li>5-Feld-Cron-Parser + <code>nextRun</code>-Berechnung</li> <li>Quellen: <code>script</code> oder <code>chain</code></li> <li>Laufzeitlogs (<code>cron_run_logs</code>)</li> <li>manuelles Triggern</li> <li>WebSocket-Events: <code>CRON_JOBS_UPDATED</code>, <code>CRON_JOB_UPDATED</code></li> </ul>"},{"location":"architecture/backend/#runtimeactivityservicejs","title":"<code>runtimeActivityService.js</code>","text":"<p>In-Memory-Tracking aller laufenden und k\u00fcrzlich abgeschlossenen Aktivit\u00e4ten (Skripte, Ketten, Cron-Jobs, Tasks).</p> <p>Features:</p> <ul> <li><code>startActivity(type, payload)</code> \u2192 Aktivit\u00e4t registrieren, ID zur\u00fcckgeben</li> <li><code>updateActivity(id, patch)</code> \u2192 Laufende Aktivit\u00e4t aktualisieren</li> <li><code>completeActivity(id, payload)</code> \u2192 Aktivit\u00e4t abschlie\u00dfen und in <code>recent</code> verschieben</li> <li><code>setControls(id, { cancel, nextStep })</code> \u2192 Steuer-Handler registrieren (f\u00fcr <code>canCancel</code>/<code>canNextStep</code>)</li> <li><code>requestCancel(id)</code> / <code>requestNextStep(id)</code> \u2192 Steuer-Handler aufrufen</li> <li><code>clearRecent()</code> \u2192 Abgeschlossene Aktivit\u00e4ten l\u00f6schen</li> <li><code>getSnapshot()</code> \u2192 Snapshot mit <code>active</code> + <code>recent</code> + <code>updatedAt</code></li> <li>Broadcasts <code>RUNTIME_ACTIVITY_CHANGED</code> \u00fcber WebSocket bei jeder \u00c4nderung</li> </ul> <p>Limits:</p> <ul> <li><code>recent</code> max. 120 Eintr\u00e4ge</li> <li><code>stdout</code>/<code>stderr</code>/<code>output</code> max. 12.000 Zeichen</li> <li><code>message</code>/<code>errorMessage</code> max. 2.000 Zeichen</li> </ul> <p>Vollst\u00e4ndige API-Dokumentation: Runtime Activities API</p>"},{"location":"architecture/backend/#weitere-services","title":"Weitere Services","text":"<ul> <li><code>scriptService.js</code> (CRUD + Test + Wrapper-Ausf\u00fchrung)</li> <li><code>scriptChainService.js</code> (CRUD + Step-Execution)</li> <li><code>userPresetService.js</code> (HandBrake User-Presets)</li> <li><code>hardwareMonitorService.js</code> (CPU/RAM/GPU/Storage)</li> <li><code>websocketService.js</code> (Client-Registry + Broadcast)</li> <li><code>notificationService.js</code> (PushOver)</li> <li><code>cdRipService.js</code> (CD-Ripping mit cdparanoia)</li> <li><code>musicBrainzService.js</code> (MusicBrainz-Metadaten-Lookup)</li> <li><code>audnexService.js</code> (Audnex-API f\u00fcr Audiobook-Metadaten)</li> <li><code>coverArtRecoveryService.js</code> (Cover-Art-Wiederherstellung)</li> <li><code>thumbnailService.js</code> (Vorschaubild-Generierung)</li> <li><code>omdbService.js</code> (OMDb-Metadaten-Suche)</li> <li><code>logger.js</code> (rotierende Datei-Logs)</li> </ul>"},{"location":"architecture/backend/#bootstrapping-srcindexjs","title":"Bootstrapping (<code>src/index.js</code>)","text":"<p>Beim Start:</p> <ol> <li>DB init/migrate</li> <li>Pipeline-Init (inkl. Plugin-Registry)</li> <li>Cron-Init</li> <li>Express-Routes + Error-Handler</li> <li>WebSocket-Server auf <code>/ws</code></li> <li>Hardware-Monitoring-Init</li> <li>Disk-Detection-Start</li> <li>Converter-Scan-Service-Init (Polling falls aktiviert)</li> </ol>"},{"location":"architecture/database/","title":"Datenbank","text":"<p>Ripster verwendet SQLite (<code>backend/data/ripster.db</code>).</p>"},{"location":"architecture/database/#tabellen","title":"Tabellen","text":"<pre><code>settings_schema\nsettings_values\njobs\njob_lineage_artifacts\njob_output_folders\npipeline_state\nscripts\nscript_chains\nscript_chain_steps\nuser_presets\ncron_jobs\ncron_run_logs\naax_activation_bytes\nuser_prefs\nconverter_scan_entries\n</code></pre>"},{"location":"architecture/database/#jobs","title":"<code>jobs</code>","text":"<p>Speichert Pipeline-Lifecycle und Artefakte pro Job.</p> <p>Zentrale Felder:</p> <ul> <li>Metadaten: <code>title</code>, <code>year</code>, <code>imdb_id</code>, <code>poster_url</code>, <code>omdb_json</code>, <code>selected_from_omdb</code></li> <li>Laufzeit: <code>start_time</code>, <code>end_time</code>, <code>status</code>, <code>last_state</code></li> <li>Pfade: <code>raw_path</code>, <code>output_path</code>, <code>encode_input_path</code></li> <li>Tool-Ausgaben: <code>makemkv_info_json</code>, <code>handbrake_info_json</code>, <code>mediainfo_info_json</code>, <code>encode_plan_json</code></li> <li>Kontrolle: <code>encode_review_confirmed</code>, <code>rip_successful</code>, <code>error_message</code></li> <li>Medientyp: <code>media_type</code> (<code>bluray|dvd|cd|audiobook|converter</code>)</li> <li>Job-Typ/Beziehung: <code>job_kind</code>, <code>parent_job_id</code></li> <li>Multipart-/Disc-Merkmale: <code>is_multipart_movie</code>, <code>disc_number</code>, <code>metadata_fingerprint</code></li> <li>Pr\u00fcfsumme: <code>aax_checksum</code> (f\u00fcr AAX-Dateien)</li> <li>Audit: <code>created_at</code>, <code>updated_at</code></li> </ul> <p>Typische <code>job_kind</code>-Werte:</p> <ul> <li>Standard: <code>bluray</code>, <code>dvd</code>, <code>cd</code>, <code>audiobook</code>, <code>converter_audio</code>, <code>converter_video</code>, <code>converter_iso</code></li> <li>Serien: <code>dvd_series_container</code>, <code>dvd_series_child</code></li> <li>Multipart Film: <code>multipart_movie_container</code>, <code>multipart_movie_child</code></li> </ul> <p>Wichtige Indizes:</p> <ul> <li><code>idx_jobs_status</code></li> <li><code>idx_jobs_parent_job_id</code></li> <li><code>idx_jobs_job_kind</code></li> <li><code>idx_jobs_disc_number</code></li> <li><code>idx_jobs_metadata_fingerprint</code></li> </ul>"},{"location":"architecture/database/#job_lineage_artifacts","title":"<code>job_lineage_artifacts</code>","text":"<p>Verkn\u00fcpft Jobs mit ihren Quell-Jobs (z. B. Re-Encode aus einem vorherigen Rip).</p> <p>Felder:</p> <ul> <li><code>job_id</code> \u2014 der abgeleitete Job</li> <li><code>source_job_id</code> \u2014 der Quell-Job</li> <li><code>artifact_type</code> \u2014 Art der Verkn\u00fcpfung (z. B. <code>reencode</code>, <code>restart_review</code>)</li> <li><code>created_at</code></li> </ul>"},{"location":"architecture/database/#job_output_folders","title":"<code>job_output_folders</code>","text":"<p>Verfolgt Ausgabepfade pro Job (mehrere Ausgaben m\u00f6glich, z. B. bei Kapitel-Splitting).</p> <p>Felder:</p> <ul> <li><code>job_id</code></li> <li><code>folder_path</code></li> <li><code>label</code> \u2014 optionaler Bezeichner</li> <li><code>created_at</code></li> </ul>"},{"location":"architecture/database/#pipeline_state","title":"<code>pipeline_state</code>","text":"<p>Singleton-Tabelle (<code>id = 1</code>) f\u00fcr aktiven Snapshot:</p> <ul> <li><code>state</code></li> <li><code>active_job_id</code></li> <li><code>progress</code></li> <li><code>eta</code></li> <li><code>status_text</code></li> <li><code>context_json</code></li> <li><code>updated_at</code></li> </ul>"},{"location":"architecture/database/#settings_schema-settings_values","title":"<code>settings_schema</code> + <code>settings_values</code>","text":"<ul> <li><code>settings_schema</code>: Definition (Typ, Default, Validation, Reihenfolge)</li> <li><code>settings_values</code>: aktueller Wert pro Key</li> </ul>"},{"location":"architecture/database/#scripts-script_chains-script_chain_steps","title":"<code>scripts</code>, <code>script_chains</code>, <code>script_chain_steps</code>","text":"<ul> <li><code>scripts</code>: Shell-Skripte (<code>name</code>, <code>script_body</code>, <code>order_index</code>)</li> <li><code>script_chains</code>: Ketten (<code>name</code>, <code>order_index</code>)</li> <li><code>script_chain_steps</code>: Schritte je Kette</li> <li><code>step_type</code>: <code>script</code> oder <code>wait</code></li> <li><code>script_id</code> oder <code>wait_seconds</code></li> </ul>"},{"location":"architecture/database/#user_presets","title":"<code>user_presets</code>","text":"<p>Benannte HandBrake-Preset-Sets:</p> <ul> <li><code>name</code></li> <li><code>media_type</code> (<code>bluray|dvd|other|all</code>)</li> <li><code>handbrake_preset</code></li> <li><code>extra_args</code></li> <li><code>description</code></li> </ul>"},{"location":"architecture/database/#cron_jobs-cron_run_logs","title":"<code>cron_jobs</code> + <code>cron_run_logs</code>","text":"<ul> <li><code>cron_jobs</code>: Zeitplan + Status</li> <li><code>cron_run_logs</code>: einzelne L\u00e4ufe</li> <li><code>status</code>: <code>running|success|error</code></li> <li><code>output</code></li> <li><code>error_message</code></li> </ul>"},{"location":"architecture/database/#aax_activation_bytes","title":"<code>aax_activation_bytes</code>","text":"<p>Cache f\u00fcr Audible-Activation-Bytes (AAX-DRM).</p> <p>Felder:</p> <ul> <li><code>checksum</code> \u2014 SHA-1-Pr\u00fcfsumme der AAX-Datei (Prim\u00e4rschl\u00fcssel)</li> <li><code>activation_bytes</code> \u2014 Hex-String der Activation Bytes</li> <li><code>created_at</code></li> </ul>"},{"location":"architecture/database/#user_prefs","title":"<code>user_prefs</code>","text":"<p>Benutzer-spezifische Einstellungen (Key-Value).</p> <p>Felder:</p> <ul> <li><code>key</code></li> <li><code>value</code></li> <li><code>updated_at</code></li> </ul>"},{"location":"architecture/database/#converter_scan_entries","title":"<code>converter_scan_entries</code>","text":"<p>DB-seitige Erfassung gescannter Dateien im Converter-Eingangsordner.</p> <p>Felder:</p> <ul> <li><code>rel_path</code> \u2014 relativer Pfad zum <code>converter_raw_dir</code></li> <li><code>is_dir</code> \u2014 Verzeichnis-Flag</li> <li><code>size</code> \u2014 Dateigr\u00f6\u00dfe in Bytes</li> <li><code>modified_at</code> \u2014 Datei-\u00c4nderungsdatum</li> <li><code>job_id</code> \u2014 zugewiesener Converter-Job (falls vorhanden)</li> <li><code>scanned_at</code></li> </ul>"},{"location":"architecture/database/#migrationrecovery","title":"Migration/Recovery","text":"<p>Beim Start werden Schema und Settings-Metadaten automatisch abgeglichen.</p> <p>Bei korruptem SQLite-File:</p> <ol> <li>Datei wird nach <code>backend/data/corrupt-backups/</code> verschoben</li> <li>neue DB wird initialisiert</li> <li>Schema wird neu aufgebaut</li> </ol>"},{"location":"architecture/database/#direkte-inspektion","title":"Direkte Inspektion","text":"<pre><code>sqlite3 backend/data/ripster.db\n\n.mode table\nSELECT id, status, title, media_type, created_at FROM jobs ORDER BY created_at DESC;\nSELECT id, parent_job_id, job_kind, is_multipart_movie, disc_number FROM jobs ORDER BY created_at DESC;\nSELECT key, value FROM settings_values ORDER BY key;\nSELECT checksum, activation_bytes FROM aax_activation_bytes;\nSELECT rel_path, job_id FROM converter_scan_entries;\n</code></pre>"},{"location":"architecture/frontend/","title":"Frontend-Komponenten","text":"<p>Frontend: React + PrimeReact + Vite.</p>"},{"location":"architecture/frontend/#hauptseiten","title":"Hauptseiten","text":""},{"location":"architecture/frontend/#jobsinboxpagejsx","title":"<code>JobsInboxPage.jsx</code>","text":"<p>Zentrale Job-Inbox:</p> <ul> <li>einheitliche Liste \u00fcber Ripper-, Converter- und Audiobook-Jobs</li> <li>View-/Status-Filter, Suche, Paginierung</li> <li>Schnellsprung in passende Detailseite (<code>Ripper</code>/<code>Converter</code>)</li> </ul>"},{"location":"architecture/frontend/#ripperpagejsx","title":"<code>RipperPage.jsx</code>","text":"<p>Pipeline-Steuerung:</p> <ul> <li>Status/Progress/ETA</li> <li>Metadaten-Dialog</li> <li>Playlist-Entscheidung</li> <li>Review-Panel (Track-Auswahl)</li> <li>Queue-Interaktion (reorder/add/remove)</li> <li>Job-Aktionen (Start/Cancel/Retry/Re-Encode)</li> <li>Hardware-Monitoring-Anzeige</li> <li>Aktivit\u00e4ts-Tracking (Skripte, Ketten, Cron)</li> </ul>"},{"location":"architecture/frontend/#settingspagejsx","title":"<code>SettingsPage.jsx</code>","text":"<p>Konfiguration:</p> <ul> <li>dynamisches Settings-Formular (<code>DynamicSettingsForm</code>)</li> <li>Skripte/Ketten inkl. Reorder/Test</li> <li>User-Presets</li> <li>Cron-Jobs (<code>CronJobsTab</code>)</li> </ul>"},{"location":"architecture/frontend/#historypagejsx","title":"<code>HistoryPage.jsx</code>","text":"<p>Historie:</p> <ul> <li>Job-Liste/Filter</li> <li>Job-Details + Logs</li> <li>OMDb-Nachzuweisung</li> <li>Re-Encode/Restart-Workflows</li> </ul>"},{"location":"architecture/frontend/#converterpagejsx","title":"<code>ConverterPage.jsx</code>","text":"<p>Datei-Converter:</p> <ul> <li>Datei-Explorer des Converter-Eingangsordners (Baum-Ansicht)</li> <li>Upload von Audio/Video-Dateien (bis zu 50 Dateien gleichzeitig)</li> <li>Dateiverwaltung: Umbenennen, Verschieben, L\u00f6schen, Ordner erstellen</li> <li>Jobs aus Dateiauswahl erstellen</li> <li>Converter-Job-Konfiguration (Ausgabeformat, Preset, Tracks)</li> <li>Job-Start, Abbruch, L\u00f6schung</li> <li>Automatischer Scan-Status</li> </ul>"},{"location":"architecture/frontend/#audiobookspagejsx","title":"<code>AudiobooksPage.jsx</code>","text":"<p>Dedizierter AAX-/Audiobook-Flow:</p> <ul> <li>Upload-Panel f\u00fcr AAX-Dateien</li> <li>aktive Audiobook-Jobkarten mit Start/Cancel/Retry/Delete</li> <li>Audiobook-Konfigurationspanel und Output-Explorer</li> </ul>"},{"location":"architecture/frontend/#downloadspagejsx","title":"<code>DownloadsPage.jsx</code>","text":"<p>Download-Queue:</p> <ul> <li>Ausgabedateien aus der Job-Historie in die Queue einreihen</li> <li>Download-Status verfolgen (ausstehend, wird verarbeitet, bereit, fehlgeschlagen)</li> <li>Dateien als ZIP herunterladen</li> <li>Download-Eintr\u00e4ge l\u00f6schen</li> </ul>"},{"location":"architecture/frontend/#databasepagejsx","title":"<code>DatabasePage.jsx</code>","text":"<p>Expert-Modus:</p> <ul> <li>Tabellarische Rohsicht der Job-Datenbank</li> <li>Orphan-RAW-Import</li> </ul>"},{"location":"architecture/frontend/#wichtige-komponenten","title":"Wichtige Komponenten","text":"<ul> <li><code>PipelineStatusCard.jsx</code> \u2014 Pipeline-Status-Anzeige mit Progress</li> <li><code>MetadataSelectionDialog.jsx</code> \u2014 OMDb-Metadaten-Auswahl</li> <li><code>MediaInfoReviewPanel.jsx</code> \u2014 Track-Auswahl-Interface (Video/Audio/Untertitel)</li> <li><code>JobDetailDialog.jsx</code> \u2014 Job-Detailansicht mit Logs</li> <li><code>CronJobsTab.jsx</code> \u2014 Cron-Job-Verwaltung</li> <li><code>DynamicSettingsForm.jsx</code> \u2014 Schema-gesteuertes Einstellungsformular</li> <li><code>ConverterJobCard.jsx</code> \u2014 Converter-Job-Darstellung</li> <li><code>CdRipConfigPanel.jsx</code> \u2014 Konfiguration f\u00fcr Audio-CD-Ripping</li> </ul>"},{"location":"architecture/frontend/#api-client-apiclientjs","title":"API-Client (<code>api/client.js</code>)","text":"<ul> <li>zentraler <code>request()</code> mit JSON-Handling</li> <li>Fehlerobjekt aus API wird auf <code>Error(message)</code> gemappt</li> <li><code>VITE_API_BASE</code> default <code>/api</code></li> </ul>"},{"location":"architecture/frontend/#websocket-hooksusewebsocketjs","title":"WebSocket (<code>hooks/useWebSocket.js</code>)","text":"<ul> <li>URL: <code>VITE_WS_URL</code> oder automatisch <code>ws(s)://&lt;host&gt;/ws</code></li> <li>Auto-Reconnect mit 1500ms Intervall</li> </ul> <p>In <code>App.jsx</code> werden u. a. verarbeitet:</p> <ul> <li><code>PIPELINE_STATE_CHANGED</code></li> <li><code>PIPELINE_PROGRESS</code></li> <li><code>PIPELINE_QUEUE_CHANGED</code></li> <li>Disk erkannt / Disk entfernt</li> <li><code>HARDWARE_MONITOR_UPDATE</code></li> <li><code>DOWNLOADS_UPDATED</code></li> <li><code>CONVERTER_SCAN_UPDATE</code></li> <li><code>RUNTIME_ACTIVITY_CHANGED</code></li> </ul>"},{"location":"architecture/frontend/#buildrun","title":"Build/Run","text":"<pre><code># dev\nnpm run dev --prefix frontend\n\n# prod build\nnpm run build --prefix frontend\n</code></pre>"},{"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":"<p><code>pipelineService</code> h\u00e4lt einen Snapshot der State-Machine und broadcastet \u00c4nderungen sofort via WebSocket.</p> <pre><code>State-\u00c4nderung -&gt; PIPELINE_STATE_CHANGED/PIPELINE_PROGRESS -&gt; Frontend-Update\n</code></pre>"},{"location":"architecture/overview/#service-layer","title":"Service-Layer","text":"<pre><code>Route -&gt; Service -&gt; DB/Tool-Execution\n</code></pre> <p>Routes enthalten kaum Business-Logik.</p>"},{"location":"architecture/overview/#schema-getriebene-settings","title":"Schema-getriebene Settings","text":"<p>Settings sind DB-schema-getrieben (<code>settings_schema</code> + <code>settings_values</code>), UI rendert dynamisch aus diesen Daten.</p>"},{"location":"architecture/overview/#echtzeit-kommunikation","title":"Echtzeit-Kommunikation","text":"<p>WebSocket l\u00e4uft auf <code>/ws</code>.</p> <p>Wichtige Events:</p> <ul> <li><code>PIPELINE_STATE_CHANGED</code>, <code>PIPELINE_PROGRESS</code>, <code>PIPELINE_QUEUE_CHANGED</code></li> <li>Disk erkannt, Disk entfernt</li> <li><code>HARDWARE_MONITOR_UPDATE</code></li> <li><code>SETTINGS_UPDATED</code>, <code>SETTINGS_BULK_UPDATED</code></li> <li><code>SETTINGS_SCRIPTS_UPDATED</code>, <code>SETTINGS_SCRIPT_CHAINS_UPDATED</code>, <code>USER_PRESETS_UPDATED</code></li> <li><code>CRON_JOBS_UPDATED</code>, <code>CRON_JOB_UPDATED</code></li> <li><code>PIPELINE_ERROR</code>, <code>DISK_DETECTION_ERROR</code></li> </ul>"},{"location":"architecture/overview/#prozessausfuhrung","title":"Prozessausf\u00fchrung","text":"<p>Externe Tools werden als Child-Processes gestartet (<code>processRunner</code>):</p> <ul> <li>Streaming von stdout/stderr</li> <li>Progress-Parsing (<code>progressParsers.js</code>)</li> <li>kontrollierter Abbruch (SIGINT/SIGKILL-Fallback)</li> </ul>"},{"location":"architecture/overview/#persistenz","title":"Persistenz","text":"<p>SQLite-Datei: <code>backend/data/ripster.db</code></p> <p>Kern-Tabellen:</p> <ul> <li><code>jobs</code>, <code>pipeline_state</code></li> <li><code>settings_schema</code>, <code>settings_values</code></li> <li><code>scripts</code>, <code>script_chains</code>, <code>script_chain_steps</code></li> <li><code>user_presets</code></li> <li><code>cron_jobs</code>, <code>cron_run_logs</code></li> </ul> <p>Beim Start werden Schema und Settings-Migrationen automatisch ausgef\u00fchrt.</p>"},{"location":"architecture/overview/#fehlerbehandlung","title":"Fehlerbehandlung","text":"<p>Zentrales Error-Handling liefert:</p> <pre><code>{\n \"error\": {\n \"message\": \"...\",\n \"statusCode\": 400,\n \"reqId\": \"...\",\n \"details\": []\n }\n}\n</code></pre> <p>Fehlgeschlagene Jobs bleiben in der Historie (<code>Fehler</code> oder <code>Abgebrochen</code>) und k\u00f6nnen erneut gestartet werden.</p>"},{"location":"architecture/overview/#cors-runtime-konfig","title":"CORS &amp; Runtime-Konfig","text":"<ul> <li><code>CORS_ORIGIN</code> default: <code>*</code></li> <li><code>LOG_LEVEL</code> default: <code>info</code></li> <li>DB-/Log-Pfade \u00fcber <code>DB_PATH</code>/<code>LOG_DIR</code> konfigurierbar</li> </ul>"},{"location":"configuration/","title":"Anhang: Konfiguration","text":"<p>Dieser Abschnitt ist die technische Referenz zu allen Konfigurationsarten in Ripster.</p>"},{"location":"configuration/#inhalte","title":"Inhalte","text":"<ul> <li> <p> Einstellungsreferenz</p> <p>Vollst\u00e4ndige Liste aller UI-Settings (Typ, Default, Hinweise).</p> <p> Einstellungsreferenz</p> </li> <li> <p> Umgebungsvariablen</p> <p><code>backend/.env</code> und <code>frontend/.env</code> inkl. Priorit\u00e4ten.</p> <p> Umgebungsvariablen</p> </li> </ul>"},{"location":"configuration/#zuruck-zum-handbuch","title":"Zur\u00fcck zum Handbuch","text":"<ul> <li>Benutzerhandbuch \u00dcberblick</li> </ul>"},{"location":"configuration/environment/","title":"Umgebungsvariablen","text":"<p>Umgebungsvariablen steuern Backend/Vite au\u00dferhalb der DB-basierten UI-Settings.</p>"},{"location":"configuration/environment/#backend-backendenv","title":"Backend (<code>backend/.env</code>)","text":"Variable Default (Code) Beschreibung <code>PORT</code> <code>3001</code> Express-Port <code>DB_PATH</code> <code>backend/data/ripster.db</code> SQLite-Datei (relativ zu <code>backend/</code>) <code>LOG_DIR</code> <code>backend/logs</code> Fallback-Logverzeichnis (wenn <code>log_dir</code>-Setting nicht gesetzt/lesbar) <code>CORS_ORIGIN</code> <code>*</code> CORS-Origin f\u00fcr API <code>LOG_LEVEL</code> <code>info</code> <code>debug</code>, <code>info</code>, <code>warn</code>, <code>error</code> <p>Beispiel:</p> <pre><code>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</code></pre> <p>Hinweis: <code>backend/.env.example</code> enth\u00e4lt bewusst dev-freundliche Werte (z. B. lokaler <code>CORS_ORIGIN</code>).</p>"},{"location":"configuration/environment/#frontend-frontendenv","title":"Frontend (<code>frontend/.env</code>)","text":"Variable Default Beschreibung <code>VITE_API_BASE</code> <code>/api</code> API-Basis f\u00fcr Fetch-Client <code>VITE_WS_URL</code> automatisch aus <code>window.location</code> + <code>/ws</code> Optional explizite WebSocket-URL <code>VITE_PUBLIC_ORIGIN</code> leer \u00d6ffentliche Vite-Origin (Remote-Dev) <code>VITE_ALLOWED_HOSTS</code> <code>true</code> Komma-separierte Hostliste f\u00fcr Vite <code>allowedHosts</code> <code>VITE_HMR_PROTOCOL</code> abgeleitet aus <code>VITE_PUBLIC_ORIGIN</code> HMR-Protokoll (<code>ws</code>/<code>wss</code>) <code>VITE_HMR_HOST</code> abgeleitet aus <code>VITE_PUBLIC_ORIGIN</code> HMR-Host <code>VITE_HMR_CLIENT_PORT</code> abgeleitet aus <code>VITE_PUBLIC_ORIGIN</code> HMR-Client-Port <p>Beispiele:</p> <pre><code># lokal (mit Vite-Proxy)\nVITE_API_BASE=/api\n</code></pre> <pre><code># 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</code></pre>"},{"location":"configuration/environment/#prioritat","title":"Priorit\u00e4t","text":"<ol> <li>Prozess-Umgebungsvariablen</li> <li><code>.env</code></li> <li>Code-Defaults</li> </ol>"},{"location":"configuration/settings-reference/","title":"Einstellungsreferenz","text":"<p>Diese Seite dokumentiert alle UI-Settings mit ihrer Wirkung auf Flows, Pfade, Queue und Automatisierung.</p>"},{"location":"configuration/settings-reference/#wie-ripster-settings-auflost","title":"Wie Ripster Settings aufl\u00f6st","text":"<p>Ripster arbeitet intern mit profilspezifischen Settings (<code>bluray</code>, <code>dvd</code>, <code>cd</code>, <code>audiobook</code>) und bildet sie auf effektive Laufzeitwerte ab.</p> <p>Wichtige Regeln:</p> <ol> <li>F\u00fcr <code>bluray</code> wird bei fehlenden profilierten Toolwerten auf <code>dvd</code> zur\u00fcckgefallen, und umgekehrt.</li> <li>F\u00fcr <code>cd</code> und <code>audiobook</code> gibt es nur das eigene Profil.</li> <li>Strikt profilgebundene Pfadkeys (<code>raw_dir</code>, <code>movie_dir</code>, <code>series_dir</code> plus Owner) fallen bei leeren Werten auf Installationsdefaults zur\u00fcck.</li> </ol> <p>Installationsdefaults:</p> <ul> <li>RAW: <code>data/output/raw</code></li> <li>Movies: <code>data/output/movies</code></li> <li>Series: <code>data/output/series</code></li> <li>CD: <code>data/output/cd</code></li> <li>Audiobook RAW: <code>data/output/audiobook-raw</code></li> <li>Audiobook Output: <code>data/output/audiobooks</code></li> <li>Converter RAW: <code>data/output/converter-raw</code></li> <li>Converter Video: <code>data/output/converted-movies</code></li> <li>Converter Audio: <code>data/output/converted-audio</code></li> </ul>"},{"location":"configuration/settings-reference/#kritische-wirkungsbereiche","title":"Kritische Wirkungsbereiche","text":""},{"location":"configuration/settings-reference/#1-pfad-und-template-einstellungen","title":"1) Pfad- und Template-Einstellungen","text":"<p>Diese Settings bestimmen direkt:</p> <ul> <li>wo RAW landet</li> <li>wo finale Ausgaben landen</li> <li>wie Ordner/Dateinamen gerendert werden</li> </ul> <p>Relevante Besonderheiten:</p> <ul> <li>Serien-RAW nutzt bei Serienworkflow bevorzugt <code>raw_dir_*_series</code>.</li> <li>Serien-Ausgabe nutzt <code>series_dir_*</code> und Serien-Templates (Episode/Multi-Episode).</li> <li>Film-Ausgabe nutzt <code>movie_dir_*</code> und Film-Template.</li> <li>Encode schreibt zun\u00e4chst in <code>Incomplete_job-&lt;id&gt;</code> und finalisiert danach in den Zielpfad.</li> </ul>"},{"location":"configuration/settings-reference/#2-queue-und-parallelitats-settings","title":"2) Queue- und Parallelit\u00e4ts-Settings","text":"<p>Die vier Limits greifen kombiniert:</p> <ul> <li><code>pipeline_max_parallel_jobs</code> (Film/Video-Lane)</li> <li><code>pipeline_max_parallel_cd_encodes</code> (Audio/CD-Lane)</li> <li><code>pipeline_max_total_encodes</code> (globaler Deckel)</li> <li><code>pipeline_cd_bypasses_queue</code> (Audio-Lane darf Filmwarteschlange umgehen)</li> </ul> <p>Effekt:</p> <ul> <li>Film und Audio haben getrennte Lane-Limits.</li> <li>Das Gesamtlimit \u00fcberstimmt beide, sobald erreicht.</li> <li>Queue kann zus\u00e4tzlich Nicht-Job-Eintr\u00e4ge (<code>Skript</code>, <code>Kette</code>, <code>Warten</code>) enthalten.</li> </ul>"},{"location":"configuration/settings-reference/#3-serien-metadaten-settings","title":"3) Serien-Metadaten-Settings","text":"<p>Diese steuern die Serienqualit\u00e4t direkt:</p> <ul> <li>TMDb-Token aktiviert TMDb-gest\u00fctzte Serienmetadaten.</li> <li>Sprache/Fallback-Sprachen beeinflussen Titel- und Episodenaufl\u00f6sung.</li> <li>Ordnungspr\u00e4ferenz steuert Episode-Order.</li> <li>Disc-Profile-Reuse verbessert automatische Zuordnung \u00fcber mehrere Discs.</li> </ul>"},{"location":"configuration/settings-reference/#4-tool-settings","title":"4) Tool-Settings","text":"<p>Toolfelder (<code>makemkv_*</code>, <code>mediainfo_*</code>, <code>handbrake_*</code>, <code>ffmpeg_*</code>, <code>ffprobe_*</code>, <code>cdparanoia_*</code>) wirken unmittelbar auf die tats\u00e4chlich ausgef\u00fchrten CLI-Aufrufe.</p> <p>Typische Auswirkungen:</p> <ul> <li>falscher Toolpfad =&gt; fr\u00fcher Pipelinefehler</li> <li>ge\u00e4nderte Extra-Args =&gt; anderes Analyze-/Encode-Verhalten</li> <li>ge\u00e4nderte Presets =&gt; andere HandBrake-Ausgaben</li> </ul>"},{"location":"configuration/settings-reference/#template-platzhalter","title":"Template-Platzhalter","text":""},{"location":"configuration/settings-reference/#film-templates","title":"Film-Templates","text":"<ul> <li><code>${title}</code>, <code>${year}</code>, <code>${imdbId}</code></li> </ul>"},{"location":"configuration/settings-reference/#serien-templates","title":"Serien-Templates","text":"<ul> <li><code>{seriesTitle}</code>, <code>{seasonNr}</code>, <code>{episodeNr}</code>, <code>{episodeNoStart}</code>, <code>{episodeNoEnd}</code></li> <li><code>{episodeRange}</code>, <code>{episodeTitle}</code>, <code>{parts}</code>, <code>{discNr}</code>, <code>{year}</code>, <code>{language}</code></li> <li>f\u00fchrende Null m\u00f6glich mit <code>{0:seasonNr}</code> etc.</li> </ul>"},{"location":"configuration/settings-reference/#audiobook-templates","title":"Audiobook-Templates","text":"<ul> <li><code>{author}</code>, <code>{title}</code>, <code>{year}</code>, <code>{narrator}</code>, <code>{series}</code>, <code>{part}</code>, <code>{format}</code></li> </ul>"},{"location":"configuration/settings-reference/#converter-templates","title":"Converter-Templates","text":"<ul> <li>Video: <code>{title}</code>, <code>{year}</code></li> <li>Audio: <code>{artist}</code>, <code>{title}</code>, <code>{album}</code>, <code>{year}</code>, <code>{trackNr}</code></li> </ul>"},{"location":"configuration/settings-reference/#vollstandige-feldliste","title":"Vollst\u00e4ndige Feldliste","text":""},{"location":"configuration/settings-reference/#kategorie-benachrichtigungen","title":"Kategorie: Benachrichtigungen","text":"GUI-Feld Key Typ Default Wirkung im System <code>Expertenmodus</code> <code>ui_expert_mode</code> <code>boolean</code> <code>false</code> Schaltet erweiterte Einstellungen in der UI ein. <code>PushOver aktiviert</code> <code>pushover_enabled</code> <code>boolean</code> <code>false</code> Master-Schalter f\u00fcr PushOver Versand. <code>PushOver Token</code> <code>pushover_token</code> <code>string</code> <code>leer</code> Application Token f\u00fcr PushOver. <code>PushOver User</code> <code>pushover_user</code> <code>string</code> <code>leer</code> User-Key f\u00fcr PushOver. <code>PushOver Device (optional)</code> <code>pushover_device</code> <code>string</code> <code>leer</code> Optionales Ziel-Device in PushOver. <code>PushOver Titel-Pr\u00e4fix</code> <code>pushover_title_prefix</code> <code>string</code> <code>Ripster</code> Prefix im PushOver Titel. <code>PushOver Priority</code> <code>pushover_priority</code> <code>number</code> <code>0</code> Priorit\u00e4t -2 bis 2. <code>PushOver Timeout (ms)</code> <code>pushover_timeout_ms</code> <code>number</code> <code>7000</code> HTTP Timeout f\u00fcr PushOver Requests. <code>Bei Metadaten-Auswahl senden</code> <code>pushover_notify_metadata_ready</code> <code>boolean</code> <code>true</code> Sendet wenn Metadaten zur Auswahl bereitstehen. <code>Bei Rip-Start senden</code> <code>pushover_notify_rip_started</code> <code>boolean</code> <code>true</code> Sendet beim Start des MakeMKV-Rips. <code>Bei Encode-Start senden</code> <code>pushover_notify_encoding_started</code> <code>boolean</code> <code>true</code> Sendet beim Start von HandBrake. <code>Bei Erfolg senden</code> <code>pushover_notify_job_finished</code> <code>boolean</code> <code>true</code> Sendet bei erfolgreich abgeschlossenem Job. <code>Bei Fehler senden</code> <code>pushover_notify_job_error</code> <code>boolean</code> <code>true</code> Sendet bei Fehlern in der Pipeline. <code>Bei Abbruch senden</code> <code>pushover_notify_job_cancelled</code> <code>boolean</code> <code>true</code> Sendet wenn Job manuell abgebrochen wurde. <code>Bei Re-Encode Start senden</code> <code>pushover_notify_reencode_started</code> <code>boolean</code> <code>true</code> Sendet beim Start von RAW Re-Encode. <code>Bei Re-Encode Erfolg senden</code> <code>pushover_notify_reencode_finished</code> <code>boolean</code> <code>true</code> Sendet bei erfolgreichem RAW Re-Encode."},{"location":"configuration/settings-reference/#kategorie-converter","title":"Kategorie: Converter","text":"GUI-Feld Key Typ Default Wirkung im System <code>Erlaubte Datei-Endungen*</code> <code>converter_scan_extensions</code> <code>string</code> <code>mkv,mp4,m2ts,iso,avi,mov,flac,mp3,wav,m4a,ogg,opus</code> Erlaubte Datei-Endungen f\u00fcr den Converter-Scan (ohne Punkt). Wird in der UI als Checkbox-Liste gepflegt. <code>Auto-Scan (Polling)</code> <code>converter_polling_enabled</code> <code>boolean</code> <code>false</code> Converter Raw-Ordner automatisch in regelm\u00e4\u00dfigen Abst\u00e4nden scannen. <code>Polling-Intervall (Sekunden)</code> <code>converter_polling_interval</code> <code>number</code> <code>300</code> Intervall f\u00fcr automatischen Scan des Converter-Ordners."},{"location":"configuration/settings-reference/#kategorie-laufwerk","title":"Kategorie: Laufwerk","text":"GUI-Feld Key Typ Default Wirkung im System <code>Laufwerksmodus</code> <code>drive_mode</code> <code>select</code> <code>auto</code> Auto-Discovery oder explizites Device. <code>Device Pfad</code> <code>drive_device</code> <code>path</code> <code>/dev/sr0</code> Nur f\u00fcr expliziten Modus, z.B. /dev/sr0. <code>MakeMKV Source Index</code> <code>makemkv_source_index</code> <code>number</code> <code>0</code> Disc Index im Auto-Modus. <code>Automatische Disk-Erkennung</code> <code>disc_auto_detection_enabled</code> <code>boolean</code> <code>true</code> Wenn deaktiviert, findet keine automatische Laufwerkspr\u00fcfung statt. Neue Disks werden nur per \"Laufwerk neu lesen\" erkannt. <code>Polling Intervall (ms)</code> <code>disc_poll_interval_ms</code> <code>number</code> <code>4000</code> Intervall f\u00fcr Disk-Erkennung."},{"location":"configuration/settings-reference/#kategorie-metadaten","title":"Kategorie: Metadaten","text":"GUI-Feld Key Typ Default Wirkung im System <code>OMDb API Key</code> <code>omdb_api_key</code> <code>string</code> <code>leer</code> API Key f\u00fcr Metadatensuche. <code>TMDb Read Access Token</code> <code>tmdb_api_read_access_token</code> <code>string</code> <code>leer</code> Read Access Token f\u00fcr Serien-Metadaten \u00fcber TheMovieDB (TMDb). <code>DVD Serien-Sprache</code> <code>dvd_series_language</code> <code>string</code> <code>de-DE</code> Bevorzugte Sprache f\u00fcr Serien-Metadaten im TMDb-Format, z.B. de-DE oder en-US. <code>DVD Serien-Fallback-Sprachen</code> <code>dvd_series_fallback_languages</code> <code>string</code> <code>de-DE,en-US</code> Kommagetrennte TMDb-Sprachen f\u00fcr Fallback-Titel, z.B. de-DE,en-US,fr-FR. <code>DVD Serien-Ordnung</code> <code>dvd_series_order_preference</code> <code>select</code> <code>episode_group</code> Bevorzugte Episodenordnung f\u00fcr DVD-Serien. Episode Groups nutzen TMDb Alternate Orders, falls vorhanden. <code>DVD Disc-Profile wiederverwenden</code> <code>dvd_series_reuse_disc_profiles</code> <code>boolean</code> <code>true</code> Verwendet zuvor best\u00e4tigte Disc-Profile erneut, um Episoden automatischer zuzuordnen. <code>OMDb Typ</code> <code>omdb_default_type</code> <code>select</code> <code>movie</code> Vorauswahl f\u00fcr Suche. <code>MusicBrainz aktiviert</code> <code>musicbrainz_enabled</code> <code>boolean</code> <code>true</code> MusicBrainz-Metadatensuche f\u00fcr CDs aktivieren. <code>Coverart-Nachladen aktiv</code> <code>coverart_recovery_enabled</code> <code>boolean</code> <code>true</code> Wenn aktiv, werden fehlende Coverarts automatisch in Intervallen gepr\u00fcft und nachgeladen. <code>Coverart-Pr\u00fcfintervall (Stunden)</code> <code>coverart_recovery_interval_hours</code> <code>number</code> <code>6</code> Intervall f\u00fcr automatische Pr\u00fcfung fehlender Coverarts in Stunden."},{"location":"configuration/settings-reference/#kategorie-monitoring","title":"Kategorie: Monitoring","text":"GUI-Feld Key Typ Default Wirkung im System <code>Hardware Monitoring aktiviert</code> <code>hardware_monitoring_enabled</code> <code>boolean</code> <code>true</code> Master-Schalter: aktiviert/deaktiviert das komplette Hardware-Monitoring (Polling + Berechnung + WebSocket-Updates). <code>Hardware Monitoring Intervall (ms)</code> <code>hardware_monitoring_interval_ms</code> <code>number</code> <code>5000</code> Polling-Intervall f\u00fcr CPU/RAM/GPU/Storage-Metriken."},{"location":"configuration/settings-reference/#kategorie-pfade","title":"Kategorie: Pfade","text":"GUI-Feld Key Typ Default Wirkung im System <code>Raw-Ordner (Blu-ray)</code> <code>raw_dir_bluray</code> <code>path</code> <code>leer</code> RAW-Zielpfad f\u00fcr Blu-ray. Leer = Standardpfad (data/output/raw). <code>Raw-Ordner (DVD)</code> <code>raw_dir_dvd</code> <code>path</code> <code>leer</code> RAW-Zielpfad f\u00fcr DVD. Leer = Standardpfad (data/output/raw). <code>CD RAW-Ordner</code> <code>raw_dir_cd</code> <code>path</code> <code>leer</code> Basisordner f\u00fcr rohe CD-WAV-Dateien (cdparanoia-Output). Leer = Standardpfad (data/output/cd). <code>Audiobook RAW-Ordner</code> <code>raw_dir_audiobook</code> <code>path</code> <code>leer</code> Basisordner f\u00fcr hochgeladene AAX-Dateien. Leer = Standardpfad (data/output/audiobook-raw). <code>Serien-Ordner (Blu-ray)</code> <code>series_dir_bluray</code> <code>path</code> <code>leer</code> Zielordner f\u00fcr Blu-ray-Serienfolgen. Leer = Standardpfad (data/output/series). <code>Film-Ordner (Blu-ray)</code> <code>movie_dir_bluray</code> <code>path</code> <code>leer</code> Encode-Zielpfad f\u00fcr Blu-ray. Leer = Standardpfad (data/output/movies). <code>Film-Ordner (DVD)</code> <code>movie_dir_dvd</code> <code>path</code> <code>leer</code> Encode-Zielpfad f\u00fcr DVD. Leer = Standardpfad (data/output/movies). <code>Serien-Ordner (DVD)</code> <code>series_dir_dvd</code> <code>path</code> <code>leer</code> Zielordner f\u00fcr DVD-Serienfolgen. Leer = Standardpfad (data/output/series). <code>CD Output-Ordner</code> <code>movie_dir_cd</code> <code>path</code> <code>leer</code> Zielordner f\u00fcr encodierte CD-Ausgaben (FLAC, MP3 usw.). Leer = gleicher Ordner wie CD RAW-Ordner. <code>Audiobook Output-Ordner</code> <code>movie_dir_audiobook</code> <code>path</code> <code>leer</code> Zielordner f\u00fcr encodierte Audiobook-Dateien. Leer = Standardpfad (data/output/audiobooks). <code>Externe Speicher</code> <code>external_storage_paths</code> <code>path</code> <code>[]</code> Wird links unter \"Freie Speicher\" angezeigt. <code>Log Ordner</code> <code>log_dir</code> <code>path</code> <code>data/logs</code> Basisordner f\u00fcr Logs. Job-Logs liegen direkt hier, Backend-Logs in /backend. <code>CD Output Template</code> <code>cd_output_template</code> <code>string</code> <code>{artist} - {album} ({year})/{trackNr} {artist} - {title}</code> Template f\u00fcr relative CD-Ausgabepfade ohne Dateiendung. Platzhalter: {artist}, {album}, {year}, {title}, {trackNr}, {trackNo}. Unterordner sind \u00fcber \"/\" m\u00f6glich \u2013 alles nach dem letzten \"/\" ist der Dateiname. Die Endung wird \u00fcber das gew\u00e4hlte Ausgabeformat gesetzt. <code>Output Template (Blu-ray)</code> <code>output_template_bluray</code> <code>string</code> <code>${title} (${year})/${title} (${year})</code> Template f\u00fcr Ordner und Dateiname. Platzhalter: ${title}, ${year}, ${imdbId}. Unterordner \u00fcber \"/\" m\u00f6glich \u2013 alles nach dem letzten \"/\" ist der Dateiname. Die Endung wird \u00fcber das gew\u00e4hlte Ausgabeformat gesetzt. <code>Output Template (Blu-ray Serie, Episode)</code> <code>output_template_bluray_series_episode</code> <code>string</code> <code>{seriesTitle}/Staffel {seasonNr}/S{seasonNr}E{episodeNr} - {episodeTitle}</code> Template f\u00fcr einzelne Blu-ray-Serienepisoden ohne Dateiendung. Platzhalter: {seriesTitle}, {seasonNr}, {episodeNr}, {episodeTitle}, {discNr}, {year}, {language}. Optional mit f\u00fchrender Null: {0:seasonNr}, {0:episodeNr}. <code>Output Template (Blu-ray Serie, Multi-Episode)</code> <code>output_template_bluray_series_multi_episode</code> <code>string</code> <code>{seriesTitle}/Staffel {seasonNr}/S{0:seasonNr}E{episodeRange} - {episodeTitle} (Teil {parts})</code> Template f\u00fcr zusammengefasste Blu-ray-Serienepisoden ohne Dateiendung. Platzhalter: {seriesTitle}, {seasonNr}, {episodeNoStart}, {episodeNoEnd}, {episodeRange}, {episodeTitle}, {parts}, {discNr}, {year}, {language}. Optional mit f\u00fchrender Null: {0:seasonNr}, {0:episodeNoStart}, {0:episodeNoEnd}. <code>Output Template (DVD)</code> <code>output_template_dvd</code> <code>string</code> <code>${title} (${year})/${title} (${year})</code> Template f\u00fcr Ordner und Dateiname. Platzhalter: ${title}, ${year}, ${imdbId}. Unterordner \u00fcber \"/\" m\u00f6glich \u2013 alles nach dem letzten \"/\" ist der Dateiname. Die Endung wird \u00fcber das gew\u00e4hlte Ausgabeformat gesetzt. <code>Output Template (DVD Serie, Episode)</code> <code>output_template_dvd_series_episode</code> <code>string</code> <code>{seriesTitle}/Staffel {seasonNr}/S{seasonNr}E{episodeNr} - {episodeTitle}</code> Template f\u00fcr einzelne DVD-Serienepisoden ohne Dateiendung. Platzhalter: {seriesTitle}, {seasonNr}, {episodeNr}, {episodeTitle}, {discNr}, {year}, {language}. Optional mit f\u00fchrender Null: {0:seasonNr}, {0:episodeNr}. <code>Output Template (DVD Serie, Multi-Episode)</code> <code>output_template_dvd_series_multi_episode</code> <code>string</code> <code>{seriesTitle}/Staffel {seasonNr}/S{0:seasonNr}E{episodeRange} - {episodeTitle} (Teil {parts})</code> Template f\u00fcr zusammengefasste DVD-Serienepisoden ohne Dateiendung. Platzhalter: {seriesTitle}, {seasonNr}, {episodeNoStart}, {episodeNoEnd}, {episodeRange}, {episodeTitle}, {parts}, {discNr}, {year}, {language}. Optional mit f\u00fchrender Null: {0:seasonNr}, {0:episodeNoStart}, {0:episodeNoEnd}. <code>Output Template (Audiobook)</code> <code>output_template_audiobook</code> <code>string</code> <code>{author}/{author} - {title} ({year})</code> Template f\u00fcr relative Audiobook-Ausgabepfade ohne Dateiendung. Platzhalter: {author}, {title}, {year}, {narrator}, {series}, {part}, {format}. Unterordner sind \u00fcber \"/\" m\u00f6glich. <code>Audiobook RAW Template</code> <code>audiobook_raw_template</code> <code>string</code> <code>{author} - {title} ({year})</code> Template f\u00fcr relative Audiobook-RAW-Ordner. Platzhalter: {author}, {title}, {year}, {narrator}, {series}, {part}. <code>Converter Raw-Ordner</code> <code>converter_raw_dir</code> <code>path</code> <code>leer</code> Eingangsordner f\u00fcr den Converter (Import-Scan und Datei-Uploads). Jeder Upload erh\u00e4lt einen Unterordner. Leer = Standardpfad (data/output/converter-raw). <code>Converter Ausgabe (Video)</code> <code>converter_movie_dir</code> <code>path</code> <code>leer</code> Ausgabepfad f\u00fcr konvertierte Video-Dateien. Leer = Standardpfad (data/output/converted-movies). <code>Converter Ausgabe (Audio)</code> <code>converter_audio_dir</code> <code>path</code> <code>leer</code> Ausgabepfad f\u00fcr konvertierte Audio-Dateien. Leer = Standardpfad (data/output/converted-audio). <code>Output-Template (Video)</code> <code>converter_output_template_video</code> <code>string</code> <code>{title}</code> Dateiname-Template f\u00fcr konvertierte Video-Dateien (ohne Endung). Platzhalter: {title}, {year}. <code>Output-Template (Audio)</code> <code>converter_output_template_audio</code> <code>string</code> <code>{artist} - {title}</code> Dateiname-Template f\u00fcr konvertierte Audio-Dateien (ohne Endung). Platzhalter: {artist}, {title}, {album}, {year}, {trackNr}. <code>RAW-Ordner (Blu-ray Serie)</code> <code>raw_dir_bluray_series</code> <code>path</code> <code>leer</code> RAW-Zielpfad f\u00fcr Blu-ray-Serien. Leer = gleicher Pfad wie RAW-Ordner (Blu-ray). <code>Eigent\u00fcmer RAW-Ordner (Blu-ray Serie)</code> <code>raw_dir_bluray_series_owner</code> <code>string</code> <code>leer</code> Eigent\u00fcmer der Dateien im Format user:gruppe f\u00fcr Blu-ray-Serien-RAW. Leer = Eigent\u00fcmer Raw-Ordner (Blu-ray). <code>Eigent\u00fcmer Raw-Ordner (Blu-ray)</code> <code>raw_dir_bluray_owner</code> <code>string</code> <code>leer</code> Eigent\u00fcmer der Dateien im Format user:gruppe. Nur aktiv wenn ein Pfad gesetzt ist. Leer = Standardbenutzer des Dienstes. <code>RAW-Ordner (DVD Serie)</code> <code>raw_dir_dvd_series</code> <code>path</code> <code>leer</code> RAW-Zielpfad f\u00fcr DVD-Serien. Leer = gleicher Pfad wie RAW-Ordner (DVD). <code>Eigent\u00fcmer RAW-Ordner (DVD Serie)</code> <code>raw_dir_dvd_series_owner</code> <code>string</code> <code>leer</code> Eigent\u00fcmer der Dateien im Format user:gruppe f\u00fcr DVD-Serien-RAW. Leer = Eigent\u00fcmer Raw-Ordner (DVD). <code>Eigent\u00fcmer Raw-Ordner (DVD)</code> <code>raw_dir_dvd_owner</code> <code>string</code> <code>leer</code> Eigent\u00fcmer der Dateien im Format user:gruppe. Nur aktiv wenn ein Pfad gesetzt ist. Leer = Standardbenutzer des Dienstes. <code>Eigent\u00fcmer CD RAW-Ordner</code> <code>raw_dir_cd_owner</code> <code>string</code> <code>leer</code> Eigent\u00fcmer der Dateien im Format user:gruppe. Nur aktiv wenn ein Pfad gesetzt ist. Leer = Standardbenutzer des Dienstes. <code>Eigent\u00fcmer Audiobook RAW-Ordner</code> <code>raw_dir_audiobook_owner</code> <code>string</code> <code>leer</code> Eigent\u00fcmer der Audiobook-RAW-Dateien im Format user:gruppe. Nur aktiv wenn ein Pfad gesetzt ist. Leer = Standardbenutzer des Dienstes. <code>Eigent\u00fcmer Serien-Ordner (Blu-ray)</code> <code>series_dir_bluray_owner</code> <code>string</code> <code>leer</code> Eigent\u00fcmer der Blu-ray-Serienausgaben im Format user:gruppe. Leer = Standardbenutzer des Dienstes. <code>Eigent\u00fcmer Film-Ordner (Blu-ray)</code> <code>movie_dir_bluray_owner</code> <code>string</code> <code>leer</code> Eigent\u00fcmer der Dateien im Format user:gruppe. Nur aktiv wenn ein Pfad gesetzt ist. Leer = Standardbenutzer des Dienstes. <code>Eigent\u00fcmer Film-Ordner (DVD)</code> <code>movie_dir_dvd_owner</code> <code>string</code> <code>leer</code> Eigent\u00fcmer der Dateien im Format user:gruppe. Nur aktiv wenn ein Pfad gesetzt ist. Leer = Standardbenutzer des Dienstes. <code>Eigent\u00fcmer Serien-Ordner (DVD)</code> <code>series_dir_dvd_owner</code> <code>string</code> <code>leer</code> Eigent\u00fcmer der DVD-Serienausgaben im Format user:gruppe. Leer = Standardbenutzer des Dienstes. <code>Eigent\u00fcmer CD Output-Ordner</code> <code>movie_dir_cd_owner</code> <code>string</code> <code>leer</code> Eigent\u00fcmer der encodierten CD-Ausgaben im Format user:gruppe. Leer = Standardbenutzer des Dienstes. <code>Eigent\u00fcmer Audiobook Output-Ordner</code> <code>movie_dir_audiobook_owner</code> <code>string</code> <code>leer</code> Eigent\u00fcmer der encodierten Audiobook-Dateien im Format user:gruppe. Leer = Standardbenutzer des Dienstes."},{"location":"configuration/settings-reference/#kategorie-tools","title":"Kategorie: Tools","text":"GUI-Feld Key Typ Default Wirkung im System <code>MakeMKV Kommando</code> <code>makemkv_command</code> <code>string</code> <code>makemkvcon</code> Pfad oder Befehl f\u00fcr makemkvcon. <code>MakeMKV Key</code> <code>makemkv_registration_key</code> <code>string</code> <code>leer</code> Optionaler Registrierungsschl\u00fcssel. Wird vor Analyze/Rip automatisch per \"makemkvcon reg\" gesetzt. <code>Mediainfo Kommando</code> <code>mediainfo_command</code> <code>string</code> <code>mediainfo</code> Pfad oder Befehl f\u00fcr mediainfo. <code>Minimale Titell\u00e4nge (Minuten)</code> <code>makemkv_min_length_minutes</code> <code>number</code> <code>60</code> Filtert kurze Titel beim Rip. <code>HandBrake Kommando</code> <code>handbrake_command</code> <code>string</code> <code>HandBrakeCLI</code> Pfad oder Befehl f\u00fcr HandBrakeCLI. <code>Encode-Neustart: unvollst\u00e4ndige Ausgabe l\u00f6schen</code> <code>handbrake_restart_delete_incomplete_output</code> <code>boolean</code> <code>true</code> Wenn aktiv, wird bei \"Encode neu starten\" der bisherige (nicht erfolgreiche) Output vor Start entfernt. <code>Max. parallele Film/Video Encodes</code> <code>pipeline_max_parallel_jobs</code> <code>number</code> <code>1</code> Maximale Anzahl parallel laufender Film/Video Encode-Jobs. Gilt zus\u00e4tzlich zum Gesamtlimit. <code>Max. parallele Audio CD Jobs</code> <code>pipeline_max_parallel_cd_encodes</code> <code>number</code> <code>2</code> Maximale Anzahl parallel laufender Audio CD Jobs (Rip + Encode als Einheit). Gilt zus\u00e4tzlich zum Gesamtlimit. <code>Max. Encodes gesamt (medienunabh\u00e4ngig)</code> <code>pipeline_max_total_encodes</code> <code>number</code> <code>3</code> Gesamtlimit f\u00fcr alle parallel laufenden Encode-Jobs (Film + Audio CD). Dieses Limit hat Vorrang vor den Einzellimits. <code>Audio CDs: Queue-Reihenfolge \u00fcberspringen</code> <code>pipeline_cd_bypasses_queue</code> <code>boolean</code> <code>false</code> Wenn aktiv, k\u00f6nnen Audio CD Jobs unabh\u00e4ngig von Film-Jobs starten (\u00fcberspringen die Film-Queue-Reihenfolge). Einzellimits und Gesamtlimit gelten weiterhin. <code>Script-Test Timeout (ms)</code> <code>script_test_timeout_ms</code> <code>number</code> <code>0</code> Timeout fuer Script-Tests in den Settings. 0 = kein Timeout. <code>cdparanoia Kommando</code> <code>cdparanoia_command</code> <code>string</code> <code>cdparanoia</code> Pfad oder Befehl f\u00fcr cdparanoia. Wird als Fallback genutzt wenn kein individuelles Kommando gesetzt ist. <code>FFmpeg Kommando</code> <code>ffmpeg_command</code> <code>string</code> <code>ffmpeg</code> Pfad oder Befehl f\u00fcr ffmpeg. Wird f\u00fcr Audiobook-Encoding genutzt. <code>FFprobe Kommando</code> <code>ffprobe_command</code> <code>string</code> <code>ffprobe</code> Pfad oder Befehl f\u00fcr ffprobe. Wird f\u00fcr Audiobook-Metadaten und Kapitel genutzt. <code>Mediainfo Extra Args</code> <code>mediainfo_extra_args_bluray</code> <code>string</code> <code>leer</code> Zus\u00e4tzliche CLI-Parameter f\u00fcr mediainfo (Blu-ray). <code>MakeMKV Rip Modus</code> <code>makemkv_rip_mode_bluray</code> <code>select</code> <code>backup</code> backup: vollst\u00e4ndige Blu-ray Struktur im RAW-Ordner (empfohlen, erm\u00f6glicht --decrypt). <code>MakeMKV Analyze Extra Args</code> <code>makemkv_analyze_extra_args_bluray</code> <code>string</code> <code>leer</code> Zus\u00e4tzliche CLI-Parameter f\u00fcr Analyze (Blu-ray). <code>MakeMKV Rip Extra Args</code> <code>makemkv_rip_extra_args_bluray</code> <code>string</code> <code>leer</code> Zus\u00e4tzliche CLI-Parameter f\u00fcr Rip (Blu-ray). <code>HandBrake Preset</code> <code>handbrake_preset_bluray</code> <code>string</code> <code>H.264 MKV 1080p30</code> Preset Name f\u00fcr -Z (Blu-ray). Leer = kein Preset, nur CLI-Parameter werden verwendet. <code>HandBrake Extra Args</code> <code>handbrake_extra_args_bluray</code> <code>string</code> <code>leer</code> Zus\u00e4tzliche CLI-Argumente (Blu-ray). <code>Ausgabeformat</code> <code>output_extension_bluray</code> <code>select</code> <code>mkv</code> Dateiendung f\u00fcr finale Datei (Blu-ray). <code>Mediainfo Extra Args</code> <code>mediainfo_extra_args_dvd</code> <code>string</code> <code>leer</code> Zus\u00e4tzliche CLI-Parameter f\u00fcr mediainfo (DVD). <code>MakeMKV Rip Modus</code> <code>makemkv_rip_mode_dvd</code> <code>select</code> <code>backup</code> backup: vollst\u00e4ndige Disc-Struktur im RAW-Ordner (einzig g\u00fcltige Option f\u00fcr DVDs). <code>MakeMKV Analyze Extra Args</code> <code>makemkv_analyze_extra_args_dvd</code> <code>string</code> <code>leer</code> Zus\u00e4tzliche CLI-Parameter f\u00fcr Analyze (DVD). <code>MakeMKV Rip Extra Args</code> <code>makemkv_rip_extra_args_dvd</code> <code>string</code> <code>leer</code> Zus\u00e4tzliche CLI-Parameter f\u00fcr Rip (DVD). <code>HandBrake Preset</code> <code>handbrake_preset_dvd</code> <code>string</code> <code>H.264 MKV 480p30</code> Preset Name f\u00fcr -Z (DVD). Leer = kein Preset, nur CLI-Parameter werden verwendet. <code>HandBrake Extra Args</code> <code>handbrake_extra_args_dvd</code> <code>string</code> <code>leer</code> Zus\u00e4tzliche CLI-Argumente (DVD). <code>Ausgabeformat</code> <code>output_extension_dvd</code> <code>select</code> <code>mkv</code> Dateiendung f\u00fcr finale Datei (DVD)."},{"location":"deployment/","title":"Anhang: Deployment","text":"<p>Technische Betriebsdokumentation f\u00fcr Entwicklung und Produktion.</p> <ul> <li> <p> Entwicklungsumgebung</p> <p>Lokale Entwicklung mit Hot-Reload.</p> <p> Entwicklung</p> </li> <li> <p> Produktion</p> <p>Installation und Betrieb auf Servern.</p> <p> Produktion</p> </li> </ul>"},{"location":"deployment/development/","title":"Entwicklungsumgebung","text":""},{"location":"deployment/development/#voraussetzungen","title":"Voraussetzungen","text":"<ul> <li>Node.js &gt;= 20.19.0</li> <li>externe Tools installiert (<code>makemkvcon</code>, <code>HandBrakeCLI</code>, <code>mediainfo</code>)</li> </ul>"},{"location":"deployment/development/#schnellstart","title":"Schnellstart","text":"<pre><code>./start.sh\n</code></pre> <p>Startet:</p> <ul> <li>Backend (<code>http://localhost:3001</code>, mit nodemon)</li> <li>Frontend (<code>http://localhost:5173</code>, mit Vite HMR)</li> </ul> <p>Stoppen: <code>Ctrl+C</code>.</p>"},{"location":"deployment/development/#manuell","title":"Manuell","text":""},{"location":"deployment/development/#backend","title":"Backend","text":"<pre><code>cd backend\nnpm install\nnpm run dev\n</code></pre>"},{"location":"deployment/development/#frontend","title":"Frontend","text":"<pre><code>cd frontend\nnpm install\nnpm run dev\n</code></pre>"},{"location":"deployment/development/#vite-proxy-dev","title":"Vite-Proxy (Dev)","text":"<p><code>frontend/vite.config.js</code> proxied standardm\u00e4\u00dfig:</p> <ul> <li><code>/api</code> -&gt; <code>http://127.0.0.1:3001</code></li> <li><code>/ws</code> -&gt; <code>ws://127.0.0.1:3001</code></li> </ul>"},{"location":"deployment/development/#remote-dev-optional","title":"Remote-Dev (optional)","text":"<p>Beispiel <code>frontend/.env.local</code>:</p> <pre><code>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</code></pre>"},{"location":"deployment/development/#nutzliche-kommandos","title":"N\u00fctzliche Kommandos","text":"<pre><code># 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</code></pre>"},{"location":"deployment/production/","title":"Produktions-Deployment","text":""},{"location":"deployment/production/#automatische-installation-empfohlen","title":"Automatische Installation (empfohlen)","text":"<p>Das mitgelieferte <code>install.sh</code> richtet Ripster vollautomatisch ein \u2013 inklusive Node.js, MakeMKV, HandBrake, nginx und systemd-Dienst.</p> <p>Unterst\u00fctzte Systeme laut Script: Debian, Ubuntu, Linux Mint, Pop!_OS Voraussetzung: root-Rechte, Internetzugang</p>"},{"location":"deployment/production/#schnellstart-via-curl","title":"Schnellstart via curl","text":"<pre><code>curl -fsSL https://raw.githubusercontent.com/Mboehmlaender/ripster/main/install.sh | sudo bash\n</code></pre> <p>Oder mit wget:</p> <pre><code>wget -qO- https://raw.githubusercontent.com/Mboehmlaender/ripster/main/install.sh | sudo bash\n</code></pre> <p>Optionen nur via Datei</p> <p>Beim Pipen von curl/wget k\u00f6nnen keine Argumente \u00fcbergeben werden. F\u00fcr benutzerdefinierte Optionen zuerst herunterladen und dann mit <code>sudo bash install.sh [Optionen]</code> ausf\u00fchren.</p>"},{"location":"deployment/production/#optionen","title":"Optionen","text":"Option Standard Beschreibung <code>--branch &lt;branch&gt;</code> <code>dev</code> Git-Branch f\u00fcr die Installation <code>--dir &lt;pfad&gt;</code> <code>/opt/ripster</code> Installationsverzeichnis <code>--user &lt;benutzer&gt;</code> <code>ripster</code> Systembenutzer f\u00fcr den Dienst <code>--port &lt;port&gt;</code> <code>3001</code> Backend-Port <code>--host &lt;hostname&gt;</code> Auto (Maschinen-IP) Hostname/IP f\u00fcr die Weboberfl\u00e4che <code>--no-makemkv</code> \u2013 MakeMKV-Installation \u00fcberspringen <code>--no-handbrake</code> \u2013 HandBrake-Installation \u00fcberspringen <code>--no-nginx</code> \u2013 nginx-Einrichtung \u00fcberspringen <code>--reinstall</code> \u2013 Bestehende Installation aktualisieren (Daten bleiben erhalten) <code>-h</code>, <code>--help</code> \u2013 Hilfe anzeigen"},{"location":"deployment/production/#beispiele","title":"Beispiele","text":"<pre><code># Standard-Installation\nsudo bash install.sh\n\n# Anderen Branch und Port verwenden\nsudo bash install.sh --branch dev --port 8080\n\n# Ohne MakeMKV (bereits installiert)\nsudo bash install.sh --no-makemkv\n\n# Bestehende Installation aktualisieren\nsudo bash install.sh --reinstall\n\n# Ohne nginx (eigener Reverse-Proxy)\nsudo bash install.sh --no-nginx --host mein-server.local\n</code></pre>"},{"location":"deployment/production/#was-das-skript-erledigt","title":"Was das Skript erledigt","text":"<ol> <li>Systempr\u00fcfung \u2013 OS-Erkennung und Root-Check</li> <li>Systempakete \u2013 <code>curl</code>, <code>wget</code>, <code>git</code>, <code>mediainfo</code>, <code>udev</code> u. a.</li> <li>Node.js 20 \u2013 via NodeSource, falls noch nicht installiert</li> <li>MakeMKV \u2013 aktuelle Version wird aus dem offiziellen Forum ermittelt und aus dem Quellcode kompiliert (kann mit <code>--no-makemkv</code> \u00fcbersprungen werden)</li> <li>HandBrake \u2013 interaktive Auswahl:<ul> <li>Option 1: Standard (<code>apt install handbrake-cli</code>)</li> <li>Option 2: Geb\u00fcndelte GPU-Version mit NVDEC aus <code>bin/HandBrakeCLI</code></li> </ul> </li> <li>Systembenutzer <code>ripster</code> \u2013 ohne Login-Shell und ohne Home-Verzeichnis; Gruppen werden (falls vorhanden) erg\u00e4nzt: <code>cdrom</code>, <code>optical</code>, <code>disk</code>, <code>video</code>, <code>render</code></li> <li>Repository \u2013 klont Branch nach <code>--dir</code> (bei <code>--reinstall</code>: sichert <code>backend/data</code>, aktualisiert Repo, stellt Daten wieder her)</li> <li>Verzeichnisse \u2013 stellt u. a. sicher: <code>backend/data</code>, <code>backend/logs</code>, <code>backend/data/output/raw</code>, <code>backend/data/output/movies</code>, <code>backend/data/logs</code></li> <li>npm-Abh\u00e4ngigkeiten \u2013 Root, Backend (nur production), Frontend</li> <li>Frontend-Build \u2013 <code>npm run build</code> mit relativen API-URLs (nginx-kompatibel)</li> <li>Backend <code>.env</code> \u2013 wird automatisch generiert (bei <code>--reinstall</code> bleibt bestehende <code>.env</code> erhalten)</li> <li>Berechtigungen \u2013 zun\u00e4chst <code>ripster:ripster</code> auf Installationsverzeichnis; bei sudo-Aufruf werden Output-/Log-Ordner auf <code>&lt;aufrufender user&gt;:ripster</code> mit <code>775</code> gesetzt; <code>.env</code> wird auf <code>600</code> gesetzt</li> <li>MakeMKV User-Ordner \u2013 erstellt bei sudo-Aufruf <code>~/.MakeMKV</code> f\u00fcr den aufrufenden Benutzer</li> <li>systemd-Dienst \u2013 <code>ripster-backend.service</code> erstellt, aktiviert und gestartet</li> <li>nginx \u2013 konfiguriert als Reverse-Proxy f\u00fcr Frontend, <code>/api/</code> und <code>/ws</code> (kann mit <code>--no-nginx</code> \u00fcbersprungen werden)</li> </ol>"},{"location":"deployment/production/#nach-der-installation","title":"Nach der Installation","text":"<pre><code># Status pr\u00fcfen\nsudo systemctl status ripster-backend\n\n# Logs verfolgen\nsudo journalctl -u ripster-backend -f\n\n# Neustart\nsudo systemctl restart ripster-backend\n\n# Aktualisieren\nsudo bash /opt/ripster/install.sh --reinstall\n</code></pre> <p>Zugriff: <code>http://&lt;Maschinen-IP&gt;</code> (oder der mit <code>--host</code> angegebene Hostname)</p>"},{"location":"deployment/production/#handbrake-modus-gpunvdec","title":"HandBrake-Modus (GPU/NVDEC)","text":"<p>Bei nicht-interaktiver Ausf\u00fchrung (Pipe von curl) wird automatisch die Standard-Version gew\u00e4hlt. F\u00fcr die GPU-Version zuerst herunterladen:</p> <pre><code>curl -fsSL https://raw.githubusercontent.com/Mboehmlaender/ripster/main/install.sh -o install.sh\nsudo bash install.sh\n# \u2192 Interaktive Auswahl: Option 2 f\u00fcr NVDEC\n</code></pre> <p>Das geb\u00fcndelte Binary liegt unter <code>bin/HandBrakeCLI</code> und wird nach <code>/usr/local/bin/HandBrakeCLI</code> kopiert.</p>"},{"location":"deployment/production/#manuelle-installation","title":"Manuelle Installation","text":"<p>Die folgenden Abschnitte beschreiben die einzelnen Schritte f\u00fcr manuelle oder angepasste Setups.</p>"},{"location":"deployment/production/#empfohlene-architektur","title":"Empfohlene Architektur","text":"<pre><code>Client\n -&gt; nginx (Reverse Proxy + statisches Frontend)\n -&gt; Backend API/WebSocket (Node.js, Port 3001)\n</code></pre> <p>Wichtig: Das Backend serviert im aktuellen Stand keine <code>frontend/dist</code>-Dateien automatisch.</p>"},{"location":"deployment/production/#1-frontend-builden","title":"1) Frontend builden","text":"<pre><code>cd frontend\nnpm install\nnpm run build\n</code></pre> <p>Artefakte liegen in <code>frontend/dist/</code>.</p>"},{"location":"deployment/production/#2-backend-als-systemd-service","title":"2) Backend als systemd-Service","text":"<p>Beispiel <code>/etc/systemd/system/ripster-backend.service</code>:</p> <pre><code>[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</code></pre> <p>Aktivieren:</p> <pre><code>sudo systemctl daemon-reload\nsudo systemctl enable --now ripster-backend\nsudo systemctl status ripster-backend\n</code></pre>"},{"location":"deployment/production/#3-nginx-konfigurieren","title":"3) nginx konfigurieren","text":"<p>Beispiel <code>/etc/nginx/sites-available/ripster</code>:</p> <pre><code>server {\n listen 80;\n server_name ripster.local;\n client_max_body_size 8G;\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</code></pre> <p>Aktivieren:</p> <pre><code>sudo ln -s /etc/nginx/sites-available/ripster /etc/nginx/sites-enabled/\nsudo nginx -t\nsudo systemctl reload nginx\n</code></pre> <p>Hinweis: Fuer groessere Uploads wie <code>.aax</code>-Audiobooks muss <code>client_max_body_size</code> ausreichend hoch gesetzt sein. Im mitgelieferten Beispiel sind <code>8G</code> hinterlegt.</p>"},{"location":"deployment/production/#datenbank-backup","title":"Datenbank-Backup","text":"<pre><code>sqlite3 /opt/ripster/backend/data/ripster.db \\\n \".backup '/var/backups/ripster-$(date +%Y%m%d).db'\"\n</code></pre>"},{"location":"deployment/production/#sicherheit","title":"Sicherheit","text":"<ul> <li>Ripster hat keine eingebaute Authentifizierung.</li> <li>F\u00fcr externen Zugriff mindestens Basic Auth + TLS + Netzwerksegmentierung/VPN einsetzen.</li> <li>Secrets nicht ins Repo committen (<code>.env</code>, Settings-Felder).</li> </ul>"},{"location":"getting-started/","title":"Benutzerhandbuch \u00dcberblick","text":"<p>Dieses Kapitel ist f\u00fcr den Betrieb von Ripster im Alltag geschrieben.</p>"},{"location":"getting-started/#zielgruppe","title":"Zielgruppe","text":"<ul> <li>Anwender, die Discs verarbeiten wollen</li> <li>Betreiber, die den t\u00e4glichen Ablauf stabil fahren m\u00f6chten</li> <li>Power-User, die Queue/Skripte/Cron im UI steuern m\u00f6chten</li> </ul>"},{"location":"getting-started/#kapitelstruktur","title":"Kapitelstruktur","text":"Kapitel Zweck Voraussetzungen Produktions- vs. Dev-Voraussetzungen klar trennen Installation Ripster aufsetzen und starten Ersteinrichtung Pfade, Tools und Metadaten korrekt setzen Erster Lauf Ein kompletter Job von Disc bis Datei GUI-Seiten Alle Ansichten und Aktionen im Detail Workflows Typische Abl\u00e4ufe und Entscheidungen aus User-Sicht"},{"location":"getting-started/#wenn-du-neu-startest","title":"Wenn du neu startest","text":"<ol> <li>Voraussetzungen</li> <li>Installation</li> <li>Ersteinrichtung</li> <li>Erster Lauf</li> </ol>"},{"location":"getting-started/#wenn-ripster-bereits-lauft","title":"Wenn Ripster bereits l\u00e4uft","text":"<ol> <li>GUI-Seiten</li> <li>Workflows</li> <li>Bei Detailfragen: Technischer Anhang</li> </ol>"},{"location":"getting-started/configuration/","title":"Ersteinrichtung","text":"<p>Diese Seite ist die Betriebs-Checkliste vor dem ersten produktiven Lauf.</p>"},{"location":"getting-started/configuration/#ziel-der-ersteinrichtung","title":"Ziel der Ersteinrichtung","text":"<p>Vor dem ersten Job m\u00fcssen drei Dinge stabil sein:</p> <ol> <li>Pfade stimmen (RAW, Output, Logs)</li> <li>Tools sind aufrufbar (MakeMKV, HandBrake, MediaInfo)</li> <li>Metadatenzug\u00e4nge funktionieren (OMDb, optional TMDb/MusicBrainz)</li> </ol>"},{"location":"getting-started/configuration/#empfohlene-reihenfolge","title":"Empfohlene Reihenfolge","text":""},{"location":"getting-started/configuration/#1-basis-in-settings-konfiguration","title":"1) Basis in <code>Settings -&gt; Konfiguration</code>","text":"<p>Setze zuerst:</p> <ul> <li><code>Raw-Ordner</code> und <code>Film-/Serien-Ordner</code></li> <li><code>Log Ordner</code></li> <li><code>MakeMKV Kommando</code>, <code>HandBrake Kommando</code>, <code>Mediainfo Kommando</code></li> <li><code>OMDb API Key</code></li> </ul> <p>Dann speichern.</p>"},{"location":"getting-started/configuration/#2-profile-pro-medium-festziehen","title":"2) Profile pro Medium festziehen","text":"<p>Ripster l\u00f6st viele Werte profilspezifisch auf (<code>bluray</code>, <code>dvd</code>, <code>cd</code>, <code>audiobook</code>).</p> <p>Pflichtpr\u00fcfung:</p> <ul> <li>RAW-Pfade je Profil</li> <li>Output-Pfade je Profil</li> <li>HandBrake-Preset je Profil</li> <li>Output-Templates je Profil</li> </ul> <p>F\u00fcr Serien zus\u00e4tzlich:</p> <ul> <li><code>RAW-Ordner (Blu-ray/DVD Serie)</code></li> <li><code>Serien-Ordner (Blu-ray/DVD)</code></li> <li>Serien-Templates (Episode/Multi-Episode)</li> </ul>"},{"location":"getting-started/configuration/#3-queue-verhalten-definieren","title":"3) Queue-Verhalten definieren","text":"<p>Stelle ein:</p> <ul> <li><code>Max. parallele Film/Video Encodes</code></li> <li><code>Max. parallele Audio CD Jobs</code></li> <li><code>Max. Encodes gesamt</code></li> <li>optional <code>Audio CDs: Queue-Reihenfolge \u00fcberspringen</code></li> </ul> <p>Damit steuerst du Durchsatz, Priorit\u00e4t und Lastverteilung.</p>"},{"location":"getting-started/configuration/#4-metadatenquellen-absichern","title":"4) Metadatenquellen absichern","text":"<p>Film:</p> <ul> <li>OMDb-Key pr\u00fcfen</li> </ul> <p>Serien:</p> <ul> <li>TMDb Read Access Token setzen</li> <li>Sprache/Fallback-Sprachen festlegen</li> </ul> <p>CD/Audiodaten:</p> <ul> <li><code>MusicBrainz aktiviert</code> nach Bedarf</li> </ul>"},{"location":"getting-started/configuration/#5-optional-benachrichtigungen","title":"5) Optional: Benachrichtigungen","text":"<ul> <li>PushOver-Zugangsdaten setzen</li> <li><code>PushOver Test</code> ausf\u00fchren</li> <li>Event-Toggles pro Phase pr\u00fcfen</li> </ul>"},{"location":"getting-started/configuration/#6-optional-automatisierung","title":"6) Optional: Automatisierung","text":"<ol> <li>Skripte testen</li> <li>Skriptketten bauen/testen</li> <li>Cronjobs erst danach aktivieren</li> </ol>"},{"location":"getting-started/configuration/#funktionsprufung-kurztest","title":"Funktionspr\u00fcfung (Kurztest)","text":"<ol> <li>Disc in <code>Ripper</code> erkennen lassen.</li> <li><code>Analyse starten</code>.</li> <li>Metadaten \u00fcbernehmen.</li> <li>Bis <code>Bereit zum Encodieren</code> laufen.</li> <li>Encode starten und Abschluss in <code>Historie</code> pr\u00fcfen.</li> </ol>"},{"location":"getting-started/configuration/#typische-konfigurationsfehler","title":"Typische Konfigurationsfehler","text":"Symptom H\u00e4ufige Ursache Job bleibt fr\u00fch auf Fehler Toolkommando falsch oder nicht im PATH RAW/Output landet am falschen Ort Profilpfade nicht gesetzt, Fallback greift Serienausgabe landet bei Filmen Serienpfade/Serientemplates fehlen Queue verh\u00e4lt sich unerwartet Gesamtlimit und Lane-Limits widersprechen sich"},{"location":"getting-started/configuration/#weiter","title":"Weiter","text":"<ul> <li>Erster Lauf</li> <li>GUI-Seiten</li> <li>Workflows aus Nutzersicht</li> </ul>"},{"location":"getting-started/installation/","title":"Installation","text":"<p>Die empfohlene Installation l\u00e4uft \u00fcber <code>install.sh</code> und richtet Ripster vollst\u00e4ndig ein.</p> <p>Du musst daf\u00fcr keine Tools manuell vorinstallieren. Das Skript installiert die ben\u00f6tigten Abh\u00e4ngigkeiten automatisch, sofern sie nicht explizit mit <code>--no-*</code> \u00fcbersprungen werden.</p>"},{"location":"getting-started/installation/#unterstutzte-systeme-und-anforderungen","title":"Unterst\u00fctzte Systeme und Anforderungen","text":"<ul> <li>unterst\u00fctzt laut Script: <code>debian</code>, <code>ubuntu</code>, <code>linuxmint</code>, <code>pop</code></li> <li>Ausf\u00fchrung als <code>root</code> (oder via <code>sudo</code>)</li> <li>Internetzugang w\u00e4hrend der Installation</li> </ul>"},{"location":"getting-started/installation/#schritt-fur-schritt","title":"Schritt-f\u00fcr-Schritt","text":""},{"location":"getting-started/installation/#1-installationsskript-herunterladen","title":"1. Installationsskript herunterladen","text":"<pre><code>wget -qO install.sh https://raw.githubusercontent.com/Mboehmlaender/ripster/main/install.sh\n</code></pre>"},{"location":"getting-started/installation/#2-installation-ausfuhren","title":"2. Installation ausf\u00fchren","text":"<pre><code>sudo bash install.sh\n</code></pre> <p>W\u00e4hrend der Installation w\u00e4hlst du den HandBrake-Modus:</p> <ul> <li><code>1</code> Standard (Paketinstallation)</li> <li><code>2</code> GPU/NVDEC (geb\u00fcndeltes Binary)</li> </ul>"},{"location":"getting-started/installation/#3-dienststatus-prufen","title":"3. Dienststatus pr\u00fcfen","text":"<pre><code>sudo systemctl status ripster-backend\n</code></pre>"},{"location":"getting-started/installation/#4-weboberflache-offnen","title":"4. Weboberfl\u00e4che \u00f6ffnen","text":"<ul> <li>mit nginx (Standard): <code>http://&lt;Server-IP&gt;</code></li> <li>ohne nginx (<code>--no-nginx</code>): API auf <code>http://&lt;Server-IP&gt;:3001/api</code></li> </ul>"},{"location":"getting-started/installation/#was-installsh-konkret-einrichtet","title":"Was <code>install.sh</code> konkret einrichtet","text":"<ol> <li>pr\u00fcft Betriebssystem, Root-Rechte und ermittelt Host/IP</li> <li>aktualisiert Paketquellen und installiert Basispakete (<code>curl</code>, <code>wget</code>, <code>git</code>, <code>mediainfo</code>, <code>udev</code> ...)</li> <li>installiert Node.js 20 (falls nicht passend vorhanden)</li> <li>installiert optional MakeMKV (Build aus den offiziellen Quellen)</li> <li>installiert optional HandBrakeCLI (Standard oder GPU/NVDEC)</li> <li>installiert optional nginx</li> <li>legt den Systembenutzer <code>ripster</code> an (ohne Login-Shell, ohne Home) und erg\u00e4nzt Gruppen (<code>cdrom</code>, <code>optical</code>, <code>disk</code>, <code>video</code>, <code>render</code>, falls vorhanden)</li> <li>klont das Repository nach <code>/opt/ripster</code> (oder aktualisiert bei <code>--reinstall</code>)</li> <li>legt Verzeichnisse an:</li> <li><code>/opt/ripster/backend/data</code></li> <li><code>/opt/ripster/backend/logs</code></li> <li><code>/opt/ripster/backend/data/output/raw</code></li> <li><code>/opt/ripster/backend/data/output/movies</code></li> <li><code>/opt/ripster/backend/data/logs</code></li> <li>installiert npm-Abh\u00e4ngigkeiten (Root, Backend, Frontend) und baut das Frontend</li> <li>erzeugt <code>backend/.env</code> (bei <code>--reinstall</code> bleibt bestehende <code>.env</code> erhalten)</li> <li>setzt Rechte/Besitz und erstellt bei sudo-Installation zus\u00e4tzlich <code>~/.MakeMKV</code> f\u00fcr den aufrufenden Benutzer</li> <li>erzeugt und startet <code>ripster-backend.service</code></li> <li>konfiguriert und startet nginx (falls nicht \u00fcbersprungen)</li> </ol>"},{"location":"getting-started/installation/#wichtige-optionen","title":"Wichtige Optionen","text":"Option Default laut Script Zweck <code>--branch &lt;branch&gt;</code> <code>dev</code> Branch f\u00fcr die Installation <code>--dir &lt;pfad&gt;</code> <code>/opt/ripster</code> Installationsverzeichnis <code>--user &lt;benutzer&gt;</code> <code>ripster</code> Service-Benutzer <code>--port &lt;port&gt;</code> <code>3001</code> Backend-Port <code>--host &lt;hostname&gt;</code> automatisch ermittelte Host-IP Hostname/IP f\u00fcr Webzugriff/CORS <code>--no-makemkv</code> aus MakeMKV-Installation \u00fcberspringen <code>--no-handbrake</code> aus HandBrake-Installation \u00fcberspringen <code>--no-nginx</code> aus nginx-Setup \u00fcberspringen <code>--reinstall</code> aus bestehende Installation aktualisieren <p>Beispiele:</p> <pre><code>sudo bash install.sh --branch dev\nsudo bash install.sh --port 8080 --host ripster.local\nsudo bash install.sh --reinstall\n</code></pre>"},{"location":"getting-started/installation/#betrieb-im-alltag","title":"Betrieb im Alltag","text":"<pre><code># Logs live ansehen\nsudo journalctl -u ripster-backend -f\n\n# Dienst neu starten\nsudo systemctl restart ripster-backend\n\n# Update aus bestehender Installation\nsudo bash /opt/ripster/install.sh --reinstall\n</code></pre>"},{"location":"getting-started/installation/#haufige-stolperstellen","title":"H\u00e4ufige Stolperstellen","text":"<ul> <li>Laufwerk nicht zugreifbar: Laufwerksrechte/Gruppen pr\u00fcfen</li> <li>CLI-Tool fehlt wegen <code>--no-*</code>: Tool nachinstallieren oder Befehl in Settings korrigieren</li> <li>UI nicht erreichbar: nginx-Status und Firewall pr\u00fcfen</li> </ul>"},{"location":"getting-started/installation/#danach-weiter","title":"Danach weiter","text":"<ol> <li>Ersteinrichtung</li> <li>Erster Lauf</li> </ol>"},{"location":"getting-started/prerequisites/","title":"Voraussetzungen","text":"<p>Die Voraussetzungen h\u00e4ngen davon ab, wie du Ripster betreibst.</p>"},{"location":"getting-started/prerequisites/#produktionsbetrieb-mit-installsh-standard","title":"Produktionsbetrieb mit <code>install.sh</code> (Standard)","text":"<p>F\u00fcr den normalen Betrieb sind nur wenige Punkte vorab n\u00f6tig.</p>"},{"location":"getting-started/prerequisites/#pflicht","title":"Pflicht","text":"<ul> <li>unterst\u00fctztes Linux-System (laut Script: Debian, Ubuntu, Linux Mint, Pop!_OS)</li> <li><code>root</code>-Rechte</li> <li>Internetzugang w\u00e4hrend der Installation</li> <li>optisches Laufwerk f\u00fcr Disc-Betrieb</li> </ul> <p><code>install.sh</code> installiert die ben\u00f6tigten Tools selbst (u. a. Node.js, MakeMKV, HandBrakeCLI, MediaInfo), sofern sie nicht explizit per <code>--no-*</code> \u00fcbersprungen werden.</p>"},{"location":"getting-started/prerequisites/#laufwerk-kurz-prufen","title":"Laufwerk kurz pr\u00fcfen","text":"<pre><code>ls /dev/sr*\nlsblk | grep rom\n</code></pre> <p>Wenn n\u00f6tig Rechte setzen (Beispiel):</p> <pre><code>sudo chmod a+rw /dev/sr0\n</code></pre>"},{"location":"getting-started/prerequisites/#optional-vorab","title":"Optional vorab","text":"<ul> <li>OMDb API-Key (kann auch nach Installation in den <code>Settings</code> gesetzt werden)</li> <li>PushOver-Zugangsdaten (optional)</li> </ul>"},{"location":"getting-started/prerequisites/#entwicklungsmodus-nur-fur-dev","title":"Entwicklungsmodus (nur f\u00fcr Dev)","text":"<p>Wenn du lokal entwickelst (<code>./start.sh</code>, <code>npm run dev</code>), gelten zus\u00e4tzliche Voraussetzungen:</p> <ul> <li>Node.js &gt;= 20.19.0</li> <li><code>makemkvcon</code>, <code>HandBrakeCLI</code>, <code>mediainfo</code> im <code>PATH</code></li> </ul> <p>Details: Entwicklungsumgebung</p>"},{"location":"getting-started/prerequisites/#abschluss-checkliste","title":"Abschluss-Checkliste","text":"<ul> <li>[ ] Produktionsbetrieb: Linux + root + Internet + Laufwerk vorhanden</li> <li>[ ] Dev-Modus (nur falls ben\u00f6tigt): Node.js und CLI-Tools verf\u00fcgbar</li> </ul>"},{"location":"getting-started/quickstart/","title":"Erster Lauf","text":"<p>Dieser Ablauf f\u00fchrt einen vollst\u00e4ndigen Disc-Job von Erkennung bis fertiger Datei durch.</p>"},{"location":"getting-started/quickstart/#1-disc-erkennen","title":"1) Disc erkennen","text":"<ol> <li><code>Ripper</code> \u00f6ffnen.</li> <li>Disc einlegen.</li> <li>Auf <code>Medium erkannt</code> warten.</li> </ol> <p>Pr\u00fcfen:</p> <ul> <li><code>Disk-Information</code> zeigt Device/Label.</li> <li>Falls nicht: <code>Laufwerk neu lesen</code>.</li> </ul>"},{"location":"getting-started/quickstart/#2-analyse-starten","title":"2) Analyse starten","text":"<p>Aktion:</p> <ul> <li><code>Analyse starten</code></li> </ul> <p>Erwartung:</p> <ul> <li>Status <code>Analyse</code></li> <li>danach Metadaten-Dialog</li> </ul>"},{"location":"getting-started/quickstart/#3-metadaten-bestatigen","title":"3) Metadaten best\u00e4tigen","text":"<p>Im Dialog:</p> <ol> <li>Treffer suchen/ausw\u00e4hlen.</li> <li>Bei Serie zus\u00e4tzlich Disc-Nummer setzen.</li> <li><code>Auswahl \u00fcbernehmen</code>.</li> </ol> <p>M\u00f6gliche Abzweigungen:</p> <ul> <li>Duplikatfilm -&gt; <code>\u00dcbernahme erzwingen</code> oder <code>Multipart movie</code></li> <li>vorhandenes RAW -&gt; Entscheidungsdialog (weiterverwenden oder neu rippen)</li> </ul>"},{"location":"getting-started/quickstart/#4-ripreview-phase","title":"4) Rip/Review-Phase","text":"<p>Normalfall:</p> <ul> <li><code>Startbereit</code> -&gt; <code>Rippen</code> -&gt; <code>Mediainfo-Pruefung</code></li> </ul> <p>M\u00f6gliche Alternativen:</p> <ul> <li>vorhandenes RAW: direkter Sprung in Pr\u00fcfung</li> <li>Blu-ray/DVD-Entscheidung n\u00f6tig: <code>Warte auf Auswahl</code> f\u00fcr Playlist/Titel</li> </ul>"},{"location":"getting-started/quickstart/#5-encode-review-abschlieen","title":"5) Encode-Review abschlie\u00dfen","text":"<p>Bei <code>Bereit zum Encodieren</code>:</p> <ul> <li>Titel festlegen</li> <li>Audio-/Subtitle-Spuren pr\u00fcfen</li> <li>optional Preset ausw\u00e4hlen</li> <li>optional Pre-/Post-Skripte w\u00e4hlen</li> </ul> <p>Dann:</p> <ul> <li><code>Encoding starten</code></li> </ul>"},{"location":"getting-started/quickstart/#6-encoding-uberwachen","title":"6) Encoding \u00fcberwachen","text":"<p>W\u00e4hrend <code>Encodieren</code>:</p> <ul> <li>Fortschritt/ETA im Ripper</li> <li>Queue- und Runtime-Status beobachten</li> <li>optional Logkontrolle in Historie</li> </ul>"},{"location":"getting-started/quickstart/#7-ergebnis-verifizieren","title":"7) Ergebnis verifizieren","text":"<p>Nach <code>Fertig</code>:</p> <ol> <li><code>Historie</code> \u00f6ffnen.</li> <li>Jobdetail pr\u00fcfen.</li> <li>Output-Pfad und Log plausibilisieren.</li> </ol> <p>Kontrollpunkte:</p> <ul> <li>finale Datei am erwarteten Template-Pfad</li> <li>RAW-Ordnerzustand finalisiert (kein <code>Incomplete_</code>)</li> </ul>"},{"location":"getting-started/quickstart/#8-typische-folgeaktionen","title":"8) Typische Folgeaktionen","text":"<ul> <li>falsche Metadaten: <code>OMDb neu zuordnen</code></li> <li>andere Trackauswahl: <code>Review neu starten</code></li> <li>erneuter Encode aus RAW: <code>RAW neu encodieren</code></li> <li>gleicher Plan erneut: <code>Encode neu starten</code></li> </ul>"},{"location":"gui/","title":"GUI-Seiten","text":"<p>Dieses Kapitel ist das Bedienhandbuch f\u00fcr die Oberfl\u00e4chen im Alltag.</p>"},{"location":"gui/#seitenuberblick","title":"Seiten\u00fcberblick","text":"Seite Rolle im Betrieb Typischer Einsatz Jobs zentrale Inbox Gesamtlage pr\u00fcfen und in Fachseite springen Ripper Live-Steuerung der Pipeline Disc-Flow starten, Queue steuern, Review freigeben Audiobooks AAX-Spezialflow Upload, Konfiguration und Encode von Audiobooks Settings Konfiguration/Automatisierung Pfade, Tools, Queue-Limits, Skripte, Cron Historie Nachbearbeitung und Kontrolle Logs, Re-Encode, L\u00f6schvorschau, Recovery-Aktionen Converter dateibasierte Konvertierung Video/Audio/ISO ohne Disc-Rip verarbeiten Downloads ZIP-Exports RAW/Output aus Historie bereitstellen Database (Expert) Recovery-Sicht orphan RAW finden und importieren"},{"location":"gui/#empfohlener-tagesablauf","title":"Empfohlener Tagesablauf","text":"<ol> <li><code>Jobs</code>: Statuslage und Auff\u00e4lligkeiten pr\u00fcfen.</li> <li><code>Ripper</code> oder <code>Converter</code>/<code>Audiobooks</code>: neue Arbeit starten.</li> <li><code>Historie</code>: Ergebnisse validieren und ggf. nachbearbeiten.</li> <li><code>Downloads</code>: Exporte f\u00fcr externe \u00dcbergabe erzeugen.</li> <li><code>Settings</code>: Regeln und Limits nur kontrolliert anpassen.</li> </ol>"},{"location":"gui/#navigation","title":"Navigation","text":"<p>Direkt in der Top-Navigation:</p> <ul> <li><code>Jobs</code>, <code>Ripper</code>, <code>Converter</code>, <code>Audiobooks</code>, <code>Settings</code>, <code>Historie</code>, <code>Downloads</code></li> </ul> <p>Nur per URL:</p> <ul> <li><code>Database</code> unter <code>/database</code></li> </ul>"},{"location":"gui/audiobooks/","title":"Audiobooks","text":"<p>Die Seite <code>Audiobooks</code> ist der spezialisierte AAX-Workflow.</p>"},{"location":"gui/audiobooks/#ablaufubersicht","title":"Ablauf\u00fcbersicht","text":"<p>Die Oberfl\u00e4che ist in drei Bereiche gegliedert:</p> <ol> <li><code>Audiobooks Upload</code></li> <li><code>Audiobook Jobs</code></li> <li><code>Audiobook Output Explorer</code></li> </ol>"},{"location":"gui/audiobooks/#1-audiobooks-upload","title":"1) Audiobooks Upload","text":"<p>Der Upload verarbeitet ausschlie\u00dflich AAX-Dateien.</p> <p>Direkt nach Upload passieren im Hintergrund:</p> <ul> <li>Probe/Metadatenanalyse (ffprobe)</li> <li>Anlage eines RAW-Ordners per <code>Audiobook RAW Template</code></li> <li>Ablage der AAX-Datei im RAW-Pfad</li> <li>Initiale Jobkonfiguration (Format, Kapitel, Metadaten)</li> </ul> <p>Wichtige Parameter:</p> <ul> <li>Zielformat (<code>m4b</code>, <code>mp3</code>, <code>flac</code>)</li> <li>optionaler Sofortstart</li> </ul> <p>Toolabh\u00e4ngigkeit:</p> <ul> <li><code>ffprobe_command</code> f\u00fcr Metadaten/Kapitel</li> <li><code>ffmpeg_command</code> f\u00fcr Encode/Cover-Extraktion</li> </ul>"},{"location":"gui/audiobooks/#2-audiobook-jobs","title":"2) Audiobook Jobs","text":"<p>Jobkarten zeigen Zustand, Fortschritt und verf\u00fcgbare Aktionen.</p> <p>Typische Aktionen:</p> <ul> <li><code>Starten</code></li> <li><code>Abbrechen</code></li> <li><code>Retry</code></li> <li><code>L\u00f6schen</code></li> <li><code>Im Ripper</code> (Kontextwechsel)</li> </ul> <p>Die Karten werden regelm\u00e4\u00dfig aktualisiert und durch WebSocket-Ereignisse erg\u00e4nzt.</p>"},{"location":"gui/audiobooks/#3-audiobook-output-explorer","title":"3) Audiobook Output Explorer","text":"<p>Read-only Explorer des Audiobook-Outputpfads:</p> <ul> <li>Ordner-/Dateistruktur</li> <li>Zeitstempel/Gr\u00f6\u00dfen</li> <li>schnelle Ergebnispr\u00fcfung ohne Dateisystemwechsel</li> </ul>"},{"location":"gui/audiobooks/#pfad-und-template-logik","title":"Pfad- und Template-Logik","text":"Bereich Gesteuert durch RAW-Ablage <code>raw_dir_audiobook</code> + <code>audiobook_raw_template</code> Finale Ausgabe <code>movie_dir_audiobook</code> + <code>output_template_audiobook</code> Dateiformat Jobkonfiguration (<code>m4b</code>, <code>mp3</code>, <code>flac</code>)"},{"location":"gui/audiobooks/#typische-probleme","title":"Typische Probleme","text":"<ul> <li>Upload wird abgelehnt: Eingabedatei ist keine AAX-Datei.</li> <li>Start schl\u00e4gt fehl: <code>ffmpeg</code>/<code>ffprobe</code> nicht aufrufbar.</li> <li>Unerwartete Pfade: Templates enthalten ung\u00fcltige oder zu generische Platzhalter.</li> </ul>"},{"location":"gui/converter/","title":"Converter","text":"<p>Die Seite <code>Converter</code> verarbeitet bereits vorhandene Dateien (Video, Audio, ISO) ohne Laufwerks-Rip.</p>"},{"location":"gui/converter/#betriebsmodell","title":"Betriebsmodell","text":"<p>Der Converter arbeitet auf dem Eingangspfad <code>converter_raw_dir</code>:</p> <ul> <li>Uploads landen dort</li> <li>Explorer-Aktionen arbeiten dort</li> <li>daraus werden Converter-Jobs erstellt</li> </ul> <p>Wichtig:</p> <ul> <li>Upload erzeugt nicht automatisch einen Job.</li> <li>Joberstellung erfolgt explizit \u00fcber Auswahl im Explorer.</li> </ul>"},{"location":"gui/converter/#datei-explorer","title":"Datei-Explorer","text":"<p>Der Explorer zeigt den kompletten Baum unter <code>converter_raw_dir</code>.</p> <p>Aktionen:</p> Aktion Wirkung <code>Upload</code> Dateien werden in den Raw-Baum geschrieben <code>Ordner erstellen</code> legt Unterordner im Raw-Baum an <code>Umbenennen</code> benennt Datei/Ordner im Raw-Baum um <code>Verschieben</code> verschiebt innerhalb des Raw-Baums <code>L\u00f6schen</code> entfernt Datei/Ordner dauerhaft <code>Scannen</code> synchronisiert Explorer und DB-Zuordnungen <p>Upload-Regeln:</p> <ul> <li>Dateiendung muss in <code>Erlaubte Datei-Endungen*</code> enthalten sein</li> <li>bei Versto\u00df wird der Upload abgelehnt</li> </ul>"},{"location":"gui/converter/#joberstellung-aus-explorer-auswahl","title":"Joberstellung aus Explorer-Auswahl","text":"<p>Es gibt zwei Audio-Strategien:</p> <ol> <li><code>Ein Job pro Datei</code></li> <li><code>Gemeinsamer Job (Audio)</code></li> </ol> <p>Verhalten:</p> <ul> <li>Video/ISO werden grunds\u00e4tzlich als Einzeljobs angelegt.</li> <li>Audio kann als Shared-Job mit mehreren Eingabedateien erzeugt werden.</li> </ul> <p>Shared-Audio-Job:</p> <ul> <li>h\u00e4lt mehrere <code>inputPaths</code></li> <li>kann vor Start erweitert/reduziert werden (nur Audio-Dateien)</li> </ul>"},{"location":"gui/converter/#job-konfiguration","title":"Job-Konfiguration","text":"<p>Vor Start je Job einstellbar:</p> <ul> <li><code>converterMediaType</code> (<code>video</code>, <code>audio</code>, <code>iso</code>)</li> <li><code>outputFormat</code></li> <li>Metadaten</li> <li>Track-/Titelwahl (bei video/iso)</li> <li>Pre-/Post-Encode-Skripte und Ketten</li> </ul> <p>Spezifika:</p> <ul> <li>Video/ISO laufen durch Mediainfo/Review \u00e4hnlich zum Disc-Flow.</li> <li>Audio kann direkt mit Formatoptionen konfiguriert werden.</li> </ul>"},{"location":"gui/converter/#ausgabe-pfade","title":"Ausgabe-Pfade","text":""},{"location":"gui/converter/#videoiso","title":"Video/ISO","text":"<ul> <li>Root: <code>converter_movie_dir</code></li> <li>Dateiname: <code>Output-Template (Video)</code></li> <li>Endung: aus gew\u00e4hltem Output-Format</li> </ul>"},{"location":"gui/converter/#audio","title":"Audio","text":"<ul> <li>Root: <code>converter_audio_dir</code></li> <li>Dateiname/Ordner: <code>Output-Template (Audio)</code></li> <li>bei Shared-/Folder-Job: Ausgabe als Ordnerstruktur mit Trackdateien</li> </ul>"},{"location":"gui/converter/#queue-und-start","title":"Queue und Start","text":"<p>Converter-Jobs laufen \u00fcber dieselbe Pipeline-/Queue-Logik:</p> <ul> <li>sofortiger Start wenn Limits frei</li> <li>sonst Queue-Eintrag</li> </ul> <p>Parallelit\u00e4t richtet sich nach den globalen Encode-Limits in <code>Settings</code>.</p>"},{"location":"gui/converter/#auto-scan-polling","title":"Auto-Scan (Polling)","text":"<p>Mit aktiviertem Polling:</p> <ul> <li><code>converter_raw_dir</code> wird zyklisch gescannt</li> <li>neue Dateien erscheinen automatisch im Explorer</li> </ul> <p>Relevante Settings:</p> <ul> <li><code>Auto-Scan (Polling)</code></li> <li><code>Polling-Intervall (Sekunden)</code></li> </ul>"},{"location":"gui/converter/#abgrenzung-zu-audiobooks","title":"Abgrenzung zu Audiobooks","text":"<ul> <li><code>Converter</code>: beliebige Audio/Video/ISO-Dateien</li> <li><code>Audiobooks</code>: dedizierter AAX-Workflow mit Kapitel-/Audiobook-Metadaten</li> </ul>"},{"location":"gui/database/","title":"Database (Expert)","text":"<p><code>/database</code> ist die Recovery- und Expertensicht f\u00fcr F\u00e4lle, in denen Dateisystem und Historie nicht mehr sauber zusammenpassen.</p>"},{"location":"gui/database/#zugriff","title":"Zugriff","text":"<ul> <li>direkte Route: <code>/database</code></li> <li>nicht Teil der Standardnavigation</li> </ul>"},{"location":"gui/database/#bereich-gefundene-raw-eintrage","title":"Bereich <code>Gefundene RAW-Eintr\u00e4ge</code>","text":"<p>Dieser Bereich listet RAW-Verzeichnisse ohne zugeordneten Historie-Job.</p> <p>Aktionen:</p> <ul> <li><code>RAW pr\u00fcfen</code>: scannt konfigurierte RAW-Wurzeln</li> <li><code>Job anlegen</code>: erstellt aus einem orphan RAW einen neuen Historie-Job</li> </ul>"},{"location":"gui/database/#was-bei-job-anlegen-passiert","title":"Was bei <code>Job anlegen</code> passiert","text":"<ul> <li>RAW-Pfad wird als Importquelle registriert</li> <li>Job wird in die Historie geschrieben</li> <li>anschlie\u00dfende Analyse l\u00e4uft als RAW-basierter Einstieg (ohne klassischen Disc-Read)</li> </ul> <p>Typischer Einsatz:</p> <ul> <li>manuelle Dateioperationen au\u00dferhalb der UI</li> <li>Migrationen</li> <li>Wiederherstellung nach abgebrochenen L\u00e4ufen</li> </ul>"},{"location":"gui/database/#sicherheits-und-betriebsregeln","title":"Sicherheits- und Betriebsregeln","text":"<ul> <li>Aktionen wirken direkt auf produktive Historie/Dateipfade.</li> <li>Vor Import oder L\u00f6schung immer Pfad und Ziel pr\u00fcfen.</li> <li>Diese Seite nur f\u00fcr Recovery-Szenarien nutzen, nicht f\u00fcr normalen Tagesbetrieb.</li> </ul>"},{"location":"gui/downloads/","title":"Downloads","text":"<p>Auf <code>Downloads</code> werden ZIP-Archive f\u00fcr Historie-Artefakte erstellt und bereitgestellt.</p>"},{"location":"gui/downloads/#was-heruntergeladen-werden-kann","title":"Was heruntergeladen werden kann","text":"<p>Ein Download-Eintrag referenziert immer einen Historie-Job und genau ein Ziel:</p> <ul> <li><code>RAW</code></li> <li><code>Output</code></li> </ul> <p>Erzeugt wird ein ZIP-Archiv im Download-Verzeichnis des Backends.</p>"},{"location":"gui/downloads/#statusmodell","title":"Statusmodell","text":"Status Bedeutung <code>queued</code> Eintrag erstellt, wartet auf Verarbeitung <code>processing</code> ZIP wird gerade gebaut <code>ready</code> Archiv fertig, Download-Link aktiv <code>failed</code> Erzeugung oder Zugriff fehlgeschlagen"},{"location":"gui/downloads/#eintrag-anlegen","title":"Eintrag anlegen","text":"<p>Startpunkt ist die <code>Historie</code>:</p> <ol> <li>Job \u00f6ffnen.</li> <li>Download einreihen.</li> <li>Ziel (<code>RAW</code> oder <code>Output</code>) w\u00e4hlen.</li> <li>Status in <code>Downloads</code> verfolgen.</li> </ol>"},{"location":"gui/downloads/#wichtige-laufzeitdetails","title":"Wichtige Laufzeitdetails","text":"<ul> <li>Wenn bereits ein passendes, aktuelles Archiv existiert, kann der Eintrag wiederverwendet werden.</li> <li>Nach Backend-Neustart werden unterbrochene <code>queued</code>/<code>processing</code>-Eintr\u00e4ge gepr\u00fcft und fortgesetzt.</li> <li>Fehlt eine erwartete ZIP-Datei bei <code>ready</code>, wird der Eintrag auf <code>failed</code> gesetzt.</li> </ul>"},{"location":"gui/downloads/#download-auslosen","title":"Download ausl\u00f6sen","text":"<p>Bei Status <code>ready</code>:</p> <ul> <li>Download-Button l\u00e4dt die ZIP direkt aus <code>/api/downloads/:id/file</code>.</li> </ul>"},{"location":"gui/downloads/#eintrage-entfernen","title":"Eintr\u00e4ge entfernen","text":"<p><code>L\u00f6schen</code> entfernt:</p> <ul> <li>Queue-Metadaten</li> <li>zugeh\u00f6rige Archivdatei</li> </ul> <p>Nicht erlaubt:</p> <ul> <li>L\u00f6schen w\u00e4hrend <code>queued</code>/<code>processing</code></li> </ul>"},{"location":"gui/downloads/#echtzeitaktualisierung","title":"Echtzeitaktualisierung","text":"<p>Die Seite reagiert auf <code>DOWNLOADS_UPDATED</code> per WebSocket. Dadurch sind Statuswechsel ohne Reload sichtbar.</p>"},{"location":"gui/history/","title":"Historie","text":"<p><code>Historie</code> ist die Kontroll- und Nachbearbeitungsoberfl\u00e4che f\u00fcr alle bereits angelegten Jobs.</p>"},{"location":"gui/history/#hauptansicht","title":"Hauptansicht","text":"<p>Filter und Arbeitsmittel:</p> <ul> <li>Suche (Titel, IMDb, Kontextbegriffe)</li> <li>Statusfilter</li> <li>Mediumfilter</li> <li>Sortierung</li> <li>Listen-/Grid-Layout</li> </ul> <p>Jeder Eintrag zeigt auf einen Blick:</p> <ul> <li>Titel, Jahr, Poster</li> <li>Status und Zeitstempel</li> <li>RAW-/Output-Verf\u00fcgbarkeit</li> <li>Medien- und ggf. Containerhinweise</li> </ul>"},{"location":"gui/history/#detaildialog-was-dort-zusammenlauft","title":"Detaildialog: Was dort zusammenl\u00e4uft","text":"<p>Der Detaildialog b\u00fcndelt:</p> <ul> <li>Metadaten (Film oder Serie)</li> <li>Jobstatus und Fehlertexte</li> <li>RAW-/Output-Pfade</li> <li>Encode-Plan und Trackauswahl</li> <li>ausgef\u00fchrte Kommandodetails</li> <li>Prozesslog (<code>Tail</code> oder vollst\u00e4ndig)</li> </ul> <p>Bei Seriencontainern wird disk-/child-orientiert dargestellt, nicht nur als Einzeljob.</p>"},{"location":"gui/history/#nachbearbeitungsaktionen-und-wirkung","title":"Nachbearbeitungsaktionen und Wirkung","text":"Aktion Typischer Einsatz Technische Wirkung <code>OMDb neu zuordnen</code> falsches Match, Titel/Jahr korrigieren Metadatenfelder werden neu geschrieben; Folder-Rename wird best effort angesto\u00dfen <code>Review neu starten</code> neue Track-/Titelbewertung n\u00f6tig Mediainfo/Review wird aus RAW neu aufgebaut <code>RAW neu encodieren</code> neuer Encode aus vorhandenem RAW startet Encode ohne neuen Laufwerks-Rip <code>Encode neu starten</code> gleiche best\u00e4tigte Auswahl erneut fahren letzter best\u00e4tigter Plan wird geladen, optional mit Bereinigung unvollst\u00e4ndiger Ausgabe <code>Retry Rippen</code> Rip/Analyse neu ansto\u00dfen neuer Rip-Lauf, inkl. erneuter RAW-Phase <p>L\u00f6schaktionen im Dialog:</p> <ul> <li><code>RAW l\u00f6schen</code></li> <li><code>Movie l\u00f6schen</code></li> <li><code>Beides l\u00f6schen</code></li> <li><code>Historieneintrag l\u00f6schen</code></li> </ul>"},{"location":"gui/history/#loschvorschau-und-selektives-loschen","title":"L\u00f6schvorschau und selektives L\u00f6schen","text":"<p>Vor endg\u00fcltigem L\u00f6schen zeigt Ripster eine Vorschau mit:</p> <ul> <li><code>RAW</code>-Kandidaten</li> <li><code>Movie</code>-/Episoden-Kandidaten</li> <li>Related-Scope (Container/Kinder/verbundene Jobs)</li> </ul> <p>Wichtige Punkte:</p> <ul> <li>Pfade sind selektierbar.</li> <li>F\u00fcr RAW-L\u00f6schung muss bei Mehrfachkandidaten mindestens ein RAW-Pfad gew\u00e4hlt werden.</li> <li>Schutzlogik verhindert L\u00f6schungen au\u00dferhalb erlaubter Root-Verzeichnisse.</li> </ul>"},{"location":"gui/history/#serien-und-multipart-sicht-in-der-historie","title":"Serien- und Multipart-Sicht in der Historie","text":""},{"location":"gui/history/#serien","title":"Serien","text":"<ul> <li>Container zeigt aggregierten Zustand aus Child-Jobs.</li> <li>Disk-/Episode-Aktionen k\u00f6nnen child-spezifisch ausgef\u00fchrt werden.</li> <li><code>Im Ripper \u00f6ffnen</code> hilft bei <code>Bereit zum Encodieren</code>-Kindern.</li> </ul>"},{"location":"gui/history/#multipart","title":"Multipart","text":"<ul> <li>gemeinsamer Container mit Disc-Childs</li> <li>Disc-Nummern bleiben pro Child nachvollziehbar</li> <li>Delete-Preview kann Related-Pfade gemeinsam aufl\u00f6sen</li> </ul>"},{"location":"gui/history/#logs-sinnvoll-nutzen","title":"Logs sinnvoll nutzen","text":"<ul> <li><code>Tail laden (800)</code> f\u00fcr schnellen Fehlerkontext</li> <li><code>Vollst\u00e4ndiges Log laden</code> f\u00fcr forensische Analyse</li> </ul> <p>Empfehlung:</p> <ol> <li>Erst Tail pr\u00fcfen.</li> <li>Bei Pfad-/Toolproblemen vollst\u00e4ndiges Log laden.</li> <li>Danach passende Wiederanlauf-Aktion w\u00e4hlen.</li> </ol>"},{"location":"gui/jobs/","title":"Jobs","text":"<p><code>Jobs</code> ist die zentrale Inbox \u00fcber alle Jobarten und dient als schneller Einstieg in den Tagesbetrieb.</p>"},{"location":"gui/jobs/#zweck-der-seite","title":"Zweck der Seite","text":"<p>Die Liste vereinheitlicht:</p> <ul> <li>Disc-/Ripper-Jobs</li> <li>Converter-Jobs</li> <li>Audiobook-Jobs</li> </ul> <p>Dadurch kannst du unabh\u00e4ngig von der Fachseite zuerst den Gesamtzustand pr\u00fcfen.</p>"},{"location":"gui/jobs/#filterlogik","title":"Filterlogik","text":""},{"location":"gui/jobs/#view-filter","title":"View-Filter","text":"<ul> <li><code>Alle Jobs</code></li> <li><code>Ripper-View</code></li> <li><code>Converter-View</code></li> <li><code>Audiobooks</code></li> </ul>"},{"location":"gui/jobs/#status-filter","title":"Status-Filter","text":"<ul> <li><code>Alle</code></li> <li><code>Aktiv</code></li> <li><code>Fehler/Abbruch</code></li> </ul> <p>Zus\u00e4tzlich:</p> <ul> <li>Freitextsuche</li> <li>Paginierung</li> </ul>"},{"location":"gui/jobs/#was-ein-jobeintrag-zeigt","title":"Was ein Jobeintrag zeigt","text":"<ul> <li>Job-ID und Titel</li> <li>Statusbadge</li> <li>Jobtypbadge (z. B. Blu-ray, DVD, Converter Audio, Audiobook)</li> <li>Aktualisierungszeit</li> </ul> <p>Die Ansicht wird periodisch aktualisiert und erg\u00e4nzt sich mit WebSocket-Events.</p>"},{"location":"gui/jobs/#schnellaktion-pro-zeile","title":"Schnellaktion pro Zeile","text":"<ul> <li><code>Im Ripper \u00f6ffnen</code> f\u00fcr Disc-/Audiobook-nahe Jobs</li> <li><code>Im Converter \u00f6ffnen</code> f\u00fcr Converter-Jobs</li> </ul> <p>So kommst du ohne Umwege in die Oberfl\u00e4che mit den passenden Detailaktionen.</p>"},{"location":"gui/jobs/#empfohlener-einsatz","title":"Empfohlener Einsatz","text":"<ol> <li>In <code>Jobs</code> St\u00f6rungen und Auslastung identifizieren.</li> <li>Danach gezielt in <code>Ripper</code>, <code>Converter</code> oder <code>Historie</code> springen.</li> </ol>"},{"location":"gui/ripper/","title":"Ripper","text":"<p>Die Seite <code>Ripper</code> ist die Live-Steuerung der Pipeline. Hier laufen Disc-Erkennung, Queue-Steuerung, Review und Encode-Start zusammen.</p>"},{"location":"gui/ripper/#seitenaufbau-und-betriebslogik","title":"Seitenaufbau und Betriebslogik","text":"<p>Die Bereiche erscheinen in dieser Reihenfolge:</p> <ol> <li><code>Hardware Monitoring</code></li> <li><code>Job Queue</code></li> <li><code>Skript- / Cron-Status</code></li> <li><code>Job \u00dcbersicht</code></li> <li><code>Disk-Information</code></li> </ol>"},{"location":"gui/ripper/#1-hardware-monitoring","title":"1) Hardware Monitoring","text":"<p>Zeigt kontinuierlich:</p> <ul> <li>CPU / RAM</li> <li>GPU (wenn verf\u00fcgbar)</li> <li>freie Speicherst\u00e4nde konfigurierter Pfade</li> </ul> <p>Nutzen im Betrieb:</p> <ul> <li>Engp\u00e4sse vor Jobfehlern erkennen</li> <li>Pfadf\u00fcllst\u00e4nde vor Start gro\u00dfer Batchs pr\u00fcfen</li> </ul> <p>Gesteuert \u00fcber <code>Settings</code>:</p> <ul> <li><code>Hardware Monitoring aktiviert</code></li> <li><code>Hardware Monitoring Intervall (ms)</code></li> </ul>"},{"location":"gui/ripper/#2-job-queue","title":"2) Job Queue","text":"<p>Es gibt zwei operative Zonen:</p> <ul> <li><code>Laufende Jobs</code></li> <li><code>Warteschlange</code></li> </ul> <p>M\u00f6gliche Queue-Eintr\u00e4ge:</p> <ul> <li>Job-Startaktionen (<code>Start</code>, <code>Retry Rippen</code>, <code>RAW neu encodieren</code>, <code>Encode neu starten</code>, <code>Review neu berechnen</code>, <code>Audio CD starten</code>)</li> <li>Nicht-Job-Eintr\u00e4ge (<code>Skript</code>, <code>Skriptkette</code>, <code>Warten</code>)</li> </ul> <p>Wichtige Regeln:</p> <ul> <li>Queue-Reihenfolge ist per Drag-and-Drop \u00e4nderbar.</li> <li><code>Warten</code> blockiert nachfolgende Queue-Starts bis kein aktiver Prozess mehr l\u00e4uft.</li> <li><code>Skript</code> und <code>Skriptkette</code> werden als Queue-Eintr\u00e4ge mitgef\u00fchrt.</li> </ul> <p>Parallelit\u00e4t kommt aus <code>Settings</code>:</p> <ul> <li><code>Max. parallele Film/Video Encodes</code></li> <li><code>Max. parallele Audio CD Jobs</code></li> <li><code>Max. Encodes gesamt</code></li> <li>optional <code>Audio CDs: Queue-Reihenfolge \u00fcberspringen</code></li> </ul>"},{"location":"gui/ripper/#3-skript-cron-status","title":"3) Skript- / Cron-Status","text":"<p>Zeigt Runtime-Aktivit\u00e4ten:</p> <ul> <li>laufende Skripte</li> <li>laufende Ketten</li> <li>Cron-Ausf\u00fchrungen</li> <li>letzte abgeschlossene Aktivit\u00e4ten</li> </ul> <p>Aktionen:</p> <ul> <li><code>N\u00e4chster Schritt</code> bei Ketten</li> <li><code>Abbrechen</code></li> <li><code>Liste leeren</code></li> </ul>"},{"location":"gui/ripper/#4-job-ubersicht","title":"4) Job \u00dcbersicht","text":"<p>Die Jobliste ist der zentrale Arbeitsbereich. Ein Klick \u00f6ffnet den jeweiligen <code>Pipeline-Status</code>.</p>"},{"location":"gui/ripper/#badges-und-kontext","title":"Badges und Kontext","text":"<p>Neben Status/Fortschritt zeigt die Liste je nach Job:</p> <ul> <li>Serienkontext (<code>Serien-Container</code>, Staffel/Disk)</li> <li>Multipart-Kontext (<code>Multipart Movie</code>, Disc-Nr.)</li> <li>Queue-/Subjob-Kontext bei Serien-Episoden</li> </ul>"},{"location":"gui/ripper/#zustandsabhangige-aktionen","title":"Zustandsabh\u00e4ngige Aktionen","text":"Zustand Hauptaktion im Ripper Ergebnis <code>Medium erkannt</code>, <code>Bereit</code> <code>Analyse starten</code> startet MakeMKV-Analyse <code>Metadatenauswahl</code> <code>Metadaten \u00f6ffnen</code> Film/Serie ausw\u00e4hlen, Provider festlegen <code>Warte auf Auswahl</code> Playlist/Titel/RAW-Entscheidung best\u00e4tigen setzt Flow fort zur Pr\u00fcfung <code>Startbereit</code> <code>Job starten</code> startet Rip oder RAW-basierte Pr\u00fcfung <code>Bereit zum Encodieren</code> <code>Encoding starten</code> startet HandBrake mit best\u00e4tigter Auswahl Laufend (<code>Analyse</code>, <code>Rippen</code>, <code>Encodieren</code>) <code>Abbrechen</code> setzt Job auf <code>Abgebrochen</code> <code>Fehler</code>, <code>Abgebrochen</code> <code>Retry Rippen</code> neuer Rip-Anlauf <p>Zusatzaktionen je nach Zustand und Jobtyp:</p> <ul> <li><code>Review neu starten</code></li> <li><code>Encode neu starten</code></li> <li><code>RAW neu encodieren</code></li> <li><code>Aus Queue l\u00f6schen</code></li> </ul>"},{"location":"gui/ripper/#review-bereich-bereit-zum-encodieren","title":"Review-Bereich (<code>Bereit zum Encodieren</code>)","text":"<p>Hier wird die effektive Encode-Entscheidung festgelegt:</p> <ul> <li>Titelwahl</li> <li>Audio-/Subtitle-Trackselektion</li> <li>User-Preset</li> <li>Pre-/Post-Encode-Skripte und Ketten</li> <li>HandBrake-Kommandovorschau</li> </ul> <p>Ohne Best\u00e4tigung dieses Blocks startet kein produktiver Encode.</p>"},{"location":"gui/ripper/#raw-ordnerphasen-sichtbar-uber-logsdetails","title":"RAW-Ordnerphasen (sichtbar \u00fcber Logs/Details)","text":"<p>Der Ripper arbeitet mit drei RAW-Zust\u00e4nden:</p> <ul> <li><code>Incomplete_...</code> w\u00e4hrend unvollst\u00e4ndigem Rip</li> <li><code>Rip_Complete_...</code> nach erfolgreich abgeschlossenem Rip, vor Encode</li> <li>ohne Prefix nach erfolgreichem Encode</li> </ul> <p>Das ist wichtig f\u00fcr Recovery und Delete-Vorschau.</p>"},{"location":"gui/ripper/#5-disk-information","title":"5) Disk-Information","text":"<p>Zeigt aktuelle Laufwerksdaten:</p> <ul> <li>Device-Pfad</li> <li>Modell</li> <li>Disc-Label / Mount</li> </ul> <p>Aktionen:</p> <ul> <li><code>Laufwerk neu lesen</code></li> <li><code>Disk neu analysieren</code></li> <li>Metadaten-Dialog \u00f6ffnen</li> </ul> <p>Hinweis:</p> <ul> <li><code>Laufwerk neu lesen</code> ist nicht mehr an einen festen Pipeline-State gebunden.</li> </ul>"},{"location":"gui/ripper/#wichtige-dialoge","title":"Wichtige Dialoge","text":""},{"location":"gui/ripper/#metadaten-auswahlen","title":"Metadaten ausw\u00e4hlen","text":"<p>Im Dialog werden Film-/Serienmetadaten best\u00e4tigt.</p> <p>Sonderf\u00e4lle:</p> <ul> <li>Film-Duplikat: <code>\u00dcbernahme erzwingen</code> oder <code>Multipart movie</code></li> <li>Serie: <code>Disc-Nummer</code> ist Pflicht</li> </ul>"},{"location":"gui/ripper/#vorhandenes-raw-erkannt","title":"Vorhandenes RAW erkannt","text":"<p>Wenn passendes RAW existiert, fordert der Dialog eine explizite Entscheidung:</p> <ul> <li>vorhandenes RAW verwenden</li> <li>RAW verwerfen/l\u00f6schen und neu rippen</li> </ul>"},{"location":"gui/ripper/#queue-eintrag-einfugen","title":"Queue-Eintrag einf\u00fcgen","text":"<p>Du kannst gezielt ab Position einf\u00fcgen:</p> <ul> <li>Skript</li> <li>Skriptkette</li> <li>Wartezeit</li> </ul>"},{"location":"gui/settings/","title":"Settings","text":"<p>Die Seite <code>Settings</code> steuert alle Betriebsparameter von Ripster: Pfade, Tools, Queue-Limits, Metadatenquellen, Automatisierung.</p>"},{"location":"gui/settings/#tab-uberblick","title":"Tab-\u00dcberblick","text":"Tab Hauptzweck <code>Konfiguration</code> globale und profilspezifische Laufzeitsettings <code>Scripte</code> einzelne Skripte verwalten und testen <code>Skriptketten</code> mehrstufige Abl\u00e4ufe aus Skript/Warten <code>Encode-Presets</code> wiederverwendbare HandBrake-Profile f\u00fcr Review <code>Cronjobs</code> zeitgesteuerte Ausf\u00fchrung von Skript/Kette"},{"location":"gui/settings/#tab-konfiguration","title":"Tab <code>Konfiguration</code>","text":""},{"location":"gui/settings/#bedienprinzip","title":"Bedienprinzip","text":"<ol> <li>Werte \u00e4ndern.</li> <li><code>\u00c4nderungen speichern</code>.</li> <li>erst danach sind Werte f\u00fcr neue Aktionen wirksam.</li> </ol> <p>Zus\u00e4tzliche Bedienhilfen:</p> <ul> <li><code>Neu laden</code> verwirft lokale Formzust\u00e4nde</li> <li><code>\u00c4nderungen verwerfen</code> setzt auf zuletzt gespeicherten Stand</li> <li><code>PushOver Test</code> validiert Benachrichtigungszugang sofort</li> </ul>"},{"location":"gui/settings/#was-wirkt-unmittelbar-was-erst-bei-neuem-job","title":"Was wirkt unmittelbar, was erst bei neuem Job?","text":"<ul> <li>Queue-/Parallelit\u00e4t wirkt auf neue Queue-Entscheidungen und Pump-L\u00e4ufe.</li> <li>Tool-/Pfad-/Template-\u00c4nderungen wirken vollst\u00e4ndig auf neu gestartete Schritte.</li> <li>Bereits laufende Prozesse \u00fcbernehmen \u00c4nderungen in der Regel nicht r\u00fcckwirkend.</li> </ul>"},{"location":"gui/settings/#welche-bereiche-im-alltag-kritisch-sind","title":"Welche Bereiche im Alltag kritisch sind","text":"Bereich Kritisch f\u00fcr <code>Pfade</code> wo RAW, finale Ausgaben, Converter/Audiobook-Dateien landen <code>Tools</code> ob Analyze/Rip/Encode ausf\u00fchrbar sind <code>Metadaten</code> Film-/Serienzuordnung und Episode-Mapping <code>Queue-Limits</code> Durchsatz und Reihenfolgeverhalten <code>Monitoring</code> Sichtbarkeit von Last und Speicherengp\u00e4ssen <code>Benachrichtigungen</code> PushOver-Ereignisse je Pipelinephase"},{"location":"gui/settings/#tab-scripte","title":"Tab <code>Scripte</code>","text":"<p>Funktionen:</p> <ul> <li>anlegen, bearbeiten, l\u00f6schen</li> <li>testen (<code>Test</code>)</li> <li>sortieren</li> </ul> <p>Testausf\u00fchrung liefert:</p> <ul> <li>Exit-Code</li> <li>Dauer</li> <li>stdout/stderr</li> </ul> <p>Wichtig:</p> <ul> <li>Nur erfolgreiche Skripte sollten in produktive Pre-/Post-Listen \u00fcbernommen werden.</li> </ul>"},{"location":"gui/settings/#tab-skriptketten","title":"Tab <code>Skriptketten</code>","text":"<p>Ketten bestehen aus Schritten:</p> <ul> <li><code>Skript</code></li> <li><code>Warten</code></li> </ul> <p>Typischer Einsatz:</p> <ul> <li>externe Hooks vor/nach Encode</li> <li>gestaffelte Abl\u00e4ufe mit Pausen</li> </ul> <p>In der Queue erscheinen Ketten als eigene Eintr\u00e4ge und werden dort ausgef\u00fchrt.</p>"},{"location":"gui/settings/#tab-encode-presets","title":"Tab <code>Encode-Presets</code>","text":"<p>Ein Preset b\u00fcndelt:</p> <ul> <li>optionales HandBrake-Preset (<code>-Z</code>)</li> <li>optionale Extra-Args</li> <li>Medienziel (Universell/Blu-ray/DVD/Sonstiges)</li> </ul> <p>Im Ripper-Review kannst du Presets pro Job ausw\u00e4hlen, statt Parameter jedes Mal neu zu setzen.</p>"},{"location":"gui/settings/#tab-cronjobs","title":"Tab <code>Cronjobs</code>","text":"<p>Funktionen:</p> <ul> <li>Cronjob anlegen und bearbeiten</li> <li>Quelle w\u00e4hlen (<code>Skript</code> oder <code>Skriptkette</code>)</li> <li>Cron-Ausdruck pr\u00fcfen</li> <li><code>Jetzt ausf\u00fchren</code></li> <li>Job-Logs ansehen</li> <li>Aktivierung/Pushover toggeln</li> </ul> <p>Empfehlung:</p> <ol> <li>Skript/Kette manuell testen.</li> <li>Dann Cron aktivieren.</li> <li>Erste automatische L\u00e4ufe aktiv im Runtime-Status kontrollieren.</li> </ol>"},{"location":"gui/settings/#verbindung-zur-einstellungsreferenz","title":"Verbindung zur Einstellungsreferenz","text":"<p>Die vollst\u00e4ndige Wirkung jeder einzelnen Einstellung steht in Einstellungsreferenz.</p>"},{"location":"pipeline/","title":"Anhang: Pipeline intern","text":"<p>Dieser Abschnitt beschreibt die technische Pipeline-Logik hinter den UI-Workflows.</p> <ul> <li> <p> Workflow &amp; Zust\u00e4nde</p> <p>Zustandsmodell, \u00dcberg\u00e4nge, Queue-Verhalten.</p> <p> Workflow</p> </li> <li> <p> Encode-Planung</p> <p>Aufbereitung von Titeln/Tracks und Best\u00e4tigungslogik.</p> <p> Encoding</p> </li> <li> <p> Playlist-Analyse</p> <p>Bewertung mehrdeutiger Blu-ray-Playlists.</p> <p> Playlist-Analyse</p> </li> <li> <p> Pre-/Post-Encode-Ausf\u00fchrungen</p> <p>Skript- und Kettenlauf vor/nach dem Encoding.</p> <p> Encode-Skripte</p> </li> </ul>"},{"location":"pipeline/#zuruck-zum-handbuch","title":"Zur\u00fcck zum Handbuch","text":"<ul> <li>Workflows aus Nutzersicht</li> </ul>"},{"location":"pipeline/encoding/","title":"Encode-Planung &amp; Track-Auswahl","text":"<p>Vor dem eigentlichen Encoding erstellt Ripster einen Encode-Plan und zeigt ihn im Review an.</p>"},{"location":"pipeline/encoding/#ablauf","title":"Ablauf","text":"<pre><code>Quelle bestimmen (Disc/RAW)\n -&gt; HandBrake-Scan (--scan --json)\n -&gt; Plan erstellen (Titel, Audio, Untertitel)\n -&gt; Status: Bereit zum Encodieren\n -&gt; Benutzer bestaetigt Auswahl\n -&gt; finaler HandBrake-Aufruf\n</code></pre>"},{"location":"pipeline/encoding/#review-inhalt-status-bereit-zum-encodieren","title":"Review-Inhalt (Status: <code>Bereit zum Encodieren</code>)","text":"<ul> <li>ausw\u00e4hlbarer Encode-Titel</li> <li>Audio-Track-Auswahl</li> <li>Untertitel-Track-Auswahl inkl. Flags</li> <li><code>burnIn</code></li> <li><code>forced</code></li> <li><code>defaultTrack</code></li> <li>optionale User-Presets (HandBrake Preset + Extra Args)</li> <li>optionale Pre-/Post-Skripte und Ketten</li> </ul>"},{"location":"pipeline/encoding/#bestaetigung-confirm-encode","title":"Bestaetigung (<code>confirm-encode</code>)","text":"<p>Typischer Payload:</p> <pre><code>{\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</code></pre> <p>Die best\u00e4tigte Auswahl wird im Job gespeichert und f\u00fcr Neustarts wiederverwendet.</p>"},{"location":"pipeline/encoding/#handbrake-aufruf","title":"HandBrake-Aufruf","text":"<p>Grundstruktur:</p> <pre><code>HandBrakeCLI \\\n -i &lt;input&gt; \\\n -o &lt;output&gt; \\\n -t &lt;titleId&gt; \\\n -Z \"&lt;preset&gt;\" \\\n &lt;extra-args&gt; \\\n -a &lt;audioTrackIds|none&gt; \\\n -s &lt;subtitleTrackIds|none&gt;\n</code></pre> <p>Untertitel-Flags werden bei Bedarf erg\u00e4nzt:</p> <ul> <li><code>--subtitle-burned=&lt;id&gt;</code></li> <li><code>--subtitle-default=&lt;id&gt;</code></li> <li><code>--subtitle-forced=&lt;id&gt;</code> oder <code>--subtitle-forced</code></li> </ul>"},{"location":"pipeline/encoding/#pre-post-encode-ausfuhrungen","title":"Pre-/Post-Encode-Ausf\u00fchrungen","text":"<ul> <li>Pre-Encode l\u00e4uft vor HandBrake</li> <li>Post-Encode l\u00e4uft nach HandBrake</li> </ul> <p>Fehlerverhalten:</p> <ul> <li>Pre-Encode-Fehler: Job endet mit Status <code>Fehler</code> (Encode startet nicht)</li> <li>Post-Encode-Fehler: Job kann <code>Fertig</code> bleiben, enth\u00e4lt aber Fehlerhinweis/Script-Summary</li> </ul>"},{"location":"pipeline/encoding/#dateinamenordner","title":"Dateinamen/Ordner","text":"<p>Der finale Outputpfad wird aus den Templates in den Settings aufgebaut.</p> <p>Platzhalter:</p> <ul> <li><code>${title}</code></li> <li><code>${year}</code></li> <li><code>${imdbId}</code></li> </ul> <p>Ung\u00fcltige Dateizeichen werden bereinigt.</p>"},{"location":"pipeline/playlist-analysis/","title":"Playlist-Analyse","text":"<p>Ripster analysiert bei Blu-ray-\u00e4hnlichen Quellen Playlists und fordert bei Mehrdeutigkeit eine manuelle Auswahl an.</p>"},{"location":"pipeline/playlist-analysis/#ziel","title":"Ziel","text":"<p>Erkennen, welche Playlist sehr wahrscheinlich der Hauptfilm ist, statt versehentlich eine Fake-/Dummy-Playlist zu verwenden.</p>"},{"location":"pipeline/playlist-analysis/#eingabedaten","title":"Eingabedaten","text":"<p>Die Analyse basiert auf MakeMKV-Infos (u. a. Playlist-/Segment-Struktur, Laufzeiten, Titelzuordnung).</p>"},{"location":"pipeline/playlist-analysis/#auswertung-vereinfacht","title":"Auswertung (vereinfacht)","text":"<p>F\u00fcr Kandidaten werden u. a. ber\u00fccksichtigt:</p> <ul> <li>Laufzeit</li> <li>Segment-Reihenfolge</li> <li>R\u00fcckw\u00e4rtsspr\u00fcnge/gro\u00dfe Spr\u00fcnge</li> <li>Koh\u00e4renz linearer Segmentfolgen</li> <li>Duplikatgruppen mit \u00e4hnlicher Laufzeit</li> </ul> <p>Daraus entstehen intern Kandidatenlisten, Bewertungen und eine Empfehlung.</p>"},{"location":"pipeline/playlist-analysis/#wann-muss-der-benutzer-entscheiden","title":"Wann muss der Benutzer entscheiden?","text":"<p>Wenn nach Filterung mehr als ein relevanter Kandidat \u00fcbrig bleibt, wechselt der Job in der GUI auf:</p> <ul> <li><code>Warte auf Auswahl</code></li> </ul> <p>Dann muss eine Playlist best\u00e4tigt werden, bevor der Workflow weiterl\u00e4uft.</p>"},{"location":"pipeline/playlist-analysis/#konfigurationseinfluss","title":"Konfigurationseinfluss","text":"Feld in <code>Settings</code> Wirkung <code>Minimale Titellaenge (Minuten)</code> Mindestlaufzeit f\u00fcr Kandidaten <p>Default ist aktuell <code>60</code> Minuten.</p>"},{"location":"pipeline/playlist-analysis/#ui-verhalten","title":"UI-Verhalten","text":"<p>Bei manueller Entscheidung zeigt das Ripper Kandidaten inkl. Score/Bewertung und markiert eine Empfehlung.</p> <p>Nach Best\u00e4tigung:</p> <ul> <li>mit vorhandenem RAW -&gt; zur\u00fcck zu <code>Mediainfo-Pruefung</code></li> <li>ohne RAW -&gt; Startpfad \u00fcber <code>Startbereit</code> / <code>Rippen</code></li> </ul>"},{"location":"pipeline/post-encode-scripts/","title":"Encode-Skripte (Pre &amp; Post)","text":"<p>Ripster kann Skripte und Skript-Ketten vor und nach dem Encode ausf\u00fchren.</p>"},{"location":"pipeline/post-encode-scripts/#ablauf","title":"Ablauf","text":"<pre><code>Bereit zum Encodieren\n -&gt; Pre-Encode Skripte/Ketten\n -&gt; HandBrake Encoding\n -&gt; Post-Encode Skripte/Ketten\n -&gt; Fertig oder Fehler\n</code></pre>"},{"location":"pipeline/post-encode-scripts/#auswahl-im-review","title":"Auswahl im Review","text":"<p>Im Review-Panel kannst du getrennt w\u00e4hlen:</p> <ul> <li><code>selectedPreEncodeScriptIds</code></li> <li><code>selectedPostEncodeScriptIds</code></li> <li><code>selectedPreEncodeChainIds</code></li> <li><code>selectedPostEncodeChainIds</code></li> </ul>"},{"location":"pipeline/post-encode-scripts/#fehlerverhalten","title":"Fehlerverhalten","text":"<ul> <li>Pre-Encode-Fehler stoppen die Kette und f\u00fchren zu <code>Fehler</code>.</li> <li>Post-Encode-Fehler stoppen die restlichen Post-Schritte; Job kann dennoch <code>Fertig</code> sein (mit Fehlerzusatz im Status/Log).</li> </ul>"},{"location":"pipeline/post-encode-scripts/#verfugbare-umgebungsvariablen","title":"Verf\u00fcgbare Umgebungsvariablen","text":"<p>Beim Script-Run werden gesetzt:</p> <ul> <li><code>RIPSTER_SCRIPT_RUN_AT</code></li> <li><code>RIPSTER_JOB_ID</code></li> <li><code>RIPSTER_JOB_TITLE</code></li> <li><code>RIPSTER_MODE</code></li> <li><code>RIPSTER_INPUT_PATH</code></li> <li><code>RIPSTER_OUTPUT_PATH</code></li> <li><code>RIPSTER_RAW_PATH</code></li> <li><code>RIPSTER_SCRIPT_ID</code></li> <li><code>RIPSTER_SCRIPT_NAME</code></li> <li><code>RIPSTER_SCRIPT_SOURCE</code></li> </ul>"},{"location":"pipeline/post-encode-scripts/#skript-ketten","title":"Skript-Ketten","text":"<p>Ketten unterst\u00fctzen zwei Step-Typen:</p> <ul> <li><code>script</code> (f\u00fchrt ein hinterlegtes Skript aus)</li> <li><code>wait</code> (wartet <code>waitSeconds</code>)</li> </ul> <p>Bei Fehler in einem Script-Step wird die Kette abgebrochen.</p>"},{"location":"pipeline/post-encode-scripts/#testlaufe","title":"Testl\u00e4ufe","text":"<ul> <li>Skript testen: <code>POST /api/settings/scripts/:id/test</code></li> <li>Kette testen: <code>POST /api/settings/script-chains/:id/test</code></li> </ul> <p>Ergebnisse enthalten Erfolg/Exit-Code, Laufzeit und stdout/stderr.</p>"},{"location":"pipeline/workflow/","title":"Workflow &amp; Zust\u00e4nde","text":"<p>Ripster steuert den Ablauf als Zustandsmaschine.</p>"},{"location":"pipeline/workflow/#zustandsdiagramm-vereinfacht","title":"Zustandsdiagramm (vereinfacht)","text":"<pre><code>flowchart LR\n A[Bereit] --&gt; B[Medium erkannt]\n B --&gt; C[Analyse]\n C --&gt; D[Metadatenauswahl]\n D --&gt; E[Startbereit]\n E --&gt; F[Rippen]\n E --&gt; G[Mediainfo-Pruefung]\n G --&gt; H[Warte auf Auswahl]\n H --&gt; G\n G --&gt; I[Bereit zum Encodieren]\n I --&gt; J[Encodieren]\n J --&gt; K[Fertig]\n J --&gt; L[Fehler]\n F --&gt; L\n F --&gt; M[Abgebrochen]</code></pre>"},{"location":"pipeline/workflow/#statusliste-gui-anzeige","title":"Statusliste (GUI-Anzeige)","text":"Status in der GUI Bedeutung <code>Bereit</code> Wartet auf Disc <code>Medium erkannt</code> Disc wurde erkannt <code>Analyse</code> MakeMKV-Analyse l\u00e4uft <code>Metadatenauswahl</code> Metadaten m\u00fcssen best\u00e4tigt werden <code>Warte auf Auswahl</code> Playlist-Auswahl ist erforderlich <code>Warte auf Auswahl</code> auch f\u00fcr RAW-Entscheidung bei vorhandenen Quelldaten genutzt <code>Startbereit</code> kurzer \u00dcbergang vor Start <code>Rippen</code> MakeMKV-Rip l\u00e4uft <code>Mediainfo-Pruefung</code> Titel/Spuren werden ausgewertet <code>Bereit zum Encodieren</code> Review ist bereit <code>Encodieren</code> HandBrake l\u00e4uft <code>CD-Metadatenauswahl</code> CD-Metadaten m\u00fcssen best\u00e4tigt werden <code>CD-Startbereit</code> CD-Job wartet auf Start <code>CD-Analyse</code> CD-Track-/Strukturaufbereitung <code>CD-Rippen</code> Audio-CD-Rip l\u00e4uft <code>CD-Encodieren</code> CD-Encode l\u00e4uft <code>Fertig</code> erfolgreich abgeschlossen <code>Abgebrochen</code> manuell oder technisch abgebrochen <code>Fehler</code> fehlgeschlagen"},{"location":"pipeline/workflow/#typische-pfade","title":"Typische Pfade","text":""},{"location":"pipeline/workflow/#standardfall-kein-vorhandenes-raw","title":"Standardfall (kein vorhandenes RAW)","text":"<ol> <li>Medium erkannt</li> <li>Analyse + Metadaten</li> <li>Rippen</li> <li>Mediainfo-Pruefung</li> <li>Bereit zum Encodieren</li> <li>Encodieren</li> <li>Fertig</li> </ol>"},{"location":"pipeline/workflow/#vorhandenes-raw","title":"Vorhandenes RAW","text":"<p><code>Startbereit</code> springt direkt zu <code>Mediainfo-Pruefung</code> (kein neuer Rip).</p>"},{"location":"pipeline/workflow/#mehrdeutige-blu-ray-playlist","title":"Mehrdeutige Blu-ray-Playlist","text":"<p><code>Mediainfo-Pruefung</code> -&gt; <code>Warte auf Auswahl</code> bis Playlist best\u00e4tigt wurde.</p>"},{"location":"pipeline/workflow/#vorhandenes-raw-mit-nutzerentscheidung","title":"Vorhandenes RAW mit Nutzerentscheidung","text":"<p><code>Metadatenauswahl</code> -&gt; <code>Warte auf Auswahl</code> bis Entscheidung f\u00fcr Weiterverarbeitung (<code>continue</code>) oder Neustart getroffen wurde.</p>"},{"location":"pipeline/workflow/#cd-flow","title":"CD-Flow","text":"<p><code>CD-Metadatenauswahl</code> -&gt; <code>CD-Startbereit</code> -&gt; <code>CD-Rippen</code> -&gt; <code>CD-Encodieren</code> -&gt; <code>Fertig</code></p>"},{"location":"pipeline/workflow/#queue-verhalten","title":"Queue-Verhalten","text":"<p>Wenn der Wert <code>Parallele Jobs</code> erreicht ist:</p> <ul> <li>neue Starts werden als Queue-Eintr\u00e4ge abgelegt</li> <li>die Queue kann zus\u00e4tzlich Nicht-Job-Eintr\u00e4ge enthalten (<code>Skript</code>, <code>Kette</code>, <code>Warten</code>)</li> <li>Reihenfolge ist per UI/API \u00e4nderbar</li> </ul>"},{"location":"pipeline/workflow/#abbruch-wiederaufnahme-neustart","title":"Abbruch, Wiederaufnahme, Neustart","text":"<ul> <li><code>Abbrechen</code>: laufenden Job stoppen oder Queue-Eintrag entfernen</li> <li><code>Retry Rippen</code>: Fehler-/Abbruch-Job erneut starten</li> <li><code>RAW neu encodieren</code>: aus vorhandenem RAW neu encodieren</li> <li><code>Review neu starten</code>: Review aus RAW neu aufbauen</li> <li><code>Encode neu starten</code>: Encoding mit letzter best\u00e4tigter Auswahl neu starten</li> </ul>"},{"location":"tools/","title":"Anhang: Externe Tools","text":"<p>Ripster orchestriert externe CLI-Tools. Dieser Abschnitt erkl\u00e4rt deren Rolle im Gesamtsystem.</p> <ul> <li> <p> MakeMKV</p> <p>Disc-Analyse und Ripping.</p> <p> MakeMKV</p> </li> <li> <p> HandBrake</p> <p>Video-Encoding inklusive Preset-Logik.</p> <p> HandBrake</p> </li> <li> <p> MediaInfo</p> <p>Track-/Containeranalyse f\u00fcr Review und Auswahl.</p> <p> MediaInfo</p> </li> </ul>"},{"location":"tools/handbrake/","title":"HandBrake","text":"<p>Ripster verwendet <code>HandBrakeCLI</code> f\u00fcr Scan und Encode.</p>"},{"location":"tools/handbrake/#verwendete-aufrufe","title":"Verwendete Aufrufe","text":""},{"location":"tools/handbrake/#scan-review-aufbau","title":"Scan (Review-Aufbau)","text":"<pre><code>HandBrakeCLI --scan --json -i &lt;input&gt; -t 0\n</code></pre>"},{"location":"tools/handbrake/#encode-vereinfacht","title":"Encode (vereinfacht)","text":"<pre><code>HandBrakeCLI \\\n -i &lt;input&gt; \\\n -o &lt;output&gt; \\\n -t &lt;titleId&gt; \\\n -Z \"&lt;preset&gt;\" \\\n &lt;extra-args&gt; \\\n -a &lt;audioTrackIds|none&gt; \\\n -s &lt;subtitleTrackIds|none&gt;\n</code></pre> <p>Optional erg\u00e4nzt Ripster:</p> <ul> <li><code>--subtitle-burned=&lt;id&gt;</code></li> <li><code>--subtitle-default=&lt;id&gt;</code></li> <li><code>--subtitle-forced=&lt;id&gt;</code> oder <code>--subtitle-forced</code></li> </ul>"},{"location":"tools/handbrake/#presets-auslesen","title":"Presets auslesen","text":"<p>Ripster liest Presets mit:</p> <pre><code>HandBrakeCLI -z\n</code></pre>"},{"location":"tools/handbrake/#relevante-felder-in-settings","title":"Relevante Felder in <code>Settings</code>","text":"Feldname in der GUI Bedeutung <code>HandBrake Kommando</code> CLI-Binary <code>HandBrake Preset</code> (Blu-ray/DVD) profilspezifisches Preset <code>HandBrake Extra Args</code> (Blu-ray/DVD) profilspezifische Zusatzargumente <code>Ausgabeformat</code> (Blu-ray/DVD) Dateiendung der finalen Datei <code>Encode-Neustart: unvollst\u00e4ndige Ausgabe l\u00f6schen</code> unvollst\u00e4ndige Ausgabe bei Neustart l\u00f6schen"},{"location":"tools/handbrake/#fortschritts-parsing","title":"Fortschritts-Parsing","text":"<p>Ripster parst HandBrake-Stderr (Prozent/ETA/Detail) und sendet WebSocket-Progress (<code>PIPELINE_PROGRESS</code>).</p>"},{"location":"tools/handbrake/#troubleshooting","title":"Troubleshooting","text":"<ul> <li>Preset nicht gefunden: Preset-Namen mit <code>HandBrakeCLI -z</code> pr\u00fcfen</li> <li>sehr langsames Encoding: Preset/Extra-Args pr\u00fcfen (z. B. <code>--encoder-preset</code>)</li> </ul> <p>Das Produktions-Installer-Script <code>install.sh</code> bietet eine Option zur Installation eines geb\u00fcndelten HandBrakeCLI-Binaries mit NVDEC-Unterst\u00fctzung (NVIDIA GPU-Dekodierung). Diese Option erscheint interaktiv w\u00e4hrend der Installation.</p>"},{"location":"tools/makemkv/","title":"MakeMKV","text":"<p>Ripster nutzt <code>makemkvcon</code> f\u00fcr Disc-Analyse und Rip.</p>"},{"location":"tools/makemkv/#verwendete-aufrufe","title":"Verwendete Aufrufe","text":""},{"location":"tools/makemkv/#analyse","title":"Analyse","text":"<pre><code>makemkvcon -r info &lt;source&gt;\n</code></pre> <p><code>&lt;source&gt;</code> ist typischerweise:</p> <ul> <li><code>disc:&lt;index&gt;</code> (Auto-Modus)</li> <li><code>dev:/dev/sr0</code> (explicit)</li> <li><code>file:&lt;path&gt;</code> (Datei/Ordner-Analyse)</li> </ul>"},{"location":"tools/makemkv/#rip-mkv-modus","title":"Rip (MKV-Modus)","text":"<pre><code>makemkvcon mkv &lt;source&gt; &lt;title-or-all&gt; &lt;rawDir&gt; [--minlength=...] [...extraArgs]\n</code></pre>"},{"location":"tools/makemkv/#rip-backup-modus","title":"Rip (Backup-Modus)","text":"<pre><code>makemkvcon backup &lt;source&gt; &lt;rawDir&gt; --decrypt\n</code></pre>"},{"location":"tools/makemkv/#registrierungsschlussel-optional","title":"Registrierungsschl\u00fcssel (optional)","text":"<p>Wenn in <code>Settings</code> ein <code>MakeMKV Key</code> gesetzt ist, f\u00fchrt Ripster vor Analyse/Rip aus:</p> <pre><code>makemkvcon reg &lt;key&gt;\n</code></pre>"},{"location":"tools/makemkv/#relevante-felder-in-settings","title":"Relevante Felder in <code>Settings</code>","text":"Feldname in der GUI Bedeutung <code>MakeMKV Kommando</code> CLI-Binary <code>MakeMKV Source Index</code> Source-Index im Auto-Modus <code>Minimale Titellaenge (Minuten)</code> Mindestlaufzeitfilter <code>MakeMKV Rip Modus</code> (Blu-ray/DVD) <code>mkv</code> oder <code>backup</code> <code>MakeMKV Analyze Extra Args</code> (Blu-ray/DVD) Zusatzargumente f\u00fcr Analyse <code>MakeMKV Rip Extra Args</code> (Blu-ray/DVD) Zusatzargumente f\u00fcr Rip"},{"location":"tools/makemkv/#hinweise","title":"Hinweise","text":"<ul> <li>Blu-ray-Backups werden oft f\u00fcr robuste Playlist-Analyse genutzt.</li> <li>MakeMKV-Ausgaben werden geparst und als <code>makemkvInfo</code> im Job gespeichert.</li> </ul>"},{"location":"tools/mediainfo/","title":"MediaInfo","text":"<p>Ripster nutzt <code>mediainfo</code> zur JSON-Analyse von Medien-Dateien.</p>"},{"location":"tools/mediainfo/#aufruf","title":"Aufruf","text":"<pre><code>mediainfo --Output=JSON &lt;input&gt;\n</code></pre> <p>Der Input ist typischerweise eine RAW-Datei oder ein vom Workflow gew\u00e4hlter Inputpfad.</p>"},{"location":"tools/mediainfo/#verwendung-in-ripster","title":"Verwendung in Ripster","text":"<ul> <li>Track-/Codec-Metadaten f\u00fcr Review-Plan</li> <li>Fallback-Informationen in bestimmten Analysepfaden</li> <li>Persistenz als <code>mediainfoInfo</code> im Job</li> </ul>"},{"location":"tools/mediainfo/#relevante-settings","title":"Relevante Settings","text":"Key Bedeutung <code>mediainfo_command</code> CLI-Binary <code>mediainfo_extra_args_bluray</code> / <code>_dvd</code> profilspezifische Zusatzargumente"},{"location":"tools/mediainfo/#troubleshooting","title":"Troubleshooting","text":"<ul> <li>JSON-Test: <code>mediainfo --Output=JSON &lt;datei&gt;</code></li> <li>unbekannte Sprache erscheint oft als <code>und</code> (undetermined)</li> </ul>"},{"location":"workflows/","title":"Workflows aus Nutzersicht","text":"<p>Diese Seite beschreibt die produktiven Abl\u00e4ufe als Handbuch: welche Entscheidung an welcher Stelle f\u00e4llt, welche Objekte dabei entstehen (Job, Container, Child-Job) und welche Dateien in welchen Ordnern landen.</p>"},{"location":"workflows/#gemeinsame-grundlagen","title":"Gemeinsame Grundlagen","text":""},{"location":"workflows/#statuskette-im-normalfall","title":"Statuskette im Normalfall","text":"<p><code>Medium erkannt</code> -&gt; <code>Analyse</code> -&gt; <code>Metadatenauswahl</code> -&gt; <code>Startbereit</code> -&gt; <code>Rippen</code> -&gt; <code>Mediainfo-Pruefung</code> -&gt; <code>Bereit zum Encodieren</code> -&gt; <code>Encodieren</code> -&gt; <code>Fertig</code></p> <p>Abweichungen:</p> <ul> <li>bei Blu-ray/DVD mit n\u00f6tiger Playlistwahl: <code>Mediainfo-Pruefung</code> -&gt; <code>Warte auf Auswahl</code> -&gt; zur\u00fcck in <code>Mediainfo-Pruefung</code></li> <li>bei vorhandenem RAW: <code>Startbereit</code> springt direkt in Review/Pr\u00fcfung ohne neuen Laufwerks-Rip</li> <li>bei Fehler/Abbruch: <code>Fehler</code> oder <code>Abgebrochen</code> mit Wiederanlauf-Aktionen</li> </ul>"},{"location":"workflows/#job-und-container-typen","title":"Job- und Container-Typen","text":"Typ Zweck Was enth\u00e4lt der Typ? Normaler Disc-Job Einzel-Film oder einzelne Disc RAW-Pfad, Output-Pfad, Logs, Review <code>dvd_series_container</code> logischer Serien-Container (pro Serie/Staffel) Metadatenanker, aggregierter Status \u00fcber Kinder <code>dvd_series_child</code> einzelne Serien-Disc oder Serien-Episode-Job eigentliche RAW-/Encode-Arbeit <code>multipart_movie_container</code> logischer Multipart-Film-Container Zusammenfassung f\u00fcr mehrere Film-Discs <code>multipart_movie_child</code> einzelne Disc innerhalb Multipart-Film Disc-spezifische RAW-/Encode-Daten <p>Wichtig:</p> <ul> <li>Container sind prim\u00e4r Ordnungs- und Statusobjekte.</li> <li>Dateien (RAW/Output) h\u00e4ngen an den Child-Jobs.</li> </ul>"},{"location":"workflows/#workflow-a-film-blu-raydvd","title":"Workflow A: Film (Blu-ray/DVD)","text":""},{"location":"workflows/#1-disc-analysieren-und-film-metadaten-wahlen","title":"1) Disc analysieren und Film-Metadaten w\u00e4hlen","text":"<ol> <li>Im <code>Ripper</code> <code>Analyse starten</code>.</li> <li>Im Metadaten-Dialog Film ausw\u00e4hlen (OMDb bzw. manuelle Film-Metadaten).</li> <li><code>Auswahl \u00fcbernehmen</code>.</li> </ol> <p>Wirkung:</p> <ul> <li>der Job wird als Film-Workflow gef\u00fchrt</li> <li>Film-Fingerprint f\u00fcr Duplikaterkennung wird gebildet (IMDb oder Titel/Jahr)</li> </ul>"},{"location":"workflows/#2-duplikatfall-entscheiden","title":"2) Duplikatfall entscheiden","text":"<p>Wenn Film mit gleichem Fingerprint bereits existiert, gibt es zwei Wege:</p> <ol> <li><code>\u00dcbernahme erzwingen</code> (<code>allow_new</code>): neuer unabh\u00e4ngiger Film-Job</li> <li><code>Multipart movie</code>: neuer Job wird mit bestehendem Job in einem Multipart-Container verkn\u00fcpft</li> </ol> <p>Bei Multipart gilt:</p> <ul> <li>beide Discs brauchen unterschiedliche <code>Disc-Nummer</code></li> <li>gleiche Disc-Nummer blockiert den Vorgang (<code>MULTIPART_DISC_ALREADY_EXISTS</code>)</li> </ul>"},{"location":"workflows/#3-raw-verzeichnis-entsteht","title":"3) RAW-Verzeichnis entsteht","text":"<p>Ordnername basiert auf Metadaten plus Job-ID:</p> <ul> <li><code>Incomplete_&lt;Titel&gt; (&lt;Jahr&gt;) - RAW - job-&lt;id&gt;</code></li> </ul> <p>Zustandswechsel des RAW-Ordners:</p> <ul> <li>beim Start: <code>Incomplete_...</code></li> <li>nach erfolgreichem Rip: <code>Rip_Complete_...</code></li> <li>nach erfolgreichem Encode: Prefix wird entfernt (final)</li> </ul>"},{"location":"workflows/#4-review-und-encode","title":"4) Review und Encode","text":"<p>In <code>Bereit zum Encodieren</code>:</p> <ul> <li>Titel w\u00e4hlen</li> <li>Audio-/Subtitle-Spuren festlegen</li> <li>optional User-Preset</li> <li>optional Pre-/Post-Encode-Skripte oder Ketten</li> </ul> <p>Beim Start:</p> <ul> <li>tempor\u00e4re Ausgabe in <code>Incomplete_job-&lt;id&gt;/&lt;Datei&gt;.&lt;ext&gt;</code></li> <li>nach Erfolg Verschiebung in finalen Zielpfad aus Film-Template</li> </ul>"},{"location":"workflows/#workflow-b-serie-blu-raydvd","title":"Workflow B: Serie (Blu-ray/DVD)","text":""},{"location":"workflows/#1-serienmodus-aktivieren","title":"1) Serienmodus aktivieren","text":"<ol> <li>Disc analysieren.</li> <li>Im Metadaten-Dialog Serienmetadaten w\u00e4hlen (TMDb-basiert).</li> <li><code>Disc-Nummer</code> setzen (Pflicht, ab 1).</li> </ol> <p>Wirkung:</p> <ul> <li>Workflow-Kind wird <code>series</code></li> <li>Seriencontainer wird gesucht/angelegt</li> <li>aktueller Job wird als <code>dvd_series_child</code> darunter gef\u00fchrt</li> </ul>"},{"location":"workflows/#2-seriencontainer-und-disc-konflikte","title":"2) Seriencontainer und Disc-Konflikte","text":"<p>Beim Speichern wird gepr\u00fcft:</p> <ul> <li>existiert f\u00fcr Serie/Staffel bereits ein Container?</li> <li>existiert in diesem Container bereits dieselbe <code>Disc-Nummer</code>?</li> </ul> <p>Wenn ja, wird blockiert (<code>SERIES_DISC_ALREADY_EXISTS</code>).</p>"},{"location":"workflows/#3-serien-raw-pfad","title":"3) Serien-RAW-Pfad","text":"<p>F\u00fcr Serien wird ein eigener RAW-Pfad bevorzugt:</p> <ul> <li>Blu-ray: <code>raw_dir_bluray_series</code> (Fallback: <code>raw_dir_bluray</code>)</li> <li>DVD: <code>raw_dir_dvd_series</code> (Fallback: <code>raw_dir_dvd</code>)</li> </ul> <p>RAW-Namensbasis enth\u00e4lt Staffel/Disc (<code>SxDy</code>) und wird ebenfalls mit den Prefix-Phasen gef\u00fchrt:</p> <ul> <li><code>Incomplete_...</code> -&gt; <code>Rip_Complete_...</code> -&gt; final</li> </ul>"},{"location":"workflows/#4-serien-review-und-episoden-ausgabe","title":"4) Serien-Review und Episoden-Ausgabe","text":"<p>Beim Review werden Episoden-/Titelzuordnungen genutzt. Je nach Auswahl:</p> <ul> <li>Einzel-Episode: Episode-Template</li> <li>Multi-Episode-Titel: Multi-Episode-Template</li> </ul> <p>Ausgabepfad:</p> <ul> <li>Root: <code>series_dir_*</code> (Fallback auf <code>movie_dir_*</code>)</li> <li>Dateiname/Unterordner aus Serien-Template</li> </ul>"},{"location":"workflows/#5-multi-episode-disc-als-batch","title":"5) Multi-Episode-Disc als Batch","text":"<p>Wenn mehrere Episoden-Titel aus einer Disc encodiert werden:</p> <ul> <li>Ripster erzeugt Episode-Child-Jobs</li> <li>der urspr\u00fcngliche Disc-Job dient als Batch-Anker</li> <li>RAW-Finalisierung erfolgt erst, wenn alle Episode-Encodes abgeschlossen sind</li> </ul>"},{"location":"workflows/#workflow-c-multipart-film","title":"Workflow C: Multipart-Film","text":""},{"location":"workflows/#wann-dieser-flow-gedacht-ist","title":"Wann dieser Flow gedacht ist","text":"<ul> <li>gleicher Film auf mehreren Discs (z. B. Disc 1/Disc 2)</li> <li>Zusammengeh\u00f6rigkeit soll in Historie und Detailansichten sichtbar sein</li> </ul>"},{"location":"workflows/#ablauf","title":"Ablauf","text":"<ol> <li>Film-Metadaten w\u00e4hlen.</li> <li>Duplikatdialog -&gt; <code>Multipart movie</code>.</li> <li>Disc-Nummern f\u00fcr bestehenden und neuen Job festlegen.</li> </ol> <p>Wirkung:</p> <ul> <li><code>multipart_movie_container</code> wird angelegt oder wiederverwendet</li> <li>beteiligte Jobs werden <code>multipart_movie_child</code></li> <li>Container f\u00fchrt gemeinsame Metadaten, Kinder f\u00fchren je Disc die eigentlichen Artefakte</li> </ul>"},{"location":"workflows/#workflow-d-vorhandenes-raw-raw-import","title":"Workflow D: Vorhandenes RAW / RAW-Import","text":""},{"location":"workflows/#fall-1-raw-bereits-vorhanden-bei-metadatenubernahme","title":"Fall 1: RAW bereits vorhanden bei Metadaten\u00fcbernahme","text":"<p>Nach Metadaten-\u00dcbernahme erkennt Ripster passende RAW-Ordner und setzt den Job auf Entscheidungsstatus.</p> <p>M\u00f6glichkeiten:</p> <ul> <li>vorhandenes RAW weiterverwenden (kein neuer Rip)</li> <li>RAW l\u00f6schen/ignorieren und neu rippen</li> </ul>"},{"location":"workflows/#fall-2-orphan-raw-aus-database","title":"Fall 2: Orphan-RAW aus <code>/database</code>","text":"<ol> <li>In <code>Database</code> <code>RAW pr\u00fcfen</code>.</li> <li>Bei orphan RAW <code>Job anlegen</code>.</li> <li>Job wird in Historie erzeugt und wie \"Disk analysieren aus RAW\" weiterverarbeitet.</li> </ol>"},{"location":"workflows/#workflow-e-nachbearbeitung-bestehender-jobs","title":"Workflow E: Nachbearbeitung bestehender Jobs","text":"<p>In <code>Historie</code> (Detaildialog):</p> Aktion Wirkung <code>OMDb neu zuordnen</code> Metadaten neu schreiben, Folder-Rename wird best effort angesto\u00dfen <code>Review neu starten</code> Titel-/Spurpr\u00fcfung aus aktuellem RAW neu aufbauen <code>RAW neu encodieren</code> Encode erneut aus vorhandenem RAW starten <code>Encode neu starten</code> letzte best\u00e4tigte Review-Einstellungen wiederverwenden <code>Retry Rippen</code> Rip/Analyse neu starten; alter Job-RAW kann bereinigt werden"},{"location":"workflows/#wo-landet-was-artefakt-matrix","title":"Wo landet was? (Artefakt-Matrix)","text":"Artefakt Film Serie Multipart RAW <code>raw_dir_*</code> bevorzugt <code>raw_dir_*_series</code> pro Child-Job im normalen Film-RAW-Schema Finale Ausgabe <code>movie_dir_*</code> + Film-Template <code>series_dir_*</code> (Fallback <code>movie_dir_*</code>) + Serien-Templates pro Child-Job normaler Film-Output Container-Datensatz nein ja (<code>dvd_series_container</code>) ja (<code>multipart_movie_container</code>) L\u00f6schvorschau in Historie einzelner Job oder Related Scope Container-/Child-bezogen mit Pfadauswahl Container-/Child-bezogen mit Pfadauswahl"},{"location":"workflows/#queue-verhalten-im-alltag","title":"Queue-Verhalten im Alltag","text":"<p>Die Startfreigabe h\u00e4ngt an vier Settings:</p> <ul> <li><code>Max. parallele Film/Video Encodes</code></li> <li><code>Max. parallele Audio CD Jobs</code></li> <li><code>Max. Encodes gesamt</code></li> <li><code>Audio CDs: Queue-Reihenfolge \u00fcberspringen</code></li> </ul> <p>Praxis:</p> <ul> <li>Film/Video- und Audio/CD laufen in getrennten Lanes</li> <li>Gesamtlimit begrenzt beide gemeinsam</li> <li>Queue kann neben Jobs auch <code>Skript</code>, <code>Kette</code>, <code>Warten</code> enthalten</li> </ul>"}]}