{"config":{"lang":["de"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"","title":"Ripster","text":"
Halbautomatische Disc-Ripping-Plattform f\u00fcr DVDs und Blu-rays
Automatisiertes Ripping
Disc einlegen \u2013 Ripster erkennt sie automatisch und startet den Analyse-Workflow mit MakeMKV.
Workflow verstehen
Metadata-Integration
Automatische Suche in der OMDb-Datenbank f\u00fcr Filmtitel, Poster und IMDb-IDs.
Konfiguration
Flexibles Encoding
HandBrake-Encoding mit individueller Track-Auswahl f\u00fcr Audio- und Untertitelspuren.
Encode-Planung
Job-Historie
Vollst\u00e4ndiges Audit-Trail aller Ripping-Jobs mit Logs und Re-Encode-Funktion.
History API
Ripster ist eine webbasierte Anwendung zur halbautomatischen Digitalisierung von DVDs und Blu-rays. Die Anwendung kombiniert bew\u00e4hrte Open-Source-Tools zu einem durchg\u00e4ngigen, komfortablen Workflow:
Disc einlegen \u2192 Erkennung \u2192 Analyse \u2192 Metadaten w\u00e4hlen \u2192 Rippen \u2192 Encodieren \u2192 Fertig\n"},{"location":"#kernfunktionen","title":"Kernfunktionen","text":"Feature Beschreibung Echtzeit-Updates WebSocket-basierte Live-Statusanzeige ohne Reload Intelligente Playlist-Analyse Erkennt Blu-ray Playlist-Verschleierung (Fake-Playlists) Track-Auswahl Individuelle Auswahl von Audio- und Untertitelspuren Orphan-Recovery Import von bereits gerippten Dateien als Jobs PushOver-Benachrichtigungen Mobile Alerts bei Fertigstellung oder Fehlern DB-Korruptions-Recovery Automatische Quarant\u00e4ne bei korrupten SQLite-Dateien Re-Encoding Erneutes Encodieren ohne neu rippen"},{"location":"#technologie-stack","title":"Technologie-Stack","text":"BackendFrontendExterne Tools ws) f\u00fcr Echtzeit-Kommunikationmakemkvcon, HandBrakeCLI, mediainfomakemkvcon Disc-Analyse & MKV/Backup-Ripping HandBrakeCLI Video-Encoding mediainfo Track-Informationen aus gerippten Dateien OMDb API Filmmetadaten (Titel, Poster, IMDb-ID)"},{"location":"#schnellstart","title":"Schnellstart","text":"# 1. Repository klonen\ngit clone https://github.com/YOUR_GITHUB_USERNAME/ripster.git\ncd ripster\n\n# 2. Starten (Node.js >= 20 erforderlich)\n./start.sh\n\n# 3. Browser \u00f6ffnen\nopen http://localhost:5173\n Erste Schritte
Die vollst\u00e4ndige Installationsanleitung mit allen Voraussetzungen findest du unter Erste Schritte.
"},{"location":"#pipeline-uberblick","title":"Pipeline-\u00dcberblick","text":"flowchart LR\n IDLE --> DD[DISC_DETECTED]\n DD --> META[METADATA\\nSELECTION]\n META -->|1 Kandidat| RTS[READY_TO\\nSTART]\n META -->|Obfuskierung| WUD[WAITING_FOR\\nUSER_DECISION]\n WUD --> RTS\n RTS --> RIP[RIPPING]\n RTS -->|Raw vorhanden| MIC\n RIP --> MIC[MEDIAINFO\\nCHECK]\n MIC --> RTE[READY_TO\\nENCODE]\n RTE --> ENC[ENCODING]\n ENC --> PES[POST_ENCODE\\nSCRIPTS]\n ENC -->|keine Skripte| FIN([FINISHED])\n PES --> FIN\n ENC --> ERR([ERROR])\n RIP --> ERR\n\n style FIN fill:#e8f5e9,stroke:#66bb6a,color:#2e7d32\n style ERR fill:#ffebee,stroke:#ef5350,color:#c62828\n style WUD fill:#fff8e1,stroke:#ffa726,color:#e65100\n style PES fill:#f3e5f5,stroke:#ab47bc,color:#6a1b9a\n style ENC fill:#f3e5f5,stroke:#ab47bc,color:#6a1b9a"},{"location":"api/","title":"API-Referenz","text":"Ripster bietet eine REST-API f\u00fcr alle Operationen sowie einen WebSocket-Endpunkt f\u00fcr Echtzeit-Updates.
"},{"location":"api/#basis-url","title":"Basis-URL","text":"http://localhost:3001\n Konfigurierbar \u00fcber die Umgebungsvariable PORT.
Pipeline API
Pipeline-Steuerung: Analyse starten, Metadaten setzen, Ripping und Encoding steuern.
Pipeline API
Settings API
Einstellungen lesen und schreiben.
Settings API
History API
Job-Geschichte abfragen, Jobs l\u00f6schen, Orphan-Ordner importieren.
History API
WebSocket Events
Echtzeit-Events f\u00fcr Pipeline-Status, Fortschritt und Disc-Erkennung.
WebSocket
Die API hat keine Authentifizierung. Sie ist f\u00fcr den Einsatz im lokalen Netzwerk konzipiert.
Produktionsbetrieb
Falls Ripster \u00f6ffentlich erreichbar sein soll, sch\u00fctze die API mit einem Reverse-Proxy (z. B. nginx mit Basic Auth oder OAuth).
"},{"location":"api/#fehlerformat","title":"Fehlerformat","text":"Alle API-Fehler werden im folgenden Format zur\u00fcckgegeben:
{\n \"error\": \"Job nicht gefunden\",\n \"details\": \"Kein Job mit ID 999 vorhanden\"\n}\n HTTP-Statuscodes:
Code Bedeutung200 Erfolg 400 Ung\u00fcltige Anfrage 404 Ressource nicht gefunden 409 Konflikt (z.B. Pipeline bereits aktiv) 500 Interner Serverfehler"},{"location":"api/history/","title":"History API","text":"Endpunkte f\u00fcr die Job-Histoire, Dateimanagement und Orphan-Import.
"},{"location":"api/history/#get-apihistory","title":"GET /api/history","text":"Gibt eine Liste aller Jobs zur\u00fcck, optional gefiltert.
Query-Parameter:
Parameter Typ Beschreibungstatus string Filtert nach Status (z.B. FINISHED, ERROR) search string Sucht in Filmtiteln Beispiel:
GET /api/history?status=FINISHED&search=Inception\n Response:
{\n \"jobs\": [\n {\n \"id\": 42,\n \"status\": \"FINISHED\",\n \"title\": \"Inception\",\n \"imdb_id\": \"tt1375666\",\n \"omdb_year\": \"2010\",\n \"omdb_type\": \"movie\",\n \"omdb_poster\": \"https://...\",\n \"raw_path\": \"/mnt/nas/raw/Inception_t00.mkv\",\n \"output_path\": \"/mnt/nas/movies/Inception (2010).mkv\",\n \"created_at\": \"2024-01-15T10:00:00.000Z\",\n \"updated_at\": \"2024-01-15T12:30:00.000Z\"\n }\n ],\n \"total\": 1\n}\n"},{"location":"api/history/#get-apihistoryid","title":"GET /api/history/:id","text":"Gibt Detail-Informationen f\u00fcr einen einzelnen Job zur\u00fcck.
URL-Parameter: id \u2013 Job-ID
Query-Parameter:
Parameter Typ Standard BeschreibungincludeLogs boolean false Log-Inhalte einschlie\u00dfen includeLiveLog boolean false Aktuellen Live-Log einschlie\u00dfen Response:
{\n \"id\": 42,\n \"status\": \"FINISHED\",\n \"title\": \"Inception\",\n \"imdb_id\": \"tt1375666\",\n \"encode_plan\": { ... },\n \"makemkv_output\": { ... },\n \"mediainfo_output\": { ... },\n \"handbrake_log\": \"/path/to/log\",\n \"logs\": {\n \"handbrake\": \"Encoding: task 1 of 1, 100.0%\\n...\"\n },\n \"created_at\": \"2024-01-15T10:00:00.000Z\",\n \"updated_at\": \"2024-01-15T12:30:00.000Z\"\n}\n"},{"location":"api/history/#get-apihistorydatabase","title":"GET /api/history/database","text":"Gibt alle rohen Datenbankzeilen zur\u00fcck (Debug-Ansicht).
Response:
{\n \"jobs\": [ { \"id\": 1, \"status\": \"FINISHED\", ... } ],\n \"total\": 15\n}\n"},{"location":"api/history/#get-apihistoryorphan-raw","title":"GET /api/history/orphan-raw","text":"Findet Raw-Ordner, die nicht als Jobs in der Datenbank registriert sind.
Response:
{\n \"orphans\": [\n {\n \"path\": \"/mnt/nas/raw/UnknownMovie_2023-12-01\",\n \"size\": \"45.2 GB\",\n \"modifiedAt\": \"2023-12-01T15:00:00.000Z\",\n \"files\": [\"t00.mkv\", \"t01.mkv\"]\n }\n ]\n}\n"},{"location":"api/history/#post-apihistoryorphan-rawimport","title":"POST /api/history/orphan-raw/import","text":"Importiert einen Orphan-Raw-Ordner als Job in die Datenbank.
Request:
{\n \"path\": \"/mnt/nas/raw/UnknownMovie_2023-12-01\"\n}\n Response:
{\n \"ok\": true,\n \"jobId\": 99,\n \"message\": \"Orphan-Ordner als Job importiert\"\n}\n Nach dem Import kann dem Job \u00fcber /api/history/:id/omdb/assign Metadaten zugewiesen werden.
Weist einem bestehenden Job OMDb-Metadaten nachtr\u00e4glich zu.
URL-Parameter: id \u2013 Job-ID
Request:
{\n \"imdbId\": \"tt1375666\",\n \"title\": \"Inception\",\n \"year\": \"2010\",\n \"type\": \"movie\",\n \"poster\": \"https://...\"\n}\n Response:
{ \"ok\": true }\n"},{"location":"api/history/#post-apihistoryiddelete-files","title":"POST /api/history/:id/delete-files","text":"L\u00f6scht die Dateien eines Jobs (Raw und/oder Output), beh\u00e4lt den Job-Eintrag.
URL-Parameter: id \u2013 Job-ID
Request:
{\n \"deleteRaw\": true,\n \"deleteOutput\": false\n}\n Response:
{\n \"ok\": true,\n \"deleted\": {\n \"raw\": \"/mnt/nas/raw/Inception_t00.mkv\",\n \"output\": null\n }\n}\n"},{"location":"api/history/#post-apihistoryiddelete","title":"POST /api/history/:id/delete","text":"L\u00f6scht den Job-Eintrag aus der Datenbank, optional auch die Dateien.
URL-Parameter: id \u2013 Job-ID
Request:
{\n \"deleteFiles\": true\n}\n Response:
{ \"ok\": true, \"message\": \"Job gel\u00f6scht\" }\n Unwiderruflich
Das L\u00f6schen von Jobs und Dateien ist nicht r\u00fcckg\u00e4ngig zu machen.
"},{"location":"api/pipeline/","title":"Pipeline API","text":"Alle Endpunkte zur Steuerung des Ripping-Workflows.
"},{"location":"api/pipeline/#get-apipipelinestate","title":"GET /api/pipeline/state","text":"Gibt den aktuellen Pipeline-Zustand zur\u00fcck.
Response:
{\n \"state\": \"ENCODING\",\n \"jobId\": 42,\n \"job\": {\n \"id\": 42,\n \"title\": \"Inception\",\n \"status\": \"ENCODING\",\n \"imdb_id\": \"tt1375666\",\n \"omdb_year\": \"2010\"\n },\n \"progress\": 73.5,\n \"eta\": \"00:12:34\",\n \"updatedAt\": \"2024-01-15T14:30:00.000Z\"\n}\n Pipeline-Zust\u00e4nde:
Wert BeschreibungIDLE Wartet auf Disc DISC_DETECTED Disc erkannt, wartet auf Benutzer METADATA_SELECTION Disc-Scan l\u00e4uft / Metadaten-Dialog WAITING_FOR_USER_DECISION Mehrere Playlist-Kandidaten \u2013 manuelle Auswahl READY_TO_START Bereit zum Starten RIPPING MakeMKV-Ripping l\u00e4uft MEDIAINFO_CHECK HandBrake-Scan & Encode-Plan-Erstellung READY_TO_ENCODE Wartet auf Encode-Best\u00e4tigung ENCODING HandBrake encodiert POST_ENCODE_SCRIPTS Post-Encode-Skripte laufen FINISHED Abgeschlossen ERROR Fehler Kontext-Felder (state-abh\u00e4ngig):
Beim Zustand WAITING_FOR_USER_DECISION enth\u00e4lt die Response zus\u00e4tzlich:
{\n \"state\": \"WAITING_FOR_USER_DECISION\",\n \"context\": {\n \"playlistAnalysis\": {\n \"evaluatedCandidates\": [...],\n \"recommendation\": { \"playlistId\": \"00800\", \"score\": 18 },\n \"manualDecisionRequired\": true,\n \"manualDecisionReason\": \"multiple_candidates_after_min_length\"\n },\n \"playlistCandidates\": [\"00800\", \"00801\", \"00900\"]\n }\n}\n"},{"location":"api/pipeline/#post-apipipelineanalyze","title":"POST /api/pipeline/analyze","text":"Startet eine manuelle Disc-Analyse.
Request: Kein Body
Response:
{ \"ok\": true, \"message\": \"Analyse gestartet\" }\n Fehlerf\u00e4lle: - 409 \u2013 Pipeline bereits aktiv
Erzwingt eine erneute Disc-Erkennung.
Response: { \"ok\": true }
Sucht in der OMDb-API nach einem Filmtitel.
Query-Parameter:
Parameter Typ Beschreibungq string Suchbegriff type string movie oder series (optional) Beispiel: GET /api/pipeline/omdb/search?q=Inception&type=movie
Response:
{\n \"results\": [\n { \"imdbId\": \"tt1375666\", \"title\": \"Inception\", \"year\": \"2010\", \"type\": \"movie\", \"poster\": \"https://...\" }\n ]\n}\n"},{"location":"api/pipeline/#post-apipipelineselect-metadata","title":"POST /api/pipeline/select-metadata","text":"Best\u00e4tigt Metadaten und optionale Playlist-Auswahl.
Request:
{\n \"jobId\": 42,\n \"omdb\": {\n \"imdbId\": \"tt1375666\",\n \"title\": \"Inception\",\n \"year\": \"2010\",\n \"type\": \"movie\",\n \"poster\": \"https://...\"\n },\n \"selectedPlaylist\": \"00800\"\n}\n Playlist-Felder
selectedPlaylist ist optional. Wird es beim ersten Aufruf weggelassen (kein Obfuskierungsverdacht), wird die Empfehlung automatisch \u00fcbernommen.
Beim zweiten Aufruf aus dem WAITING_FOR_USER_DECISION-Dialog reicht es, nur jobId + selectedPlaylist zu schicken \u2013 omdb kann dann weggelassen werden.
Response: { \"ok\": true }
Startet den Ripping-Prozess.
URL-Parameter: jobId
Response: { \"ok\": true, \"message\": \"Ripping gestartet\" }
Sonderfall: Falls f\u00fcr den Job bereits eine Raw-Datei vorhanden ist, wird das Ripping \u00fcbersprungen und direkt der HandBrake-Scan gestartet.
Fehlerf\u00e4lle: - 404 \u2013 Job nicht gefunden - 409 \u2013 Job nicht im Status READY_TO_START
Best\u00e4tigt die Encode-Konfiguration mit Track-Auswahl und Post-Encode-Skripten.
URL-Parameter: jobId
Request:
{\n \"selectedEncodeTitleId\": 1,\n \"selectedTrackSelection\": {\n \"1\": {\n \"audioTrackIds\": [1, 2],\n \"subtitleTrackIds\": [1]\n }\n },\n \"selectedPostEncodeScriptIds\": [\"script-abc123\", \"script-def456\"]\n}\n Feld Typ Beschreibung selectedEncodeTitleId number HandBrake-Titel-ID (aus dem Encode-Plan) selectedTrackSelection object Pro Titel: Audio- und Untertitel-Track-IDs selectedPostEncodeScriptIds string[] Skript-IDs in Ausf\u00fchrungsreihenfolge (optional) Track-IDs
Die Track-IDs entsprechen den id-Feldern aus dem Encode-Plan (encode_plan_json), nicht den rohen HandBrake-Track-Nummern.
Response: { \"ok\": true, \"message\": \"Encoding gestartet\" }
Bricht den aktiven Pipeline-Prozess ab.
Response: { \"ok\": true, \"message\": \"Pipeline abgebrochen\" }
SIGINT \u2192 graceful exit (10 s Timeout) \u2192 SIGKILL.
"},{"location":"api/pipeline/#post-apipipelineretryjobid","title":"POST /api/pipeline/retry/:jobId","text":"Wiederholt einen fehlgeschlagenen Job.
Response: { \"ok\": true, \"message\": \"Job wird wiederholt\" }
Fehlerf\u00e4lle: - 404 \u2013 Job nicht gefunden - 409 \u2013 Job nicht im Status ERROR
Reaktiviert einen Job im Status READY_TO_ENCODE in die aktive Pipeline (z. B. nach Neustart).
Response: { \"ok\": true }
Encodiert eine abgeschlossene Raw-MKV erneut \u2013 ohne Ripping.
Request:
{\n \"selectedEncodeTitleId\": 1,\n \"selectedTrackSelection\": {\n \"1\": { \"audioTrackIds\": [1, 2], \"subtitleTrackIds\": [1] }\n },\n \"selectedPostEncodeScriptIds\": [\"script-abc123\"]\n}\n Gleiche Struktur wie confirm-encode \u2013 erm\u00f6glicht andere Track-Auswahl und Skripte als beim ersten Encoding.
Response: { \"ok\": true, \"message\": \"Re-Encoding gestartet\" }
Endpunkte zum Lesen und Schreiben der Anwendungseinstellungen.
"},{"location":"api/settings/#get-apisettings","title":"GET /api/settings","text":"Gibt alle Einstellungen kategorisiert zur\u00fcck.
Response:
{\n \"paths\": {\n \"raw_dir\": {\n \"value\": \"/mnt/nas/raw\",\n \"schema\": {\n \"type\": \"string\",\n \"label\": \"Raw-Verzeichnis\",\n \"description\": \"Speicherort f\u00fcr rohe MKV-Dateien\",\n \"required\": true\n }\n },\n \"movie_dir\": {\n \"value\": \"/mnt/nas/movies\",\n \"schema\": { ... }\n }\n },\n \"tools\": { ... },\n \"encoding\": { ... },\n \"drive\": { ... },\n \"makemkv\": { ... },\n \"omdb\": { ... },\n \"notifications\": { ... }\n}\n"},{"location":"api/settings/#put-apisettingskey","title":"PUT /api/settings/:key","text":"Aktualisiert eine einzelne Einstellung.
URL-Parameter: key \u2013 Einstellungs-Schl\u00fcssel
Request:
{\n \"value\": \"/mnt/storage/raw\"\n}\n Response:
{ \"ok\": true, \"key\": \"raw_dir\", \"value\": \"/mnt/storage/raw\" }\n Fehlerf\u00e4lle: - 400 \u2013 Ung\u00fcltiger Wert (Validierungsfehler) - 404 \u2013 Einstellung nicht gefunden
Encode-Review-Refresh
Wenn eine encoding-relevante Einstellung ge\u00e4ndert wird (z.B. handbrake_preset), wird der Encode-Plan f\u00fcr den aktuell wartenden Job automatisch neu berechnet.
Aktualisiert mehrere Einstellungen auf einmal.
Request:
{\n \"raw_dir\": \"/mnt/storage/raw\",\n \"movie_dir\": \"/mnt/storage/movies\",\n \"handbrake_preset\": \"H.265 MKV 720p30\"\n}\n Response:
{\n \"ok\": true,\n \"updated\": [\"raw_dir\", \"movie_dir\", \"handbrake_preset\"],\n \"errors\": []\n}\n"},{"location":"api/settings/#post-apisettingspushovertest","title":"POST /api/settings/pushover/test","text":"Sendet eine Test-Benachrichtigung \u00fcber PushOver.
Request: Kein Body erforderlich (verwendet gespeicherte Zugangsdaten)
Response (Erfolg):
{ \"ok\": true, \"message\": \"Test-Benachrichtigung gesendet\" }\n Response (Fehler):
{ \"ok\": false, \"error\": \"Ung\u00fcltiger API-Token\" }\n"},{"location":"api/settings/#skript-verwaltung","title":"Skript-Verwaltung","text":"Post-Encode-Skripte werden \u00fcber eigene Endpunkte unter /api/settings/scripts verwaltet.
Gibt alle konfigurierten Skripte zur\u00fcck.
Response:
{\n \"scripts\": [\n {\n \"id\": \"script-abc123\",\n \"name\": \"Zu Plex verschieben\",\n \"command\": \"/home/michael/scripts/move-to-plex.sh\",\n \"description\": \"Verschiebt die fertige Datei ins Plex-Verzeichnis\",\n \"createdAt\": \"2024-01-15T10:00:00.000Z\"\n }\n ]\n}\n"},{"location":"api/settings/#post-apisettingsscripts","title":"POST /api/settings/scripts","text":"Legt ein neues Post-Encode-Skript an.
Request:
{\n \"name\": \"Zu Plex verschieben\",\n \"command\": \"/home/michael/scripts/move-to-plex.sh\",\n \"description\": \"Verschiebt die fertige Datei ins Plex-Verzeichnis\"\n}\n Feld Typ Pflicht Beschreibung name string \u2705 Anzeigename command string \u2705 Shell-Befehl oder absoluter Skriptpfad description string \u2014 Optionale Beschreibung Response:
{\n \"ok\": true,\n \"script\": {\n \"id\": \"script-abc123\",\n \"name\": \"Zu Plex verschieben\",\n \"command\": \"/home/michael/scripts/move-to-plex.sh\"\n }\n}\n"},{"location":"api/settings/#put-apisettingsscriptsscriptid","title":"PUT /api/settings/scripts/:scriptId","text":"Aktualisiert ein vorhandenes Skript.
URL-Parameter: scriptId
Request: Gleiche Felder wie beim Anlegen (alle optional).
{ \"name\": \"Zu Jellyfin verschieben\", \"command\": \"/home/michael/scripts/move-to-jellyfin.sh\" }\n Response: { \"ok\": true }
L\u00f6scht ein Skript.
URL-Parameter: scriptId
Response: { \"ok\": true }
Referenzen in Jobs
Wenn das Skript in laufenden oder abgeschlossenen Jobs referenziert wird, wird es trotzdem gel\u00f6scht. In zuk\u00fcnftigen Encode-Reviews erscheint es nicht mehr.
"},{"location":"api/settings/#post-apisettingsscriptsscriptidtest","title":"POST /api/settings/scripts/:scriptId/test","text":"F\u00fchrt ein Skript mit Platzhalter-Umgebungsvariablen aus (Testlauf).
URL-Parameter: scriptId
Response (Erfolg):
{\n \"ok\": true,\n \"exitCode\": 0,\n \"stdout\": \"Testausgabe des Skripts\",\n \"stderr\": \"\",\n \"durationMs\": 245\n}\n Response (Fehler):
{\n \"ok\": false,\n \"exitCode\": 1,\n \"stdout\": \"\",\n \"stderr\": \"Datei nicht gefunden: /home/michael/scripts/move-to-plex.sh\",\n \"durationMs\": 12\n}\n Platzhalter-Werte beim Testlauf:
Variable TestwertRIPSTER_OUTPUT_PATH /tmp/ripster-test-output.mkv RIPSTER_JOB_ID 0 RIPSTER_TITLE Test Film RIPSTER_YEAR 2024 RIPSTER_IMDB_ID tt0000000 RIPSTER_RAW_PATH /tmp/ripster-test-raw.mkv"},{"location":"api/settings/#einstellungs-schlussel-referenz","title":"Einstellungs-Schl\u00fcssel Referenz","text":"Eine vollst\u00e4ndige Liste aller Einstellungs-Schl\u00fcssel:
Schl\u00fcssel Kategorie Typ Beschreibungraw_dir paths string Raw-MKV Verzeichnis movie_dir paths string Ausgabe-Verzeichnis log_dir paths string Log-Verzeichnis makemkv_command tools string MakeMKV-Befehl handbrake_command tools string HandBrake-Befehl mediainfo_command tools string MediaInfo-Befehl handbrake_preset encoding string HandBrake-Preset-Name handbrake_extra_args encoding string Zusatz-Argumente output_extension encoding string Dateiendung (z.B. mkv) filename_template encoding string Dateiname-Template drive_mode drive select auto oder explicit drive_device drive string Ger\u00e4te-Pfad disc_poll_interval_ms drive number Polling-Intervall (ms) makemkv_min_length_minutes makemkv number Min. Titell\u00e4nge (Minuten) makemkv_backup_mode makemkv boolean Backup-Modus aktivieren omdb_api_key omdb string OMDb API-Key omdb_default_type omdb select Standard-Suchtyp pushover_user_key notifications string PushOver User-Key pushover_api_token notifications string PushOver API-Token"},{"location":"api/websocket/","title":"WebSocket Events","text":"Ripster verwendet WebSockets f\u00fcr Echtzeit-Updates. Der Endpunkt ist /ws.
const ws = new WebSocket('ws://localhost:3001/ws');\n\nws.onmessage = (event) => {\n const message = JSON.parse(event.data);\n console.log(message.type, message.data);\n};\n"},{"location":"api/websocket/#nachrichten-format","title":"Nachrichten-Format","text":"Alle Nachrichten folgen diesem Schema:
{\n \"type\": \"EVENT_TYPE\",\n \"data\": { ... }\n}\n"},{"location":"api/websocket/#event-typen","title":"Event-Typen","text":""},{"location":"api/websocket/#pipeline_state_change","title":"PIPELINE_STATE_CHANGE","text":"Wird gesendet, wenn der Pipeline-Zustand wechselt.
{\n \"type\": \"PIPELINE_STATE_CHANGE\",\n \"data\": {\n \"state\": \"ENCODING\",\n \"jobId\": 42,\n \"job\": {\n \"id\": 42,\n \"title\": \"Inception\",\n \"status\": \"ENCODING\"\n }\n }\n}\n"},{"location":"api/websocket/#progress_update","title":"PROGRESS_UPDATE","text":"Wird w\u00e4hrend aktiver Prozesse (Ripping/Encoding) regelm\u00e4\u00dfig gesendet.
{\n \"type\": \"PROGRESS_UPDATE\",\n \"data\": {\n \"progress\": 73.5,\n \"eta\": \"00:12:34\",\n \"speed\": \"45.2 fps\",\n \"phase\": \"ENCODING\"\n }\n}\n Felder:
Feld Typ Beschreibungprogress number Fortschritt 0\u2013100 eta string Gesch\u00e4tzte Restzeit (HH:MM:SS) speed string Encoding-Geschwindigkeit (nur beim Encoding) phase string Aktuelle Phase (RIPPING oder ENCODING)"},{"location":"api/websocket/#disc_detected","title":"DISC_DETECTED","text":"Wird gesendet, wenn eine Disc erkannt wird.
{\n \"type\": \"DISC_DETECTED\",\n \"data\": {\n \"device\": \"/dev/sr0\"\n }\n}\n"},{"location":"api/websocket/#disc_removed","title":"DISC_REMOVED","text":"Wird gesendet, wenn eine Disc ausgeworfen wird.
{\n \"type\": \"DISC_REMOVED\",\n \"data\": {\n \"device\": \"/dev/sr0\"\n }\n}\n"},{"location":"api/websocket/#job_complete","title":"JOB_COMPLETE","text":"Wird gesendet, wenn ein Job erfolgreich abgeschlossen wurde.
{\n \"type\": \"JOB_COMPLETE\",\n \"data\": {\n \"jobId\": 42,\n \"title\": \"Inception\",\n \"outputPath\": \"/mnt/nas/movies/Inception (2010).mkv\"\n }\n}\n"},{"location":"api/websocket/#error","title":"ERROR","text":"Wird gesendet, wenn ein Fehler aufgetreten ist.
{\n \"type\": \"ERROR\",\n \"data\": {\n \"jobId\": 42,\n \"message\": \"HandBrake ist abgest\u00fcrzt\",\n \"details\": \"Exit code: 1\\nStderr: ...\"\n }\n}\n"},{"location":"api/websocket/#metadata_required","title":"METADATA_REQUIRED","text":"Wird gesendet, wenn Benutzer-Eingabe f\u00fcr Metadaten ben\u00f6tigt wird.
{\n \"type\": \"METADATA_REQUIRED\",\n \"data\": {\n \"jobId\": 42,\n \"makemkvData\": { ... },\n \"playlistAnalysis\": { ... }\n }\n}\n"},{"location":"api/websocket/#encode_review_required","title":"ENCODE_REVIEW_REQUIRED","text":"Wird gesendet, wenn der Benutzer den Encode-Plan best\u00e4tigen soll.
{\n \"type\": \"ENCODE_REVIEW_REQUIRED\",\n \"data\": {\n \"jobId\": 42,\n \"encodePlan\": {\n \"audioTracks\": [ ... ],\n \"subtitleTracks\": [ ... ]\n }\n }\n}\n"},{"location":"api/websocket/#reconnect-verhalten","title":"Reconnect-Verhalten","text":"Der Frontend-Hook useWebSocket.js implementiert automatisches Reconnect:
Verbindung verloren\n \u2193\nWarte 1s \u2192 Reconnect-Versuch\n \u2193 (Fehlschlag)\nWarte 2s \u2192 Reconnect-Versuch\n \u2193 (Fehlschlag)\nWarte 4s \u2192 ...\n \u2193\nMax. 30s Wartezeit\n"},{"location":"api/websocket/#beispiel-react-hook","title":"Beispiel: React-Hook","text":"import { useEffect, useState } from 'react';\n\nfunction usePipelineState() {\n const [state, setState] = useState({ state: 'IDLE' });\n\n useEffect(() => {\n const ws = new WebSocket(import.meta.env.VITE_WS_URL + '/ws');\n\n ws.onmessage = (event) => {\n const msg = JSON.parse(event.data);\n\n if (msg.type === 'PIPELINE_STATE_CHANGE') {\n setState(msg.data);\n }\n };\n\n return () => ws.close();\n }, []);\n\n return state;\n}\n"},{"location":"architecture/","title":"Architektur","text":"Ripster ist als klassische Client-Server-Anwendung mit Echtzeit-Kommunikation \u00fcber WebSockets aufgebaut.
"},{"location":"architecture/#systemuberblick","title":"System\u00fcberblick","text":"graph TB\n subgraph Browser[\"Browser (React)\"]\n Dashboard[\"Dashboard\"]\n Settings[\"Einstellungen\"]\n History[\"History\"]\n end\n\n subgraph Backend[\"Node.js Backend\"]\n API[\"REST API\\n(Express)\"]\n WS[\"WebSocket\\nServer\"]\n Pipeline[\"Pipeline\\nService\"]\n DB[\"SQLite\\nDatenbank\"]\n end\n\n subgraph ExternalTools[\"Externe Tools\"]\n MakeMKV[\"makemkvcon\"]\n HandBrake[\"HandBrakeCLI\"]\n MediaInfo[\"mediainfo\"]\n end\n\n subgraph ExternalAPIs[\"Externe APIs\"]\n OMDb[\"OMDb API\"]\n PushOver[\"PushOver\"]\n end\n\n Browser <-->|HTTP REST| API\n Browser <-->|WebSocket| WS\n Pipeline --> MakeMKV\n Pipeline --> HandBrake\n Pipeline --> MediaInfo\n Pipeline <-->|Metadaten| OMDb\n Pipeline -->|Benachrichtigungen| PushOver\n API --> DB\n Pipeline --> DB"},{"location":"architecture/#schichten-architektur","title":"Schichten-Architektur","text":""},{"location":"architecture/#backend","title":"Backend","text":"index.js (Express Server)\n\u251c\u2500\u2500 Routes (API-Endpunkte)\n\u2502 \u251c\u2500\u2500 pipelineRoutes.js\n\u2502 \u251c\u2500\u2500 settingsRoutes.js\n\u2502 \u2514\u2500\u2500 historyRoutes.js\n\u251c\u2500\u2500 Services (Business Logic)\n\u2502 \u251c\u2500\u2500 pipelineService.js \u2190 Kern-Orchestrierung\n\u2502 \u251c\u2500\u2500 diskDetectionService.js\n\u2502 \u251c\u2500\u2500 processRunner.js\n\u2502 \u251c\u2500\u2500 websocketService.js\n\u2502 \u251c\u2500\u2500 omdbService.js\n\u2502 \u251c\u2500\u2500 settingsService.js\n\u2502 \u251c\u2500\u2500 notificationService.js\n\u2502 \u251c\u2500\u2500 historyService.js\n\u2502 \u2514\u2500\u2500 logger.js\n\u251c\u2500\u2500 Database\n\u2502 \u251c\u2500\u2500 database.js\n\u2502 \u2514\u2500\u2500 defaultSettings.js\n\u2514\u2500\u2500 Utils\n \u251c\u2500\u2500 encodePlan.js\n \u251c\u2500\u2500 playlistAnalysis.js\n \u251c\u2500\u2500 progressParsers.js\n \u2514\u2500\u2500 files.js\n"},{"location":"architecture/#frontend","title":"Frontend","text":"App.jsx (React Router)\n\u251c\u2500\u2500 Pages\n\u2502 \u251c\u2500\u2500 DashboardPage.jsx \u2190 Haupt-Interface\n\u2502 \u251c\u2500\u2500 SettingsPage.jsx\n\u2502 \u2514\u2500\u2500 HistoryPage.jsx\n\u251c\u2500\u2500 Components\n\u2502 \u251c\u2500\u2500 PipelineStatusCard.jsx\n\u2502 \u251c\u2500\u2500 MetadataSelectionDialog.jsx\n\u2502 \u251c\u2500\u2500 MediaInfoReviewPanel.jsx\n\u2502 \u251c\u2500\u2500 DynamicSettingsForm.jsx\n\u2502 \u2514\u2500\u2500 JobDetailDialog.jsx\n\u251c\u2500\u2500 Hooks\n\u2502 \u2514\u2500\u2500 useWebSocket.js\n\u2514\u2500\u2500 API\n \u2514\u2500\u2500 client.js\n"},{"location":"architecture/#weiterfuhrende-dokumentation","title":"Weiterf\u00fchrende Dokumentation","text":"\u00dcbersicht
Backend-Services
Frontend-Komponenten
Datenbank
Das Backend ist in Node.js/Express geschrieben und in Services aufgeteilt, die jeweils eine klar abgegrenzte Verantwortlichkeit haben.
"},{"location":"architecture/backend/#pipelineservicejs","title":"pipelineService.js","text":"Der Kern von Ripster \u2013 orchestriert den gesamten Ripping-Workflow.
"},{"location":"architecture/backend/#zustandigkeiten","title":"Zust\u00e4ndigkeiten","text":"analyzeDisc() Startet MakeMKV-Analyse der eingelegten Disc selectMetadata(jobId, omdbData, playlist) Setzt Metadaten und Playlist f\u00fcr einen Job startJob(jobId) Startet den Ripping-Prozess confirmEncode(jobId, trackSelection) Best\u00e4tigt Encode mit Track-Auswahl cancelPipeline() Bricht aktiven Prozess ab retryJob(jobId) Wiederholt fehlgeschlagenen Job reencodeJob(jobId) Encodiert bestehende Raw-MKV neu"},{"location":"architecture/backend/#zustandsubergange","title":"Zustands\u00fcberg\u00e4nge","text":"flowchart LR\n START(( )) --> IDLE\n IDLE -->|analyzeDisc()| ANALYZING[ANALYZING]\n ANALYZING -->|MakeMKV fertig| META[METADATA\\nSELECTION]\n META -->|selectMetadata()| RTS[READY_TO\\nSTART]\n RTS -->|startJob()| RIP[RIPPING]\n RIP -->|MKV erstellt| MIC[MEDIAINFO\\nCHECK]\n MIC -->|Tracks analysiert| RTE[READY_TO\\nENCODE]\n RTE -->|confirmEncode()| ENC[ENCODING]\n ENC -->|HandBrake fertig| FIN([FINISHED])\n ENC -->|Fehler| ERR([ERROR])\n RIP -->|Fehler| ERR\n ERR -->|retryJob() / cancel| IDLE\n FIN -->|cancel / neue Disc| IDLE\n\n style FIN fill:#e8f5e9,stroke:#66bb6a,color:#2e7d32\n style ERR fill:#ffebee,stroke:#ef5350,color:#c62828\n style ENC fill:#f3e5f5,stroke:#ab47bc,color:#6a1b9a\n style RIP fill:#e3f2fd,stroke:#42a5f5,color:#1565c0\n style MIC fill:#e3f2fd,stroke:#42a5f5,color:#1565c0"},{"location":"architecture/backend/#diskdetectionservicejs","title":"diskDetectionService.js","text":"\u00dcberwacht das Disc-Laufwerk auf Disc-Einleger- und Auswurf-Ereignisse.
"},{"location":"architecture/backend/#modi","title":"Modi","text":"Modus Beschreibungauto Erkennt verf\u00fcgbare Laufwerke automatisch explicit \u00dcberwacht ein bestimmtes Ger\u00e4t (z.B. /dev/sr0)"},{"location":"architecture/backend/#polling","title":"Polling","text":"Der Service pollt das Laufwerk im konfigurierten Intervall (disc_poll_interval_ms, Standard: 5000ms) und emittiert Events:
// Ereignisse\nemit('disc-detected', { device: '/dev/sr0' })\nemit('disc-removed', { device: '/dev/sr0' })\n"},{"location":"architecture/backend/#processrunnerjs","title":"processRunner.js","text":"Verwaltet externe CLI-Prozesse.
"},{"location":"architecture/backend/#features","title":"Features","text":"const result = await runProcess(\n 'HandBrakeCLI',\n ['--input', rawFile, '--output', outputFile, '--preset', preset],\n {\n onStderr: (line) => parseHandBrakeProgress(line),\n onStdout: (line) => logger.debug(line)\n }\n);\n"},{"location":"architecture/backend/#websocketservicejs","title":"websocketService.js","text":"WebSocket-Server f\u00fcr Echtzeit-Client-Kommunikation.
"},{"location":"architecture/backend/#betrieb","title":"Betrieb","text":"/ws des Express-Serversbroadcast({ type: 'PIPELINE_STATE_CHANGE', data: { state, jobId } });\nbroadcast({ type: 'PROGRESS_UPDATE', data: { progress, eta } });\n"},{"location":"architecture/backend/#omdbservicejs","title":"omdbService.js","text":"Integration mit der OMDb API.
"},{"location":"architecture/backend/#methoden","title":"Methoden","text":"Methode BeschreibungsearchByTitle(title, type) Suche nach Titel (movie/series) fetchById(imdbId) Vollst\u00e4ndige Metadaten per IMDb-ID"},{"location":"architecture/backend/#zuruckgegebene-daten","title":"Zur\u00fcckgegebene Daten","text":"{\n \"imdbId\": \"tt1375666\",\n \"title\": \"Inception\",\n \"year\": \"2010\",\n \"type\": \"movie\",\n \"poster\": \"https://...\",\n \"plot\": \"...\",\n \"director\": \"Christopher Nolan\"\n}\n"},{"location":"architecture/backend/#settingsservicejs","title":"settingsService.js","text":"Verwaltet alle Anwendungseinstellungen.
"},{"location":"architecture/backend/#features_1","title":"Features","text":"defaultSettings.js definiert Standardwertepaths raw_dir, movie_dir, log_dir tools makemkv_command, handbrake_command, mediainfo_command encoding handbrake_preset, handbrake_extra_args, output_extension, filename_template drive drive_mode, drive_device, disc_poll_interval_ms makemkv makemkv_min_length_minutes, makemkv_backup_mode omdb omdb_api_key, omdb_default_type notifications pushover_user_key, pushover_api_token"},{"location":"architecture/backend/#historyservicejs","title":"historyService.js","text":"Datenbankoperationen f\u00fcr Job-Historie.
"},{"location":"architecture/backend/#hauptoperationen","title":"Hauptoperationen","text":"Operation BeschreibunglistJobs(filters) Jobs nach Status/Titel filtern getJob(id) Job-Details mit Logs abrufen findOrphanRawFolders() Nicht-getrackte Raw-Ordner finden importOrphanRaw(path) Orphan-Ordner als Job importieren assignOmdb(id, omdbData) OMDb-Metadaten nachtr\u00e4glich zuweisen deleteJob(id, deleteFiles) Job und optional Dateien l\u00f6schen"},{"location":"architecture/backend/#notificationservicejs","title":"notificationService.js","text":"PushOver-Push-Benachrichtigungen.
await notify({\n title: 'Ripster: Job abgeschlossen',\n message: 'Inception (2010) wurde erfolgreich encodiert'\n});\n"},{"location":"architecture/backend/#loggerjs","title":"logger.js","text":"Strukturiertes Logging mit t\u00e4glicher Log-Rotation.
"},{"location":"architecture/backend/#log-level","title":"Log-Level","text":"Level Verwendungdebug Detaillierte Entwicklungs-Informationen info Normale Betriebsereignisse warn Warnungen, die Aufmerksamkeit ben\u00f6tigen error Fehler, die den Betrieb beeintr\u00e4chtigen"},{"location":"architecture/backend/#log-dateien","title":"Log-Dateien","text":"logs/\n\u251c\u2500\u2500 ripster-2024-01-15.log \u2190 Tages-Log\n\u2514\u2500\u2500 jobs/\n \u2514\u2500\u2500 job-42-handbrake.log \u2190 Prozess-spezifische Logs\n"},{"location":"architecture/database/","title":"Datenbank","text":"Ripster verwendet SQLite3 als Datenbank. Die Datenbankdatei liegt unter backend/data/ripster.db.
-- Vier Haupt-Tabellen\nsettings_schema -- Einstellungs-Definitionen\nsettings_values -- Benutzer-Werte\njobs -- Rip-Job-Datens\u00e4tze\npipeline_state -- Aktueller Pipeline-Zustand (Singleton)\n"},{"location":"architecture/database/#tabelle-jobs","title":"Tabelle: jobs","text":"Die wichtigste Tabelle \u2013 speichert alle Ripping-Jobs.
CREATE TABLE jobs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n status TEXT NOT NULL, -- Aktueller Status\n title TEXT, -- Filmtitel (von OMDb)\n imdb_id TEXT, -- IMDb-ID\n omdb_year TEXT, -- Erscheinungsjahr\n omdb_type TEXT, -- movie/series\n omdb_poster TEXT, -- Poster-URL\n raw_path TEXT, -- Pfad zur Raw-MKV\n output_path TEXT, -- Pfad zur Ausgabedatei\n playlist TEXT, -- Gew\u00e4hlte Blu-ray Playlist\n makemkv_output TEXT, -- MakeMKV-Ausgabe (JSON)\n mediainfo_output TEXT, -- MediaInfo-Ausgabe (JSON)\n encode_plan TEXT, -- Encode-Plan (JSON)\n handbrake_log TEXT, -- HandBrake Log-Pfad\n error_message TEXT, -- Fehlermeldung bei ERROR\n error_details TEXT -- Detaillierte Fehler-Infos\n);\n"},{"location":"architecture/database/#job-status-werte","title":"Job-Status-Werte","text":"Status Beschreibung ANALYZING MakeMKV analysiert die Disc METADATA_SELECTION Wartet auf Benutzer-Metadaten-Auswahl READY_TO_START Bereit zum Starten RIPPING MakeMKV rippt die Disc MEDIAINFO_CHECK MediaInfo analysiert die Raw-Datei READY_TO_ENCODE Wartet auf Encode-Best\u00e4tigung ENCODING HandBrake encodiert FINISHED Erfolgreich abgeschlossen ERROR Fehler aufgetreten"},{"location":"architecture/database/#tabelle-pipeline_state","title":"Tabelle: pipeline_state","text":"Singleton-Tabelle f\u00fcr den aktuellen Pipeline-Zustand (immer genau 1 Zeile).
CREATE TABLE pipeline_state (\n id INTEGER PRIMARY KEY CHECK(id = 1),\n state TEXT NOT NULL DEFAULT 'IDLE',\n job_id INTEGER, -- Aktiver Job (NULL wenn IDLE)\n progress REAL, -- Fortschritt 0-100\n eta TEXT, -- Gesch\u00e4tzte Restzeit\n updated_at TEXT NOT NULL\n);\n"},{"location":"architecture/database/#tabelle-settings_schema","title":"Tabelle: settings_schema","text":"Definiert alle verf\u00fcgbaren Einstellungen mit Metadaten.
CREATE TABLE settings_schema (\n key TEXT PRIMARY KEY,\n category TEXT NOT NULL, -- paths, tools, encoding, ...\n type TEXT NOT NULL, -- string, number, boolean, select\n label TEXT NOT NULL, -- Anzeigename\n description TEXT, -- Hilfetext\n default_val TEXT, -- Standardwert\n required INTEGER, -- 1 = Pflichtfeld\n min_val REAL, -- Minimalwert (f\u00fcr number)\n max_val REAL, -- Maximalwert (f\u00fcr number)\n options TEXT -- JSON-Array f\u00fcr select-Typ\n);\n"},{"location":"architecture/database/#tabelle-settings_values","title":"Tabelle: settings_values","text":"Speichert benutzer-konfigurierte Werte.
CREATE TABLE settings_values (\n key TEXT PRIMARY KEY REFERENCES settings_schema(key),\n value TEXT NOT NULL,\n updated_at TEXT NOT NULL\n);\n"},{"location":"architecture/database/#schema-migrationen","title":"Schema-Migrationen","text":"database.js implementiert automatische Migrationen:
Falls die Datenbankdatei korrupt ist:
1. Korrupte Datei wird erkannt (Verbindungsfehler / Integrit\u00e4tspr\u00fcfung)\n2. Datei wird in /backend/data/quarantine/ verschoben\n3. Neue, leere Datenbank wird erstellt\n4. Schema wird neu initialisiert\n5. Log-Eintrag mit Warnung\n"},{"location":"architecture/database/#datenbankpfad-konfigurieren","title":"Datenbankpfad konfigurieren","text":"Standard: ./data/ripster.db (relativ zum Backend-Verzeichnis)
\u00dcber Umgebungsvariable anpassen:
DB_PATH=/var/lib/ripster/ripster.db\n"},{"location":"architecture/database/#direkte-datenbankinspektion","title":"Direkte Datenbankinspektion","text":"# SQLite3-CLI\nsqlite3 backend/data/ripster.db\n\n# Alle Jobs anzeigen\n.mode table\nSELECT id, status, title, created_at FROM jobs ORDER BY created_at DESC;\n\n# Einstellungen anzeigen\nSELECT key, value FROM settings_values;\n"},{"location":"architecture/frontend/","title":"Frontend-Komponenten","text":"Das Frontend ist mit React 18 und PrimeReact gebaut und kommuniziert \u00fcber REST-API und WebSocket mit dem Backend.
"},{"location":"architecture/frontend/#seiten-pages","title":"Seiten (Pages)","text":""},{"location":"architecture/frontend/#dashboardpagejsx","title":"DashboardPage.jsx","text":"Die Hauptseite von Ripster \u2013 zeigt den aktuellen Pipeline-Status und erm\u00f6glicht alle Workflow-Aktionen.
Funktionen: - Anzeige des aktuellen Pipeline-Zustands (IDLE, ANALYZING, RIPPING, ENCODING, ...) - Live-Fortschrittsbalken mit ETA - Trigger f\u00fcr Metadaten-Dialog - Playlist-Entscheidungs-UI (bei Blu-ray Obfuskierung) - Encode-Review mit Track-Auswahl - Job-Steuerung (Start, Abbruch, Retry)
Zugeh\u00f6rige Komponenten: - PipelineStatusCard \u2013 Status-Widget - MetadataSelectionDialog \u2013 OMDb-Suche und Playlist-Auswahl - MediaInfoReviewPanel \u2013 Track-Auswahl vor dem Encoding - DiscDetectedDialog \u2013 Benachrichtigung bei Disc-Erkennung
Konfigurationsoberfl\u00e4che f\u00fcr alle Ripster-Einstellungen.
Funktionen: - Dynamisch generiertes Formular aus dem Settings-Schema - Echtzeit-Validierungsfeedback - PushOver-Verbindungstest - Automatische Aktualisierung des Encode-Reviews bei relevanten \u00c4nderungen
"},{"location":"architecture/frontend/#historypagejsx","title":"HistoryPage.jsx","text":"Job-Historie mit vollst\u00e4ndigem Audit-Trail.
Funktionen: - Sortier- und filterbares Job-Verzeichnis - Statusfilter (FINISHED, ERROR, WAITING_FOR_USER_DECISION, ...) - Job-Detail-Dialog mit vollst\u00e4ndigen Logs - Re-Encode, L\u00f6schen und Metadaten-Zuweisung - Import von Orphan-Raw-Ordnern
"},{"location":"architecture/frontend/#komponenten-components","title":"Komponenten (Components)","text":""},{"location":"architecture/frontend/#metadataselectiondialogjsx","title":"MetadataSelectionDialog.jsx","text":"Dialog f\u00fcr die Metadaten-Auswahl nach der Disc-Analyse.
\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Metadaten ausw\u00e4hlen \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Suche: [Inception ] \ud83d\udd0d \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Ergebnisse: \u2502\n\u2502 \u25b6 Inception (2010) \u2013 Movie \u2502\n\u2502 Inception: ... (2011) \u2013 Series \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Playlist (nur Blu-ray): \u2502\n\u2502 \u25b6 00800.mpls (2:30:15) \u2713 Empfohlen \u2502\n\u2502 00801.mpls (0:01:23) \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 [Best\u00e4tigen] \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n"},{"location":"architecture/frontend/#mediainforeviewpaneljsx","title":"MediaInfoReviewPanel.jsx","text":"Track-Auswahl-Panel vor dem Encoding.
\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Encode-Review \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Audio-Tracks: \u2502\n\u2502 \u2611 Track 1: Deutsch (AC-3, 5.1) \u2502\n\u2502 \u2611 Track 2: English (TrueHD, 7.1) \u2502\n\u2502 \u2610 Track 3: Fran\u00e7ais (AC-3, 2.0) \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Untertitel: \u2502\n\u2502 \u2611 Track 1: Deutsch \u2502\n\u2502 \u2610 Track 2: English \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 [Encodierung starten] \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n"},{"location":"architecture/frontend/#dynamicsettingsformjsx","title":"DynamicSettingsForm.jsx","text":"Wiederverwendbares Formular, das aus dem Settings-Schema generiert wird.
Unterst\u00fctzte Feldtypen:
Typ UI-Elementstring Text-Input number Zahlen-Input mit Min/Max boolean Toggle/Checkbox select Dropdown password Passwort-Input"},{"location":"architecture/frontend/#pipelinestatuscardjsx","title":"PipelineStatusCard.jsx","text":"Status-Anzeige-Widget f\u00fcr die Dashboard-Seite.
"},{"location":"architecture/frontend/#jobdetaildialogjsx","title":"JobDetailDialog.jsx","text":"Vollst\u00e4ndiger Job-Detail-Dialog mit Logs-Viewer.
"},{"location":"architecture/frontend/#hooks","title":"Hooks","text":""},{"location":"architecture/frontend/#usewebsocketjs","title":"useWebSocket.js","text":"Zentraler Custom-Hook f\u00fcr die WebSocket-Verbindung.
const { status, lastMessage } = useWebSocket({\n onMessage: (msg) => {\n if (msg.type === 'PIPELINE_STATE_CHANGE') {\n setPipelineState(msg.data);\n }\n }\n});\n Features: - Automatische Verbindung zu /ws - Reconnect mit exponential backoff - Message-Parsing (JSON) - Status-Tracking (connecting, connected, disconnected)
Zentraler HTTP-Client f\u00fcr alle Backend-Anfragen.
// Beispiel-Aufrufe\nconst state = await api.getPipelineState();\nconst results = await api.searchOmdb('Inception');\nawait api.selectMetadata(jobId, omdbData, playlist);\nawait api.confirmEncode(jobId, { audioTracks: [0, 1], subtitleTracks: [0] });\n Features: - Zentralisierte Fehlerbehandlung - Automatische JSON-Serialisierung - Basis-URL aus Umgebungsvariable (VITE_API_BASE)
cd frontend\nnpm run dev\n# \u2192 http://localhost:5173\n"},{"location":"architecture/frontend/#vite-proxy-konfiguration","title":"Vite-Proxy-Konfiguration","text":"In der Entwicklungsumgebung proxied Vite API-Anfragen zum Backend:
// vite.config.js\nproxy: {\n '/api': 'http://localhost:3001',\n '/ws': { target: 'ws://localhost:3001', ws: true }\n}\n"},{"location":"architecture/frontend/#production-build","title":"Production-Build","text":"cd frontend\nnpm run build\n# \u2192 frontend/dist/\n"},{"location":"architecture/overview/","title":"Architektur-\u00dcbersicht","text":""},{"location":"architecture/overview/#kern-designprinzipien","title":"Kern-Designprinzipien","text":""},{"location":"architecture/overview/#event-driven-pipeline","title":"Event-Driven Pipeline","text":"Der gesamte Ripping-Workflow ist als State Machine implementiert. Der pipelineService verwaltet den aktuellen Zustand und emittiert Ereignisse bei jedem Zustandswechsel. Der WebSocket-Service \u00fcbertr\u00e4gt diese Ereignisse sofort an alle verbundenen Clients.
Zustandswechsel \u2192 Event \u2192 WebSocket \u2192 Frontend-Update\n"},{"location":"architecture/overview/#service-layer-muster","title":"Service-Layer-Muster","text":"HTTP-Route \u2192 Service \u2192 Datenbank\n Routes delegieren die gesamte Business-Logik an Services. Services sind voneinander unabh\u00e4ngig und k\u00f6nnen einzeln getestet werden.
"},{"location":"architecture/overview/#schema-getriebene-einstellungen","title":"Schema-getriebene Einstellungen","text":"Die Settings-Konfiguration definiert sowohl die Validierungsregeln als auch die UI-Struktur in einer einzigen Quelle (settings_schema-Tabelle). Die DynamicSettingsForm-Komponente rendert das Formular dynamisch aus dem Schema.
Der WebSocket-Server l\u00e4uft unter dem Pfad /ws. Nachrichten werden als JSON \u00fcbertragen:
{\n \"type\": \"PIPELINE_STATE_CHANGE\",\n \"data\": {\n \"state\": \"ENCODING\",\n \"jobId\": 42,\n \"progress\": 73.5,\n \"eta\": \"00:12:34\"\n }\n}\n Nachrichtentypen:
Typ BeschreibungPIPELINE_STATE_CHANGE Pipeline-Zustand hat gewechselt PROGRESS_UPDATE Fortschritt (% und ETA) DISC_DETECTED Disc wurde erkannt DISC_REMOVED Disc wurde entfernt ERROR Fehler aufgetreten JOB_COMPLETE Job abgeschlossen"},{"location":"architecture/overview/#reconnect-logik","title":"Reconnect-Logik","text":"Der Frontend-Hook useWebSocket.js implementiert automatisches Reconnect mit exponential backoff bei Verbindungsabbr\u00fcchen.
Externe Tools (MakeMKV, HandBrake, MediaInfo) werden als Child Processes gestartet:
spawn(command, args, { stdio: ['ignore', 'pipe', 'pipe'] })\n progressParsers.jsRipster verwendet eine einzige SQLite-Datei f\u00fcr alle persistenten Daten:
backend/data/ripster.db\n Tabellen:
Tabelle Inhaltjobs Alle Rip-Jobs mit Status, Logs, Metadaten pipeline_state Aktueller Pipeline-Zustand (Singleton) settings_schema Schema aller verf\u00fcgbaren Einstellungen settings_values Benutzer-konfigurierte Werte"},{"location":"architecture/overview/#migrations-strategie","title":"Migrations-Strategie","text":"Beim Start pr\u00fcft database.js automatisch, ob das Schema aktuell ist, und f\u00fchrt fehlende Migrationen aus. Korrupte Datenbankdateien werden in ein Quarant\u00e4ne-Verzeichnis verschoben und eine neue Datenbank erstellt.
Alle Fehler werden mit Kontext-Metadaten protokolliert:
logger.error('Encoding fehlgeschlagen', {\n jobId: job.id,\n command: cmd,\n exitCode: code,\n stderr: lastLines\n});\n"},{"location":"architecture/overview/#job-fehler-recovery","title":"Job-Fehler-Recovery","text":"ERROR)validators.js validiertcommandLine.js konstruiert (kein Shell-Injection-Risiko)CORS_ORIGIN=http://localhost:5173\n In Produktion sollte dieser Wert auf die tats\u00e4chliche Frontend-URL gesetzt werden.
"},{"location":"configuration/","title":"Konfiguration","text":"Einstellungsreferenz
Alle verf\u00fcgbaren Einstellungen mit Typen, Standardwerten und Beschreibungen.
Einstellungsreferenz
Umgebungsvariablen
Umgebungsvariablen f\u00fcr Backend und Frontend.
Umgebungsvariablen
Umgebungsvariablen \u00fcberschreiben die Standardwerte und eignen sich f\u00fcr Server-Deployments.
"},{"location":"configuration/environment/#backend-umgebungsvariablen","title":"Backend-Umgebungsvariablen","text":"Konfigurationsdatei: backend/.env
PORT 3001 Port des Express-Servers DB_PATH ./data/ripster.db Pfad zur SQLite-Datenbankdatei CORS_ORIGIN http://localhost:5173 Erlaubter CORS-Origin LOG_DIR ./logs Verzeichnis f\u00fcr Log-Dateien LOG_LEVEL info Log-Level (debug, info, warn, error)"},{"location":"configuration/environment/#beispiel-backendenv","title":"Beispiel: backend/.env","text":"PORT=3001\nDB_PATH=/var/lib/ripster/ripster.db\nCORS_ORIGIN=http://192.168.1.100:5173\nLOG_DIR=/var/log/ripster\nLOG_LEVEL=info\n"},{"location":"configuration/environment/#frontend-umgebungsvariablen","title":"Frontend-Umgebungsvariablen","text":"Konfigurationsdatei: frontend/.env
VITE_API_BASE http://localhost:3001 Backend-API-URL VITE_WS_URL ws://localhost:3001 WebSocket-URL VITE_PUBLIC_ORIGIN \u2014 \u00d6ffentliche Origin-URL (f\u00fcr CORS) VITE_HMR_HOST \u2014 Vite HMR-Host (f\u00fcr Remote-Entwicklung) VITE_HMR_PORT \u2014 Vite HMR-Port"},{"location":"configuration/environment/#beispiel-frontendenv-entwicklung","title":"Beispiel: frontend/.env (Entwicklung)","text":"VITE_API_BASE=http://localhost:3001\nVITE_WS_URL=ws://localhost:3001\n"},{"location":"configuration/environment/#beispiel-frontendenv-netzwerk-zugriff","title":"Beispiel: frontend/.env (Netzwerk-Zugriff)","text":"VITE_API_BASE=http://192.168.1.100:3001\nVITE_WS_URL=ws://192.168.1.100:3001\nVITE_PUBLIC_ORIGIN=http://192.168.1.100:5173\n"},{"location":"configuration/environment/#envexample-dateien","title":".env.example Dateien","text":"Das Repository enth\u00e4lt Vorlagen f\u00fcr beide Konfigurationsdateien:
# Backend\ncp backend/.env.example backend/.env\n\n# Frontend\ncp frontend/.env.example frontend/.env\n"},{"location":"configuration/environment/#prioritat-der-konfiguration","title":"Priorit\u00e4t der Konfiguration","text":"Einstellungen werden in folgender Reihenfolge geladen (h\u00f6here Priorit\u00e4t \u00fcberschreibt niedrigere):
1. Systemumgebungsvariablen (export VAR=value)\n2. .env-Datei\n3. Hardcodierte Standardwerte in config.js\n"},{"location":"configuration/environment/#log_level","title":"LOG_LEVEL","text":"Level Ausgabe debug Alle Meldungen inkl. Debugging info Normale Betriebsinformationen warn Warnungen + Fehler error Nur Fehler Produktionsempfehlung
F\u00fcr Produktionsumgebungen LOG_LEVEL=info oder LOG_LEVEL=warn verwenden. debug erzeugt sehr viele Log-Eintr\u00e4ge.
Vollst\u00e4ndige \u00dcbersicht aller Ripster-Einstellungen. Alle Einstellungen werden \u00fcber die Web-Oberfl\u00e4che unter Einstellungen verwaltet.
"},{"location":"configuration/settings-reference/#kategorie-pfade-paths","title":"Kategorie: Pfade (paths)","text":"Schl\u00fcssel Typ Standard Pflicht Beschreibungraw_dir string \u2014 \u2705 Verzeichnis f\u00fcr rohe MKV-Dateien nach dem Ripping movie_dir string \u2014 \u2705 Ausgabeverzeichnis f\u00fcr encodierte Filme log_dir string ./logs \u2014 Verzeichnis f\u00fcr Log-Dateien Beispielkonfiguration
raw_dir = /mnt/nas/raw\nmovie_dir = /mnt/nas/movies\nlog_dir = /var/log/ripster\n"},{"location":"configuration/settings-reference/#kategorie-tools-tools","title":"Kategorie: Tools (tools)","text":"Schl\u00fcssel Typ Standard Beschreibung makemkv_command string makemkvcon Befehl oder absoluter Pfad zu MakeMKV handbrake_command string HandBrakeCLI Befehl oder absoluter Pfad zu HandBrake mediainfo_command string mediainfo Befehl oder absoluter Pfad zu MediaInfo Absolute Pfade verwenden
Falls die Tools nicht im PATH des Systems sind:
makemkv_command = /usr/local/bin/makemkvcon\nhandbrake_command = /usr/local/bin/HandBrakeCLI\nmediainfo_command = /usr/bin/mediainfo\n"},{"location":"configuration/settings-reference/#kategorie-encoding-encoding","title":"Kategorie: Encoding (encoding)","text":"Schl\u00fcssel Typ Standard Beschreibung handbrake_preset string H.265 MKV 1080p30 Name des HandBrake-Presets handbrake_extra_args string (leer) Zus\u00e4tzliche HandBrake CLI-Argumente output_extension string mkv Dateiendung der Ausgabedatei filename_template string {title} ({year}) Template f\u00fcr den Dateinamen"},{"location":"configuration/settings-reference/#verfugbare-handbrake-presets","title":"Verf\u00fcgbare HandBrake-Presets","text":"Eine vollst\u00e4ndige Liste der verf\u00fcgbaren Presets:
HandBrakeCLI --preset-list\n H\u00e4ufig verwendete Presets:
Preset BeschreibungH.265 MKV 1080p30 H.265/HEVC, Full-HD, 30fps H.265 MKV 720p30 H.265/HEVC, HD, 30fps H.264 MKV 1080p30 H.264/AVC, Full-HD, 30fps HQ 1080p30 Surround Hohe Qualit\u00e4t, Full-HD mit Surround"},{"location":"configuration/settings-reference/#dateiname-template-platzhalter","title":"Dateiname-Template-Platzhalter","text":"Platzhalter Beispiel {title} Inception {year} 2010 {imdb_id} tt1375666 {type} movie"},{"location":"configuration/settings-reference/#kategorie-laufwerk-drive","title":"Kategorie: Laufwerk (drive)","text":"Schl\u00fcssel Typ Standard Optionen Beschreibung drive_mode select auto auto, explicit Laufwerk-Erkennungsmodus drive_device string /dev/sr0 \u2014 Ger\u00e4te-Pfad (nur bei explicit) disc_poll_interval_ms number 5000 1000\u201360000 Polling-Intervall in Millisekunden drive_mode Optionen:
auto Ripster erkennt das Laufwerk automatisch explicit Verwendet das in drive_device konfigurierte Ger\u00e4t"},{"location":"configuration/settings-reference/#kategorie-makemkv-makemkv","title":"Kategorie: MakeMKV (makemkv)","text":"Schl\u00fcssel Typ Standard Min Max Beschreibung makemkv_min_length_minutes number 15 0 999 Mindest-Titell\u00e4nge in Minuten makemkv_backup_mode boolean false \u2014 \u2014 Backup-Modus statt MKV-Modus makemkv_min_length_minutes: Titel k\u00fcrzer als dieser Wert werden von MakeMKV ignoriert. Verhindert das Rippen von Men\u00fc-Schleifen und kurzen Extra-Clips.
makemkv_backup_mode: Im Backup-Modus erstellt MakeMKV eine vollst\u00e4ndige Disc-Kopie mit Men\u00fcs. Im Standard-Modus werden direkt MKV-Dateien erstellt.
omdb_api_key string \u2014 \u2705 API-Key von omdbapi.com omdb_default_type select movie \u2014 Standard-Suchtyp: movie oder series"},{"location":"configuration/settings-reference/#kategorie-benachrichtigungen-notifications","title":"Kategorie: Benachrichtigungen (notifications)","text":"Schl\u00fcssel Typ Standard Beschreibung pushover_user_key string \u2014 PushOver User-Key pushover_api_token string \u2014 PushOver API-Token Beide Felder m\u00fcssen konfiguriert sein, um PushOver-Benachrichtigungen zu aktivieren. Die Verbindung kann mit dem Test-Button in den Einstellungen gepr\u00fcft werden.
"},{"location":"configuration/settings-reference/#standard-einstellungen-zurucksetzen","title":"Standard-Einstellungen zur\u00fccksetzen","text":"\u00dcber die Datenbank k\u00f6nnen Einstellungen auf Standardwerte zur\u00fcckgesetzt werden:
sqlite3 backend/data/ripster.db \\\n \"DELETE FROM settings_values WHERE key = 'handbrake_preset';\"\n Beim n\u00e4chsten Laden der Einstellungen wird der Standardwert verwendet.
"},{"location":"deployment/","title":"Deployment","text":"Entwicklungsumgebung
Lokale Entwicklungsumgebung einrichten.
Entwicklung
Produktion
Ripster auf einem Server dauerhaft betreiben.
Produktion
./start.sh\n Das Skript startet automatisch: - Backend auf Port 3001 (mit Nodemon f\u00fcr Hot-Reload) - Frontend auf Port 5173 (mit Vite HMR)
"},{"location":"deployment/development/#manuelle-entwicklungsumgebung","title":"Manuelle Entwicklungsumgebung","text":""},{"location":"deployment/development/#terminal-1-backend","title":"Terminal 1 \u2013 Backend","text":"cd backend\nnpm install\nnpm run dev\n Backend l\u00e4uft auf http://localhost:3001 mit Nodemon \u2013 Neustart bei Datei\u00e4nderungen.
cd frontend\nnpm install\nnpm run dev\n Frontend l\u00e4uft auf http://localhost:5173 mit Vite HMR \u2013 sofortige Browser-Updates.
Im Entwicklungsmodus proxied Vite alle API- und WebSocket-Anfragen zum Backend:
// frontend/vite.config.js\nserver: {\n proxy: {\n '/api': {\n target: 'http://localhost:3001',\n changeOrigin: true\n },\n '/ws': {\n target: 'ws://localhost:3001',\n ws: true\n }\n }\n}\n Das bedeutet: Im Browser macht das Frontend Anfragen an localhost:5173/api/... \u2013 Vite leitet diese an localhost:3001/api/... weiter.
Falls Ripster auf einem entfernten Server entwickelt wird (z.B. Homeserver), muss die Vite-Konfiguration angepasst werden:
# frontend/.env.local\nVITE_API_BASE=http://192.168.1.100:3001\nVITE_WS_URL=ws://192.168.1.100:3001\nVITE_HMR_HOST=192.168.1.100\nVITE_HMR_PORT=5173\n"},{"location":"deployment/development/#log-level-fur-entwicklung","title":"Log-Level f\u00fcr Entwicklung","text":"# backend/.env\nLOG_LEVEL=debug\n Im Debug-Modus werden alle Ausgaben der externen Tools (MakeMKV, HandBrake) vollst\u00e4ndig geloggt.
"},{"location":"deployment/development/#stoppen","title":"Stoppen","text":"./kill.sh\n"},{"location":"deployment/development/#linting-type-checking","title":"Linting & Type-Checking","text":"# Frontend (ESLint)\ncd frontend && npm run lint\n\n# Backend hat keine separaten Lint-Scripts,\n# nutze direkt eslint falls konfiguriert\n"},{"location":"deployment/development/#deployment-script","title":"Deployment-Script","text":"Das deploy-ripster.sh-Script \u00fcbertr\u00e4gt Code auf einen Remote-Server per SSH:
./deploy-ripster.sh\n Was das Script tut: 1. rsync synchronisiert den Code (Backend-Quellcode ohne data/) 2. Die Datenbank (backend/data/) wird nicht \u00fcberschrieben 3. Verbindung via SSH (konfigurierbar im Script)
Anpassung des Scripts:
# deploy-ripster.sh\nREMOTE_HOST=\"192.168.1.100\"\nREMOTE_USER=\"michael\"\nREMOTE_PATH=\"/home/michael/ripster\"\n"},{"location":"deployment/production/","title":"Produktions-Deployment","text":""},{"location":"deployment/production/#empfohlene-architektur","title":"Empfohlene Architektur","text":"Internet / Heimnetz\n \u2193\n nginx (Reverse Proxy)\n \u2193\n \u250c\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2510\n \u2502 \u2502\nBackend Frontend\n :3001 (statische Dateien)\n"},{"location":"deployment/production/#systemd-service","title":"systemd-Service","text":"F\u00fcr ein dauerhaftes Betreiben als systemd-Service:
sudo nano /etc/systemd/system/ripster.service\n [Unit]\nDescription=Ripster - Disc Ripping Service\nAfter=network.target\n\n[Service]\nType=simple\nUser=michael\nWorkingDirectory=/home/michael/ripster\nExecStart=/bin/bash /home/michael/ripster/start.sh\nExecStop=/bin/bash /home/michael/ripster/kill.sh\nRestart=on-failure\nRestartSec=10s\n\n# Umgebungsvariablen\nEnvironment=NODE_ENV=production\nEnvironment=PORT=3001\nEnvironment=LOG_LEVEL=info\n\n[Install]\nWantedBy=multi-user.target\n # Service aktivieren und starten\nsudo systemctl daemon-reload\nsudo systemctl enable ripster\nsudo systemctl start ripster\n\n# Status pr\u00fcfen\nsudo systemctl status ripster\n\n# Logs anzeigen\njournalctl -u ripster -f\n"},{"location":"deployment/production/#frontend-build","title":"Frontend-Build","text":"F\u00fcr Produktion das Frontend bauen:
cd frontend\nnpm run build\n Die statischen Dateien landen in frontend/dist/.
# /etc/nginx/sites-available/ripster\nserver {\n listen 80;\n server_name ripster.local;\n\n # Statisches Frontend\n root /home/michael/ripster/frontend/dist;\n index index.html;\n\n # SPA Fallback (React Router)\n location / {\n try_files $uri $uri/ /index.html;\n }\n\n # API-Proxy zum Backend\n location /api/ {\n proxy_pass http://localhost:3001;\n proxy_set_header Host $host;\n proxy_set_header X-Real-IP $remote_addr;\n }\n\n # WebSocket-Proxy\n location /ws {\n proxy_pass http://localhost: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 sudo ln -s /etc/nginx/sites-available/ripster /etc/nginx/sites-enabled/\nsudo nginx -t && sudo systemctl reload nginx\n"},{"location":"deployment/production/#nur-backend-produktion-ohne-nginx","title":"Nur-Backend-Produktion (ohne nginx)","text":"Falls kein Reverse Proxy gew\u00fcnscht ist, kann das Backend die Frontend-Dateien direkt ausliefern:
# Frontend bauen\ncd frontend && npm run build\n\n# Backend startet und serviert frontend/dist/\ncd backend && NODE_ENV=production npm start\n Das Backend ist so konfiguriert, dass es im Produktionsmodus die frontend/dist/-Dateien als statische Assets ausliefert.
# Datenbank sichern\ncp backend/data/ripster.db backend/data/ripster.db.backup.$(date +%Y%m%d)\n\n# Oder mit SQLite-eigenem Backup-Befehl\nsqlite3 backend/data/ripster.db \".backup '/mnt/backup/ripster.db'\"\n Automatisches Backup
Cron-Job f\u00fcr t\u00e4gliches Backup:
0 3 * * * sqlite3 /home/michael/ripster/backend/data/ripster.db \".backup '/mnt/backup/ripster-$(date +\\%Y\\%m\\%d).db'\"\n"},{"location":"deployment/production/#log-rotation","title":"Log-Rotation","text":"Ripster rotiert Logs automatisch t\u00e4glich. Falls zus\u00e4tzlich systemd-Journal-Rotation gew\u00fcnscht ist:
# /etc/logrotate.d/ripster\n/home/michael/ripster/backend/logs/*.log {\n daily\n rotate 14\n compress\n missingok\n notifempty\n}\n"},{"location":"deployment/production/#sicherheitshinweise","title":"Sicherheitshinweise","text":"Heimnetz-Einsatz
Ripster ist f\u00fcr den Einsatz im lokalen Heimnetz konzipiert und enth\u00e4lt keine Authentifizierung. Stelle sicher, dass der Dienst nicht \u00f6ffentlich erreichbar ist.
Falls \u00f6ffentlicher Zugang ben\u00f6tigt wird:
Basic Auth via nginx:
sudo htpasswd -c /etc/nginx/.htpasswd michael\n location / {\n auth_basic \"Ripster\";\n auth_basic_user_file /etc/nginx/.htpasswd;\n # ...\n}\n VPN-Zugang (empfohlen): Zugriff nur \u00fcber WireGuard/OpenVPN
SSL/TLS: Let's Encrypt mit certbot f\u00fcr HTTPS
Dieser Abschnitt f\u00fchrt dich durch die Installation und Einrichtung von Ripster.
"},{"location":"getting-started/#uberblick","title":"\u00dcberblick","text":":material-list-check: Voraussetzungen
Systemanforderungen und externe Tools, die vor der Installation ben\u00f6tigt werden.
Voraussetzungen pr\u00fcfen
Installation
Schritt-f\u00fcr-Schritt-Anleitung zur Installation von Ripster.
Installation starten
Konfiguration
Einrichten von Pfaden, API-Keys und Encoding-Presets.
Konfigurieren
Schnellstart
Rippe deinen ersten Film in wenigen Minuten.
Loslegen
Alle Einstellungen werden \u00fcber die Web-Oberfl\u00e4che unter Einstellungen verwaltet und in der SQLite-Datenbank gespeichert.
"},{"location":"getting-started/configuration/#pflichteinstellungen","title":"Pflichteinstellungen","text":"Diese Einstellungen m\u00fcssen vor dem ersten Rip konfiguriert werden:
"},{"location":"getting-started/configuration/#pfade","title":"Pfade","text":"Einstellung Beschreibung Beispielraw_dir Verzeichnis f\u00fcr rohe MKV-Dateien /mnt/nas/raw movie_dir Ausgabeverzeichnis f\u00fcr kodierte Filme /mnt/nas/movies log_dir Verzeichnis f\u00fcr Log-Dateien /var/log/ripster Berechtigungen
Der Ripster-Prozess ben\u00f6tigt Schreibrechte auf alle konfigurierten Verzeichnisse.
# Verzeichnisse erstellen und Berechtigungen setzen\nsudo mkdir -p /mnt/nas/{raw,movies}\nsudo chown $USER:$USER /mnt/nas/{raw,movies}\n"},{"location":"getting-started/configuration/#omdb-api","title":"OMDb API","text":"Einstellung Beschreibung omdb_api_key API-Key von omdbapi.com omdb_default_type Standard-Suchtyp: movie oder series"},{"location":"getting-started/configuration/#tool-konfiguration","title":"Tool-Konfiguration","text":"Einstellung Standard Beschreibung makemkv_command makemkvcon Pfad oder Befehl f\u00fcr MakeMKV handbrake_command HandBrakeCLI Pfad oder Befehl f\u00fcr HandBrake mediainfo_command mediainfo Pfad oder Befehl f\u00fcr MediaInfo Absolute Pfade
Falls die Tools nicht im PATH sind, verwende absolute Pfade:
/usr/local/bin/HandBrakeCLI\n"},{"location":"getting-started/configuration/#encoding-konfiguration","title":"Encoding-Konfiguration","text":"Einstellung Standard Beschreibung handbrake_preset H.265 MKV 1080p30 HandBrake-Preset-Name handbrake_extra_args (leer) Zus\u00e4tzliche HandBrake-Argumente output_extension mkv Dateiendung der Ausgabedatei filename_template {title} ({year}) Template f\u00fcr Dateinamen"},{"location":"getting-started/configuration/#dateiname-template","title":"Dateiname-Template","text":"Das Template unterst\u00fctzt folgende Platzhalter:
Platzhalter Beschreibung Beispiel{title} Filmtitel Inception {year} Erscheinungsjahr 2010 {imdb_id} IMDb-ID tt1375666 {type} movie oder series movie Beispiel-Template:
{title} ({year})\n\u2192 Inception (2010).mkv\n"},{"location":"getting-started/configuration/#laufwerk-konfiguration","title":"Laufwerk-Konfiguration","text":"Einstellung Standard Beschreibung drive_mode auto auto (automatisch erkennen) oder explicit (festes Ger\u00e4t) drive_device /dev/sr0 Ger\u00e4te-Pfad (nur bei explicit) disc_poll_interval_ms 5000 Polling-Intervall in Millisekunden"},{"location":"getting-started/configuration/#makemkv-konfiguration","title":"MakeMKV-Konfiguration","text":"Einstellung Standard Beschreibung makemkv_min_length_minutes 15 Mindestl\u00e4nge f\u00fcr Titel in Minuten makemkv_backup_mode false Backup-Modus statt MKV-Modus Backup-Modus
Im Backup-Modus erstellt MakeMKV eine vollst\u00e4ndige Kopie der Disc (inkl. Men\u00fcs). Der Standardmodus erstellt direkt MKV-Dateien.
"},{"location":"getting-started/configuration/#benachrichtigungen-pushover","title":"Benachrichtigungen (PushOver)","text":"Einstellung Beschreibungpushover_user_key Dein PushOver User-Key pushover_api_token API-Token deiner PushOver-App Nach der Eingabe kann die Verbindung mit dem Test-Button gepr\u00fcft werden.
"},{"location":"getting-started/configuration/#vollstandige-einstellungsreferenz","title":"Vollst\u00e4ndige Einstellungsreferenz","text":"Eine vollst\u00e4ndige Liste aller Einstellungen mit Typen, Validierung und Standardwerten findest du unter:
Einstellungsreferenz
"},{"location":"getting-started/installation/","title":"Installation","text":""},{"location":"getting-started/installation/#repository-klonen","title":"Repository klonen","text":"git clone https://github.com/YOUR_GITHUB_USERNAME/ripster.git\ncd ripster\n"},{"location":"getting-started/installation/#automatischer-start","title":"Automatischer Start","text":"Ripster enth\u00e4lt ein start.sh-Skript, das alle Abh\u00e4ngigkeiten installiert und Backend + Frontend gleichzeitig startet:
./start.sh\n Das Skript f\u00fchrt automatisch folgende Schritte durch:
npm install f\u00fcr Root, Backend und FrontendErfolgreich gestartet
http://localhost:3001http://localhost:5173Falls du mehr Kontrolle ben\u00f6tigst:
# Root-Abh\u00e4ngigkeiten\nnpm install\n\n# Backend-Abh\u00e4ngigkeiten\ncd backend && npm install && cd ..\n\n# Frontend-Abh\u00e4ngigkeiten\ncd frontend && npm install && cd ..\n\n# Backend starten (Terminal 1)\ncd backend && npm run dev\n\n# Frontend starten (Terminal 2)\ncd frontend && npm run dev\n"},{"location":"getting-started/installation/#umgebungsvariablen-konfigurieren","title":"Umgebungsvariablen konfigurieren","text":""},{"location":"getting-started/installation/#backend","title":"Backend","text":"cp backend/.env.example backend/.env\n Bearbeite backend/.env:
PORT=3001\nDB_PATH=./data/ripster.db\nCORS_ORIGIN=http://localhost:5173\nLOG_DIR=./logs\nLOG_LEVEL=info\n"},{"location":"getting-started/installation/#frontend","title":"Frontend","text":"cp frontend/.env.example frontend/.env\n Bearbeite frontend/.env:
VITE_API_BASE=http://localhost:3001\nVITE_WS_URL=ws://localhost:3001\n Alle Umgebungsvariablen
Eine vollst\u00e4ndige \u00dcbersicht aller Umgebungsvariablen findest du unter Umgebungsvariablen.
"},{"location":"getting-started/installation/#datenbank-initialisieren","title":"Datenbank initialisieren","text":"Die SQLite-Datenbank wird automatisch beim ersten Start erstellt und mit dem Schema aus db/schema.sql initialisiert. Es sind keine manuellen Datenbankschritte erforderlich.
backend/data/\n\u2514\u2500\u2500 ripster.db \u2190 Wird automatisch angelegt\n"},{"location":"getting-started/installation/#stoppen","title":"Stoppen","text":"./kill.sh\n Das Skript beendet Backend- und Frontend-Prozesse graceful.
"},{"location":"getting-started/installation/#verzeichnisstruktur-nach-installation","title":"Verzeichnisstruktur nach Installation","text":"ripster/\n\u251c\u2500\u2500 backend/\n\u2502 \u251c\u2500\u2500 data/ \u2190 SQLite-Datenbank (nach erstem Start)\n\u2502 \u251c\u2500\u2500 logs/ \u2190 Log-Dateien\n\u2502 \u251c\u2500\u2500 node_modules/ \u2190 Backend-Abh\u00e4ngigkeiten\n\u2502 \u2514\u2500\u2500 .env \u2190 Backend-Konfiguration\n\u251c\u2500\u2500 frontend/\n\u2502 \u251c\u2500\u2500 node_modules/ \u2190 Frontend-Abh\u00e4ngigkeiten\n\u2502 \u251c\u2500\u2500 dist/ \u2190 Production-Build (nach npm run build)\n\u2502 \u2514\u2500\u2500 .env \u2190 Frontend-Konfiguration\n\u2514\u2500\u2500 node_modules/ \u2190 Root-Abh\u00e4ngigkeiten (concurrently etc.)\n"},{"location":"getting-started/installation/#nachste-schritte","title":"N\u00e4chste Schritte","text":"Nach erfolgreicher Installation:
Zur Konfiguration
"},{"location":"getting-started/prerequisites/","title":"Voraussetzungen","text":"Bevor du Ripster installierst, stelle sicher, dass folgende Software auf deinem System verf\u00fcgbar ist.
"},{"location":"getting-started/prerequisites/#system-anforderungen","title":"System-Anforderungen","text":"Anforderung Mindestversion Empfohlen Betriebssystem Linux / macOS Ubuntu 22.04+ Node.js 20.19.0 20.x LTS RAM 4 GB 8 GB+ Festplatte 50 GB frei 500 GB+ (f\u00fcr Roh-MKVs)"},{"location":"getting-started/prerequisites/#nodejs","title":"Node.js","text":"Ripster ben\u00f6tigt Node.js >= 20.19.0.
nvm (empfohlen)Ubuntu/DebianmacOS (Homebrew)# nvm installieren\ncurl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash\n\n# Node.js 20 installieren\nnvm install 20\nnvm use 20\n\n# Version pr\u00fcfen\nnode --version # v20.x.x\n curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -\nsudo apt-get install -y nodejs\n\nnode --version # v20.x.x\n brew install node@20\nnode --version # v20.x.x\n"},{"location":"getting-started/prerequisites/#externe-tools","title":"Externe Tools","text":""},{"location":"getting-started/prerequisites/#makemkv","title":"MakeMKV","text":"Lizenz erforderlich
MakeMKV ist f\u00fcr den pers\u00f6nlichen Gebrauch kostenlos (Beta-Lizenz), ben\u00f6tigt aber eine g\u00fcltige Lizenz.
# Ubuntu/Debian - PPA verwenden\nsudo add-apt-repository ppa:heyarje/makemkv-beta\nsudo apt-get update\nsudo apt-get install makemkv-bin makemkv-oss\n\n# Installierte Version pr\u00fcfen\nmakemkvcon --version\n MakeMKV Download
"},{"location":"getting-started/prerequisites/#handbrake-cli","title":"HandBrake CLI","text":"# Ubuntu/Debian\nsudo add-apt-repository ppa:stebbins/handbrake-releases\nsudo apt-get update\nsudo apt-get install handbrake-cli\n\n# Version pr\u00fcfen\nHandBrakeCLI --version\n\n# macOS\nbrew install handbrake\n HandBrake Download
"},{"location":"getting-started/prerequisites/#mediainfo","title":"MediaInfo","text":"# Ubuntu/Debian\nsudo apt-get install mediainfo\n\n# macOS\nbrew install mediainfo\n\n# Version pr\u00fcfen\nmediainfo --Version\n"},{"location":"getting-started/prerequisites/#disc-laufwerk","title":"Disc-Laufwerk","text":"Ripster ben\u00f6tigt ein physisches DVD- oder Blu-ray-Laufwerk.
Blu-ray unter Linux
F\u00fcr Blu-ray-Ripping unter Linux wird zus\u00e4tzlich libaacs ben\u00f6tigt. MakeMKV bringt jedoch eine eigene Entschl\u00fcsselung mit, daher ist dies in den meisten F\u00e4llen nicht erforderlich.
# Laufwerk pr\u00fcfen\nls /dev/sr*\n# oder\nlsblk | grep rom\n"},{"location":"getting-started/prerequisites/#omdb-api-key","title":"OMDb API-Key","text":"Ripster verwendet die OMDb API f\u00fcr Filmmetadaten.
F\u00fcr mobile Push-Benachrichtigungen bei Fertigstellung oder Fehlern:
F\u00fcr Remote-Deployment via deploy-ripster.sh:
# sshpass installieren\nsudo apt-get install sshpass\n"},{"location":"getting-started/prerequisites/#checkliste","title":"Checkliste","text":"node --version)makemkvcon installiert (makemkvcon --version)HandBrakeCLI installiert (HandBrakeCLI --version)mediainfo installiert (mediainfo --Version)ls /dev/sr*)Nach der Installation und Konfiguration f\u00fchrt diese Seite Schritt f\u00fcr Schritt durch den ersten Rip \u2013 mit allen Details aus dem Code.
"},{"location":"getting-started/quickstart/#ubersicht-pipeline-zustande","title":"\u00dcbersicht: Pipeline-Zust\u00e4nde","text":"\u25cf IDLE Warten 1 DISC_DETECTED Disc erkannt 2 METADATA_SELECTION Scan & Metadaten \u26a0 WAITING_FOR_USER_DECISION Playlist w\u00e4hlen(nur bei Obfusk.) 3 READY_TO_START Bereit 4 RIPPING MakeMKV 5 MEDIAINFO_CHECK HandBrake-Scan 6 READY_TO_ENCODE Track-Review 7 ENCODING HandBrake 8 POST_ENCODE_SCRIPTS Skripte(optional) \u2713 FINISHED FertigLegende: \u25cf Warten \u00a0|\u00a0 \u25a0 L\u00e4uft automatisch \u00a0|\u00a0 \u25a0 Benutzeraktion \u00a0|\u00a0 \u26a0 Optional \u00a0|\u00a0 \u25a0 Encodierung \u00a0|\u00a0 \u2713 Fertig
Vollst\u00e4ndiges Zustandsdiagramm (inkl. Fehler- & Alternativpfade)flowchart LR\n START(( )) --> IDLE\n\n IDLE -->|Disc erkannt| DD[DISC_DETECTED]\n DD -->|Analyse starten| META[METADATA\\nSELECTION]\n\n META -->|1 Kandidat| RTS[READY_TO\\nSTART]\n META -->|mehrere Kandidaten| WUD[WAITING_FOR\\nUSER_DECISION]\n WUD -->|Playlist best\u00e4tigt| RTS\n\n RTS -->|Raw vorhanden| MIC[MEDIAINFO\\nCHECK]\n RTS -->|Ripping starten| RIP[RIPPING]\n RIP -->|MKV fertig| MIC\n RIP -->|Fehler| ERR\n\n MIC --> RTE[READY_TO\\nENCODE]\n RTE -->|best\u00e4tigt| ENC[ENCODING]\n\n ENC -->|mit Skripten| PES[POST_ENCODE\\nSCRIPTS]\n ENC -->|ohne Skripte| FIN([FINISHED])\n ENC -->|Fehler| ERR\n\n PES -->|Erfolg| FIN\n PES -->|Fehler| ERR\n\n ERR([ERROR]) -->|Retry / Cancel| IDLE\n\n style FIN fill:#e8f5e9,stroke:#66bb6a,color:#2e7d32\n style ERR fill:#ffebee,stroke:#ef5350,color:#c62828\n style WUD fill:#fff8e1,stroke:#ffa726,color:#e65100\n style PES fill:#f3e5f5,stroke:#ab47bc,color:#6a1b9a\n style ENC fill:#f3e5f5,stroke:#ab47bc,color:#6a1b9a"},{"location":"getting-started/quickstart/#schritt-1-ripster-starten","title":"Schritt 1 \u2013 Ripster starten","text":"cd ripster\n./start.sh\n \u00d6ffne http://localhost:5173 im Browser. Das Dashboard zeigt IDLE.
DISC_DETECTED","text":"Lege eine DVD oder Blu-ray ein. Der diskDetectionService pollt das Laufwerk alle disc_poll_interval_ms Millisekunden (Standard: 5 Sekunden).
Was passiert im Code:
diskDetectionService emittiert disc:inserted mit Ger\u00e4teinformationenpipelineService.onDiscInserted() wird aufgerufenManuelle Ausl\u00f6sung
Falls die automatische Erkennung nicht greift:
curl -X POST http://localhost:3001/api/pipeline/analyze\n"},{"location":"getting-started/quickstart/#schritt-3-analyse-starten-metadata_selection","title":"Schritt 3 \u2013 Analyse starten \u2192 METADATA_SELECTION","text":"Klicke auf \"Analyse starten\" oder warte auf automatischen Start.
Was passiert im Code:
status: METADATA_SELECTION)MetadataSelectionDialog \u00f6ffnet sich im Frontend mit den vorgeladenen SuchergebnissenErkannter Titel: Der Disc-Label (z. B. INCEPTION) wird als Suchbegriff verwendet. Falls kein Label vorhanden, bleibt das Suchfeld leer.
MetadataSelectionDialog)","text":"Der Dialog zeigt vorgeladene OMDb-Suchergebnisse. Du kannst:
"},{"location":"getting-started/quickstart/#4a-omdb-suchergebnis-wahlen","title":"4a) OMDb-Suchergebnis w\u00e4hlen","text":"\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Suche: [Inception ] \ud83d\udd0d \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 \u25b6 Inception (2010) \u00b7 Movie \u00b7 tt1375666 \u2502\n\u2502 Inception: ... \u00b7 Series \u00b7 ... \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 [Auswahl \u00fcbernehmen] \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n movie / series umschalten m\u00f6glichFalls kein passendes Ergebnis gefunden wird: - Titel, Jahr und IMDb-ID manuell eingeben - OMDb-Poster wird \u00fcbersprungen
Was passiert nach Best\u00e4tigung:
Ripster ruft pipelineService.selectMetadata() auf und f\u00fchrt sofort eine Playlist-Analyse durch:
READY_TO_START","text":"Der Dialog schlie\u00dft sich automatisch. Die empfohlene Playlist wird still \u00fcbernommen. Weiter zu Schritt 6.
"},{"location":"getting-started/quickstart/#5b-obfuskierung-erkannt-waiting_for_user_decision","title":"5b) Obfuskierung erkannt \u2192WAITING_FOR_USER_DECISION","text":"Der Playlist-Auswahl-Dialog erscheint zus\u00e4tzlich (nach dem Metadaten-Dialog):
\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Playlist-Auswahl \u2502\n\u2502 Es wurden mehrere Titel mit \u00e4hnlicher Laufzeit gefunden. \u2502\n\u2502 Bitte w\u00e4hle die korrekte Playlist: \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Playlist \u2502 Laufzeit \u2502 Score \u2502 Bewertung \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 \u25cf 00800 \u2502 2:28:05 \u2502 +18 \u2502 wahrscheinlich korrekt \u2502\n\u2502 \u2502 \u2502 \u2502 (lineare Segmentfolge) \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 \u25cb 00801 \u2502 2:28:12 \u2502 \u22124 \u2502 Auff\u00e4llige Segmentreihenfolge \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 \u25cb 00900 \u2502 2:28:05 \u2502 \u221232 \u2502 Fake-Struktur \u2502\n\u2502 \u2502 \u2502 \u2502 (alternierendes Sprungmuster) \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n 847 Playlists insgesamt \u00b7 3 relevante Kandidaten (\u2265 15 min)\n Empfehlung: 00800 (vorausgew\u00e4hlt)\n [Playlist best\u00e4tigen]\n READY_TO_STARTScoring-Details
Wie die Scores berechnet werden, erkl\u00e4rt die Playlist-Analyse-Seite.
"},{"location":"getting-started/quickstart/#schritt-6-ripping-starten-ripping","title":"Schritt 6 \u2013 Ripping starten \u2192RIPPING","text":"Vorher pr\u00fcft Ripster: Existiert bereits eine Raw-Datei f\u00fcr diesen Job?
Klicke auf \"Starten\" im Dashboard.
Was MakeMKV ausf\u00fchrt (MKV-Modus):
makemkvcon mkv disc:0 all /mnt/raw/Inception-2010/ \\\n --minlength=900 -r\n Was MakeMKV ausf\u00fchrt (Backup-Modus):
makemkvcon backup disc:0 /mnt/raw/Inception-2010-backup/ \\\n --decrypt -r\n Live-Fortschritt wird aus der MakeMKV-Ausgabe geparst:
PRGV:2048,0,65536 \u2192 Fortschritt wird berechnet und per WebSocket gesendet\nPRGT:5011,0,\"Sichern...\" \u2192 Aktueller Task-Name\n Typische Dauer: - DVD: 20\u201345 Minuten - Blu-ray: 45\u2013120 Minuten
"},{"location":"getting-started/quickstart/#schritt-7-track-review-ready_to_encode","title":"Schritt 7 \u2013 Track-Review \u2192READY_TO_ENCODE","text":"Nach dem Ripping (oder direkt bei vorhandener Raw-Datei) startet der HandBrake-Scan:
HandBrakeCLI --scan -i <quelle> -t 0\n Dieser Scan liest alle Tracks aus ohne zu encodieren. Ripster baut daraus den Encode-Plan mit automatischer Vorauswahl:
Status: MEDIAINFO_CHECK \u2013 l\u00e4uft automatisch, kein Benutzereingriff
Danach \u00f6ffnet sich das Encode-Review-Panel (READY_TO_ENCODE):
\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Encode-Review \u2502\n\u2502 Titel: Disc Title 1 \u00b7 Laufzeit: 2:28:05 \u00b7 28 Kapitel \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Audio-Spuren \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 \u2611 \u2502 Track 1: English (AC3, 5.1) \u2502 Copy (ac3) \u2502\n\u2502 \u2611 \u2502 Track 2: Deutsch (DTS, 5.1) \u2502 Fallback Transcode (av_aac)\u2502\n\u2502 \u2610 \u2502 Track 3: Fran\u00e7ais (AC3, 2.0) \u2502 Nicht \u00fcbernommen \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Untertitel-Spuren \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 \u2611 \u2502 Track 1: Deutsch \u2502 Einbr.\u2610 \u2502Forc.\u2610\u2502Default\u2611 \u2502\n\u2502 \u2610 \u2502 Track 2: English \u2502 Einbr.\u2610 \u2502Forc.\u2610\u2502Default\u2610 \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 [Encode best\u00e4tigen] \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n"},{"location":"getting-started/quickstart/#audio-track-aktionen-verstehen","title":"Audio-Track-Aktionen verstehen","text":"Symbol/Text Bedeutung Copy (ac3) Track wird verlustfrei direkt \u00fcbernommen Copy (truehd) TrueHD-Track wird direkt \u00fcbernommen Transcode (av_aac) Track wird zu AAC umgewandelt Fallback Transcode (av_aac) Copy nicht m\u00f6glich \u2192 automatisch zu AAC Preset-Default (HandBrake) HandBrake-Preset entscheidet Nicht \u00fcbernommen Track ist nicht ausgew\u00e4hlt"},{"location":"getting-started/quickstart/#untertitel-flags","title":"Untertitel-Flags","text":"Flag Bedeutung Einbrennen Untertitel werden fest ins Video gebrannt (nur ein Track m\u00f6glich) Forced Nur erzwungene Untertitel-Einblendungen \u00fcbernehmen Default Diese Spur wird beim Abspielen automatisch aktiviert"},{"location":"getting-started/quickstart/#vorauswahl-regeln","title":"Vorauswahl-Regeln","text":"Die Tracks mit \u2611 wurden nach der Regel aus den Einstellungen automatisch vorausgew\u00e4hlt (selectedByRule: true). Die Auswahl kann frei ge\u00e4ndert werden.
Klicke \"Encode best\u00e4tigen\" um fortzufahren.
"},{"location":"getting-started/quickstart/#schritt-8-encoding-encoding","title":"Schritt 8 \u2013 Encoding \u2192ENCODING","text":"HandBrake startet mit dem finalisierten Plan:
HandBrakeCLI \\\n -i /dev/sr0 \\\n -o \"/mnt/movies/Inception (2010).mkv\" \\\n -t 1 \\\n --preset \"H.265 MKV 1080p30\" \\\n -a 1,2 \\\n -E copy:ac3,av_aac \\\n -s 1 \\\n --subtitle-default 1\n Live-Fortschritt wird aus HandBrake-stderr geparst:
Encoding: task 1 of 1, 73.50 % (45.23 fps, avg 44.12 fps, ETA 00h12m34s)\n Das Dashboard zeigt: - Fortschrittsbalken (0\u2013100 %) - Aktuelle Encoding-Geschwindigkeit (FPS) - Gesch\u00e4tzte Restzeit (ETA)
Typische Dauer (abh\u00e4ngig von CPU/GPU und Preset): - Schnelles Preset (fast): 0.5\u00d7 Echtzeit - Standard-Preset: 1\u20133\u00d7 Echtzeit - Langsames Preset (slow): 5\u201310\u00d7 Echtzeit
FINISHED","text":"/mnt/nas/movies/\n\u2514\u2500\u2500 Inception (2010).mkv \u2713 Encodierung abgeschlossen\n FINISHEDERROR","text":"HandBrakeCLI --preset-list pr\u00fcfen Keine Disc erkannt Laufwerk-Berechtigungen sudo chmod a+rw /dev/sr0 Falsches Video (zerst\u00fcckelt) Falsche Playlist Job re-encodieren mit anderer Playlist OMDb: Keine Ergebnisse API-Key fehlt oder Titel nicht gefunden Einstellungen pr\u00fcfen; manuell eingeben"},{"location":"getting-started/quickstart/#kurzubersicht-aller-schritte","title":"Kurz\u00fcbersicht aller Schritte","text":"# Status Benutzeraktion Was Ripster tut 1 IDLE Disc einlegen Disc-Polling erkennt Disc 2 DISC_DETECTED \"Analyse starten\" klicken Job anlegen, OMDb vorsuchen 3 METADATA_SELECTION Film im Dialog ausw\u00e4hlen Playlist-Analyse durchf\u00fchren 4a READY_TO_START \u2014 Empfehlung automatisch \u00fcbernommen 4b WAITING_FOR_USER_DECISION Playlist manuell w\u00e4hlen Auf Best\u00e4tigung warten 5 READY_TO_START \"Starten\" klicken MakeMKV-Ripping starten 6 RIPPING Warten MakeMKV rippt, Fortschritt streamen 7 MEDIAINFO_CHECK Warten HandBrake-Scan, Encode-Plan bauen 8 READY_TO_ENCODE Tracks pr\u00fcfen + best\u00e4tigen Auswahl in Plan \u00fcbernehmen 9 ENCODING Warten HandBrake encodiert, Fortschritt streamen 10 FINISHED \u2014 Datei fertig, Benachrichtigung senden"},{"location":"pipeline/","title":"Pipeline","text":"Der Pipeline-Abschnitt beschreibt den Kern-Workflow von Ripster.
Workflow & Zust\u00e4nde
Der vollst\u00e4ndige Ripping-Workflow mit allen Zustands\u00fcberg\u00e4ngen.
Workflow
Encode-Planung
Wie Ripster Audio- und Untertitel-Tracks analysiert und Encode-Pl\u00e4ne erstellt.
Encoding
Playlist-Analyse
Erkennung von Blu-ray Playlist-Obfuskierung und Auswahl der korrekten Playlist.
Playlist-Analyse
Post-Encode-Skripte
Automatische Ausf\u00fchrung von Shell-Skripten nach erfolgreichem Encoding \u2013 z. B. zum Verschieben oder Benachrichtigen.
Post-Encode-Skripte
encodePlan.js analysiert die HandBrake-Scan-Ausgabe, w\u00e4hlt Audio- und Untertitelspuren anhand von Regeln vor und erstellt einen vollst\u00e4ndigen Encode-Plan f\u00fcr die Benutzer-Review.
RIPPING abgeschlossen (oder Pre-Rip-Scan)\n \u2193\nHandBrake --scan (alle Titel & Tracks einlesen)\n \u2193\nbuildTrackSelectors() \u2190 Regeln aus Einstellungen ableiten\n \u2193\nselectTrackIds() \u2190 Tracks anhand Regeln vorausw\u00e4hlen\n \u2193\nresolveAudioEncoderAction() \u2190 Encoder-Aktion pro Track bestimmen\n \u2193\nbuildDiscScanReview() \u2190 Vollst\u00e4ndigen Encode-Plan erstellen\n \u2193\nREADY_TO_ENCODE \u2190 Benutzer-Review im Frontend\n \u2193\napplyManualTrackSelectionToPlan() \u2190 Benutzer-Auswahl anwenden\n \u2193\nENCODING \u2190 HandBrake-CLI mit finalem Plan starten\n"},{"location":"pipeline/encoding/#phase-1-pre-rip-track-scan","title":"Phase 1: Pre-Rip Track-Scan","text":"Ripster f\u00fchrt einen HandBrake-Scan bereits vor dem eigentlichen Ripping durch:
HandBrakeCLI --scan -i /dev/sr0 -t 0\n Dieser Scan liest alle Titel und deren Tracks aus der Disc (ohne zu encodieren). So kann der Benutzer die Track-Auswahl bereits vor dem zeitintensiven Rip-Prozess best\u00e4tigen.
Pre-Rip vs. Post-Rip
Ob der Scan vor oder nach dem Ripping passiert, h\u00e4ngt vom konfigurierten Modus ab. Bei direktem Disc-Zugriff ist Pre-Rip m\u00f6glich; nach einem MakeMKV-Backup wird die entstandene .mkv-Datei gescannt.
buildTrackSelectors)","text":"Die Regeln werden aus den HandBrake-Einstellungen abgeleitet. Es gibt f\u00fcnf Selektionsmodi:
Modus Beschreibungnone Keine Tracks dieser Art \u00fcbernehmen first Nur den ersten Track \u00fcbernehmen all Alle Tracks \u00fcbernehmen language Nur Tracks in bestimmten Sprachen explicit Bestimmte Track-IDs explizit angeben Der aktive Modus wird aus den handbrake_*-Einstellungen und handbrake_extra_args abgeleitet. Explizite CLI-Argumente (--audio, --audio-lang-list) \u00fcberschreiben die Basis-Konfiguration.
selectTrackIds)","text":""},{"location":"pipeline/encoding/#audio-tracks","title":"Audio-Tracks","text":"Modus 'none' \u2192 Keine Audio-Tracks\nModus 'all' \u2192 Alle Tracks (oder nur erster, wenn firstOnly)\nModus 'language' \u2192 Alle Tracks in den konfigurierten Sprachen\nModus 'explicit' \u2192 Nur die angegebenen Track-IDs\nModus 'first' \u2192 Nur Track 1\n Jeder Audio-Track erh\u00e4lt das Feld selectedByRule: true/false \u2013 dieses zeigt dem Benutzer, welche Tracks automatisch vorausgew\u00e4hlt wurden.
Sprach-Normalisierung (normalizeLanguage):
Alle Sprachcodes werden auf ISO 639-2 (3-Buchstaben) normalisiert:
Eingabe Normalisiertde, ger deu German deu en, eng eng English eng fr, fre fra ja, jpn jpn Unbekannt und"},{"location":"pipeline/encoding/#untertitel-tracks","title":"Untertitel-Tracks","text":"Gleiche Modus-Logik wie Audio, aber mit zus\u00e4tzlichen Flags pro Track:
Flag BedeutungburnIn Untertitel in Video einbrennen (--subtitle-burned) forced Nur erzwungene Untertitel \u00fcbernehmen (--subtitle-forced) defaultTrack Als Standard-Untertitelspur markieren (--subtitle-default) Diese Flags werden im Encode-Review als Checkboxen angezeigt.
"},{"location":"pipeline/encoding/#phase-4-encoder-aktion-bestimmen-resolveaudioencoderaction","title":"Phase 4: Encoder-Aktion bestimmen (resolveAudioEncoderAction)","text":"F\u00fcr jeden vorausgew\u00e4hlten Audio-Track bestimmt Ripster die Encoder-Aktion:
Encoder-Einstellung Codec-Support in Copy-Mask? Aktion\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nKein Encoder / 'preset-default' \u2192 preset-default HandBrake-Preset entscheidet\nencoder.startsWith('copy')\n UND Codec in audioCopyMask \u2192 copy Direktkopie (verlustfrei)\n UND Codec NICHT in audioCopyMask\u2192 fallback Transcode mit Fallback-Encoder\nsonstiger Encoder \u2192 transcode Transcode mit explizitem Encoder\n Encoder-Aktionstypen:
Typ Label (UI) Qualit\u00e4tpreset-default Preset-Default (HandBrake) HandBrake entscheidet copy Copy (ac3) Verlustfrei fallback Fallback Transcode (av_aac) Mit Qualit\u00e4tsverlust transcode Transcode (av_aac) Mit Qualit\u00e4tsverlust Copy-kompatible Codecs (Standard Copy-Mask):
Codec Encoder-String AC-3copy:ac3 E-AC-3 copy:eac3 AAC copy:aac MP3 copy:mp3 TrueHD copy:truehd DTS copy:dts (nur mit spez. HandBrake-Build) DTS-HD copy:dtshd (nur mit spez. HandBrake-Build) DTS im Standard-HandBrake
Standard-HandBrake-Builds unterst\u00fctzen kein DTS-Passthrough. DTS-Tracks werden dann automatisch auf den Fallback-Encoder umgestellt (Standard: av_aac).
Der vollst\u00e4ndige Plan wird im Job-Datensatz als encode_plan_json gespeichert:
{\n \"mode\": \"pre_rip\",\n \"preRip\": true,\n \"encodeInputTitleId\": 1,\n \"encodeInputPath\": \"disc-track-scan://title-1\",\n \"selectors\": {\n \"audio\": { \"mode\": \"language\", \"languages\": [\"deu\", \"eng\"], \"copyMask\": [\"copy:ac3\", \"copy:eac3\"] },\n \"subtitle\": { \"mode\": \"none\" }\n },\n \"titles\": [\n {\n \"id\": 1,\n \"fileName\": \"Disc Title 1\",\n \"durationSeconds\": 8885,\n \"selectedByMinLength\": true,\n \"isEncodeInput\": true,\n \"audioTracks\": [\n {\n \"id\": 1,\n \"sourceTrackId\": 1,\n \"language\": \"eng\",\n \"languageLabel\": \"English\",\n \"title\": \"5.1 Surround\",\n \"format\": \"AC3\",\n \"codecToken\": \"ac3\",\n \"channels\": \"6\",\n \"selectedByRule\": true,\n \"selectedForEncode\": true,\n \"encodePreviewActions\": [\n { \"type\": \"copy\", \"encoder\": \"copy:ac3\", \"label\": \"Copy (ac3)\" }\n ],\n \"encodePreviewSummary\": \"Copy (ac3)\"\n },\n {\n \"id\": 2,\n \"sourceTrackId\": 2,\n \"language\": \"deu\",\n \"languageLabel\": \"Deutsch\",\n \"format\": \"DTS\",\n \"codecToken\": \"dts\",\n \"channels\": \"6\",\n \"selectedByRule\": true,\n \"selectedForEncode\": true,\n \"encodePreviewActions\": [\n { \"type\": \"fallback\", \"encoder\": \"av_aac\", \"label\": \"Fallback Transcode (av_aac)\" }\n ],\n \"encodePreviewSummary\": \"Fallback Transcode (av_aac)\"\n },\n {\n \"id\": 3,\n \"language\": \"fra\",\n \"languageLabel\": \"Fran\u00e7ais\",\n \"selectedByRule\": false,\n \"selectedForEncode\": false,\n \"encodePreviewSummary\": \"Nicht \u00fcbernommen\"\n }\n ],\n \"subtitleTracks\": [\n {\n \"id\": 1,\n \"language\": \"deu\",\n \"selectedByRule\": true,\n \"selectedForEncode\": true,\n \"burnIn\": false,\n \"forced\": false,\n \"defaultTrack\": true,\n \"subtitlePreviewSummary\": \"\u00dcbernehmen\",\n \"subtitlePreviewFlags\": [\"default\"]\n }\n ]\n }\n ]\n}\n"},{"location":"pipeline/encoding/#phase-6-benutzer-review-im-frontend-mediainforeviewpanel","title":"Phase 6: Benutzer-Review im Frontend (MediaInfoReviewPanel)","text":"Das Review-Panel zeigt:
\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Encode-Review Titel: Disc Title 1 \u2502\n\u2502 Laufzeit: 2:28:05 \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Audio-Spuren \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 [\u2713] \u2502 Track 1: English (AC3) \u2502 Copy (ac3) \u2502\n\u2502 [\u2713] \u2502 Track 2: Deutsch (DTS) \u2502 Fallback Transcode (av_aac) \u2502\n\u2502 [ ] \u2502 Track 3: Fran\u00e7ais (DTS) \u2502 Nicht \u00fcbernommen \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Untertitel-Spuren \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 [\u2713] \u2502 Track 1: Deutsch \u2502Einbr.[ ]\u2502Forced[ ]\u2502Default[\u2713]\u2502\n\u2502 [ ] \u2502 Track 2: English \u2502Einbr.[ ]\u2502Forced[ ]\u2502Default[ ]\u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 [Encode best\u00e4tigen] \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n Der Benutzer kann: - Audio-Tracks per Checkbox aktivieren/deaktivieren - Untertitel-Flags (Einbrennen, Forced, Default) setzen - Mehrere Titel bei der Titleauswahl wechseln (f\u00fcr Discs mit mehreren Haupttiteln)
"},{"location":"pipeline/encoding/#phase-7-benutzer-auswahl-anwenden-applymanualtrackselectiontoplan","title":"Phase 7: Benutzer-Auswahl anwenden (applyManualTrackSelectionToPlan)","text":"Nach \"Encode best\u00e4tigen\" wird die Benutzer-Auswahl auf den Plan angewendet:
Payload: {\n \"selectedEncodeTitleId\": 1,\n \"selectedTrackSelection\": {\n \"1\": {\n \"audioTrackIds\": [1, 2],\n \"subtitleTrackIds\": [1]\n }\n }\n}\n Jeder Track erh\u00e4lt selectedForEncode: true/false entsprechend der Auswahl. Die Encoder-Aktionen (encodeActions) der nicht gew\u00e4hlten Tracks werden geleert.
Aus dem finalisierten Plan baut Ripster den HandBrake-Aufruf:
HandBrakeCLI \\\n -i /dev/sr0 \\\n -o \"/mnt/movies/Inception (2010).mkv\" \\\n -t 1 \\\n --preset \"H.265 MKV 1080p30\" \\\n -a 1,2 \\\n -E copy:ac3,av_aac \\\n -s 1 \\\n --subtitle-default 1\n Argument Quelle -i encode_input_path aus Job -o Ausgabepfad aus filename_template + movie_dir -t Gew\u00e4hlter Titel-Index -a Kommagetrennte Audio-Track-IDs der ausgew\u00e4hlten Tracks -E Kommagetrennte Encoder-Aktionen (eine pro Track, gleiche Reihenfolge wie -a) -s Kommagetrennte Untertitel-Track-IDs --subtitle-default Track-ID der als Default markierten Untertitelspur --preset handbrake_preset-Einstellung Extras handbrake_extra_args-Einstellung"},{"location":"pipeline/encoding/#dateiname-template","title":"Dateiname-Template","text":"Platzhalter Wert Beispiel {title} Filmtitel von OMDb Inception {year} Erscheinungsjahr 2010 {imdb_id} IMDb-ID tt1375666 {type} movie oder series movie Sonderzeichen (:, /, ?, * etc.) werden automatisch aus dem Dateinamen entfernt.
Ein abgeschlossener Job kann ohne erneutes Ripping neu encodiert werden:
handbrake_*-EinstellungenN\u00fctzlich bei ge\u00e4nderten Presets, anderen Sprach-Pr\u00e4ferenzen oder nach einem Einstellungs-Update.
"},{"location":"pipeline/playlist-analysis/","title":"Playlist-Analyse","text":"Einige Blu-rays verwenden Playlist-Obfuskierung als Kopierschutz. Ripster analysiert automatisch alle MakeMKV-Titel und empfiehlt die korrekte Playlist \u2013 auf Basis eines Segment-Scoring-Algorithmus aus playlistAnalysis.js.
Moderne Blu-rays k\u00f6nnen Dutzende bis Hunderte von Titeln/Playlists enthalten. Der eigentliche Film steckt in genau einer davon \u2013 alle anderen sind:
Das Ziel der Obfuskierung: Ein einfacher Ripper w\u00e4hlt den erstbesten langen Titel \u2013 und bekommt ein zerst\u00fcckeltes, unbrauchbares Video.
"},{"location":"pipeline/playlist-analysis/#wann-wird-die-analyse-ausgelost","title":"Wann wird die Analyse ausgel\u00f6st?","text":"Die Playlist-Analyse wird automatisch gestartet sobald der Benutzer Metadaten best\u00e4tigt (nach dem Metadaten-Dialog). Ripster ruft makemkvcon im Info-Modus auf und parst die TINFO-Ausgabe.
TINFO:<titleId>,26,\"<segment-list>\"\n Feld 26 enth\u00e4lt die kommagetrennte Liste der Segment-Nummern in der Abspielreihenfolge des Titels.
"},{"location":"pipeline/playlist-analysis/#algorithmus-im-detail-playlistanalysisjs","title":"Algorithmus im Detail (playlistAnalysis.js)","text":""},{"location":"pipeline/playlist-analysis/#schritt-1-segment-nummern-parsen","title":"Schritt 1 \u2013 Segment-Nummern parsen","text":"TINFO:1,26,\"00000,00001,00002,00003\" \u2192 [0, 1, 2, 3] linearer Film\nTINFO:2,26,\"00100,00050,00100,00051\" \u2192 [100, 50, 100, 51] Fake-Playlist\n"},{"location":"pipeline/playlist-analysis/#schritt-2-metriken-berechnen-computesegmentmetrics","title":"Schritt 2 \u2013 Metriken berechnen (computeSegmentMetrics)","text":"F\u00fcr jedes aufeinanderfolgende Segment-Paar [a, b] wird diff = b \u2212 a berechnet:
directSequenceSteps diff == 1 Aufeinanderfolgende Segmente \u2192 linearer Film backwardJumps b < a R\u00fcckw\u00e4rtsspr\u00fcnge \u2192 verd\u00e4chtig largeJumps \\|diff\\| > 20 Gro\u00dfe Spr\u00fcnge \u2192 verd\u00e4chtig alternatingPairs Gro\u00dfe Spr\u00fcnge mit wechselndem Vorzeichen Hin-und-her-Muster \u2192 starker Fake-Indikator Score-Formel:
score = (directSequenceSteps \u00d7 2) \u2212 (backwardJumps \u00d7 3) \u2212 (largeJumps \u00d7 2)\n Konkrete Beispiele:
Segmentfolge directSeq backward large score Ergebnis0,1,2,3,4,5 5 0 0 +10 Echter Film 0,1,100,2,101,3 2 0 4 -4 Verd\u00e4chtig 50,10,60,11,70,12 0 3 3 -15 Fake"},{"location":"pipeline/playlist-analysis/#schritt-3-bewertungslabel-vergeben-buildevaluationlabel","title":"Schritt 3 \u2013 Bewertungslabel vergeben (buildEvaluationLabel)","text":"alternatingRatio = alternatingPairs / largeJumps\n\nif alternatingRatio >= 0.55 AND alternatingPairs >= 3:\n \u2192 \"Fake-Struktur (alternierendes Sprungmuster)\"\n\nelse if backwardJumps > 0 OR largeJumps > 0:\n \u2192 \"Auff\u00e4llige Segmentreihenfolge\"\n\nelse:\n \u2192 \"wahrscheinlich korrekt (lineare Segmentfolge)\"\n"},{"location":"pipeline/playlist-analysis/#schritt-4-duplikat-gruppen-bilden-buildsimilaritygroups","title":"Schritt 4 \u2013 Duplikat-Gruppen bilden (buildSimilarityGroups)","text":"Alle Titel werden nach \u00e4hnlicher Laufzeit gruppiert (\u00b190 Sekunden Toleranz). Gibt es mehrere Kandidaten mit \u00e4hnlicher Laufzeit, ist das ein klares Zeichen f\u00fcr Obfuskierung:
8 Titel mit ~148 Minuten Laufzeit \u2192 Duplikat-Gruppe\n\u2192 obfuscationDetected = true\n"},{"location":"pipeline/playlist-analysis/#schritt-5-besten-kandidaten-empfehlen-scorecandidates","title":"Schritt 5 \u2013 Besten Kandidaten empfehlen (scoreCandidates)","text":"Innerhalb der gr\u00f6\u00dften Duplikat-Gruppe werden alle Kandidaten sortiert nach:
score (h\u00f6her = besser)sequenceCoherence (Anteil linearer Segmentschritte)Der erste Kandidat der sortierten Liste ist die Empfehlung.
"},{"location":"pipeline/playlist-analysis/#schritt-6-entscheidung-erzwingen-bei-mehreren-kandidaten","title":"Schritt 6 \u2013 Entscheidung erzwingen bei mehreren Kandidaten","text":"Sobald nach MIN_LENGTH_MINUTES mehr als eine Playlist \u00fcbrig bleibt, wird immer eine manuelle Auswahl verlangt:
candidateCount > 1 \u2192 manualDecisionRequired = true\ncandidateCount <= 1 \u2192 manualDecisionRequired = false\n"},{"location":"pipeline/playlist-analysis/#wann-greift-der-benutzer-ein","title":"Wann greift der Benutzer ein?","text":"obfuscationDetected = duplicateDurationGroups.length > 0\nmanualDecisionRequired = candidates.length > 1\n Ergebnis N\u00e4chster Pipeline-Zustand Aktion Nur ein Kandidat nach Mindestl\u00e4nge READY_TO_START Automatische \u00dcbernahme m\u00f6glich Mehrere Kandidaten nach Mindestl\u00e4nge WAITING_FOR_USER_DECISION Benutzer muss Playlist ausw\u00e4hlen"},{"location":"pipeline/playlist-analysis/#benutzeroberflache-playlist-auswahl-dialog","title":"Benutzeroberfl\u00e4che: Playlist-Auswahl-Dialog","text":"Wenn manualDecisionRequired = true, \u00f6ffnet sich der Playlist-Dialog nach dem Metadaten-Dialog:
\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Playlist-Auswahl \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Playlist \u2502 Laufzeit \u2502 Score \u2502 Bewertung \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 \u2605 00800 \u2502 2:28:05 \u2502 +18 \u2502 wahrscheinlich korrekt \u2502\n\u2502 \u2502 \u2502 \u2502 (lineare Segmentfolge) \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 00801 \u2502 2:28:12 \u2502 \u22124 \u2502 Auff\u00e4llige Segmentreihenfolge \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 00900 \u2502 2:28:05 \u2502 \u221232 \u2502 Fake-Struktur \u2502\n\u2502 \u2502 \u2502 \u2502 (alternierendes Sprungmuster) \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n Hinweis: 847 Playlists insgesamt. 3 relevante Kandidaten (\u2265 15 min).\n Empfehlung: 00800 (\u2605)\n makemkv_min_length_minutes erscheinen in der ListeREADY_TO_STARTanalyzeContext.playlistAnalysis)","text":"{\n \"titles\": [\n { \"titleId\": 1, \"playlistId\": \"00800\", \"durationSeconds\": 8885, \"durationLabel\": \"2:28:05\", \"chapters\": 28 }\n ],\n \"candidates\": [\n { \"titleId\": 1, \"playlistId\": \"00800\", \"durationSeconds\": 8885 },\n { \"titleId\": 2, \"playlistId\": \"00801\", \"durationSeconds\": 8892 }\n ],\n \"evaluatedCandidates\": [\n {\n \"titleId\": 1,\n \"playlistId\": \"00800\",\n \"score\": 18,\n \"sequenceCoherence\": 0.95,\n \"evaluationLabel\": \"wahrscheinlich korrekt (lineare Segmentfolge)\",\n \"metrics\": {\n \"directSequenceSteps\": 12,\n \"backwardJumps\": 0,\n \"largeJumps\": 1,\n \"alternatingPairs\": 0\n }\n }\n ],\n \"duplicateDurationGroups\": [\n [\n { \"titleId\": 1, \"playlistId\": \"00800\" },\n { \"titleId\": 2, \"playlistId\": \"00801\" }\n ]\n ],\n \"recommendation\": {\n \"titleId\": 1,\n \"playlistId\": \"00800\",\n \"score\": 18,\n \"reason\": \"H\u00f6chster Segment-Score in der gr\u00f6\u00dften Laufzeit-Gruppe\"\n },\n \"obfuscationDetected\": true,\n \"manualDecisionRequired\": true\n}\n"},{"location":"pipeline/playlist-analysis/#konfiguration","title":"Konfiguration","text":"Einstellung Standard Wirkung makemkv_min_length_minutes 15 Titel k\u00fcrzer als dieser Wert werden als Kandidaten ignoriert"},{"location":"pipeline/playlist-analysis/#tipps-bei-fehlempfehlung","title":"Tipps bei Fehlempfehlung","text":"Falsche Playlist gew\u00e4hlt?
Wenn das resultierende Video zerst\u00fcckelt ist:
Keine Segment-Daten verf\u00fcgbar
Bei DVDs oder \u00e4lteren Blu-rays liefert MakeMKV manchmal keine Segmentinfos (TINFO-Feld 26 fehlt). In diesem Fall entf\u00e4llt die Analyse und der erste Titel \u00fcber der Mindestl\u00e4nge wird automatisch verwendet.
"},{"location":"pipeline/post-encode-scripts/","title":"Post-Encode-Skripte","text":"Post-Encode-Skripte erm\u00f6glichen es, nach erfolgreichem Encoding automatisch beliebige Shell-Befehle oder Programme auszuf\u00fchren \u2013 z. B. zum Verschieben von Dateien, Benachrichtigen externer Dienste oder Ausl\u00f6sen weiterer Verarbeitungsschritte.
"},{"location":"pipeline/post-encode-scripts/#funktionsweise","title":"Funktionsweise","text":"Nach einem erfolgreich abgeschlossenen Encoding-Schritt f\u00fchrt Ripster die konfigurierten Skripte sequenziell in der festgelegten Reihenfolge aus:
ENCODING abgeschlossen\n \u2193\nSkript 1 ausf\u00fchren \u2190 Fehler? \u2192 Abbruch\n \u2193\nSkript 2 ausf\u00fchren \u2190 Fehler? \u2192 Abbruch\n \u2193\n ...\n \u2193\nFINISHED\n Abbruch bei Fehler
Schl\u00e4gt ein Skript fehl (Exit-Code \u2260 0), werden alle nachfolgenden Skripte nicht mehr ausgef\u00fchrt. Der Job wechselt in den Status ERROR.
Skripte werden \u00fcber die Einstellungen-Seite angelegt und verwaltet. Sie stehen danach in jedem Encode-Review zur Auswahl.
"},{"location":"pipeline/post-encode-scripts/#skript-anlegen","title":"Skript anlegen","text":"Navigiere zu Einstellungen \u2192 Skripte und klicke \"Neues Skript\":
Feld Beschreibung Name Anzeigename des Skripts (z. B.Zu Plex verschieben) Befehl Shell-Befehl oder Skriptpfad (z. B. /home/michael/scripts/move-to-plex.sh) Beschreibung Optionale Erkl\u00e4rung"},{"location":"pipeline/post-encode-scripts/#verfugbare-umgebungsvariablen","title":"Verf\u00fcgbare Umgebungsvariablen","text":"Jedes Skript wird mit folgenden Umgebungsvariablen aufgerufen:
Variable Inhalt BeispielRIPSTER_OUTPUT_PATH Absoluter Pfad der encodierten Datei /mnt/movies/Inception (2010).mkv RIPSTER_JOB_ID Job-ID in der Datenbank 42 RIPSTER_TITLE Filmtitel Inception RIPSTER_YEAR Erscheinungsjahr 2010 RIPSTER_IMDB_ID IMDb-ID tt1375666 RIPSTER_RAW_PATH Pfad zur Raw-MKV-Datei /mnt/raw/Inception-2010/t00.mkv"},{"location":"pipeline/post-encode-scripts/#beispiel-skript-datei-nach-jellyfin-verschieben","title":"Beispiel-Skript: Datei nach Jellyfin verschieben","text":"#!/bin/bash\n# /home/michael/scripts/move-to-jellyfin.sh\n\nTARGET_DIR=\"/mnt/media/movies\"\nmkdir -p \"$TARGET_DIR\"\nmv \"$RIPSTER_OUTPUT_PATH\" \"$TARGET_DIR/\"\necho \"Verschoben: $RIPSTER_TITLE nach $TARGET_DIR\"\n"},{"location":"pipeline/post-encode-scripts/#beispiel-skript-webhook-auslosen","title":"Beispiel-Skript: Webhook ausl\u00f6sen","text":"#!/bin/bash\n# /home/michael/scripts/notify-webhook.sh\n\ncurl -s -X POST https://mein-webhook.example.com/ripster \\\n -H \"Content-Type: application/json\" \\\n -d \"{\\\"title\\\": \\\"$RIPSTER_TITLE\\\", \\\"year\\\": \\\"$RIPSTER_YEAR\\\", \\\"path\\\": \\\"$RIPSTER_OUTPUT_PATH\\\"}\"\n"},{"location":"pipeline/post-encode-scripts/#skript-im-encode-review-auswahlen","title":"Skript im Encode-Review ausw\u00e4hlen","text":"Im READY_TO_ENCODE-Zustand zeigt das MediaInfoReviewPanel einen Skript-Abschnitt:
\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Post-Encode-Skripte \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Ausgew\u00e4hlte Skripte (Reihenfolge per Drag & Drop): \u2502\n\u2502 \u2261 1. Zu Plex verschieben [Entfernen]\u2502\n\u2502 \u2261 2. Webhook ausl\u00f6sen [Entfernen]\u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Skript hinzuf\u00fcgen: [Zu Jellyfin verschieben \u25be] [+ Hinzuf.]\u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n \u00dcber die Einstellungen kann jedes Skript mit einem Test-Job ausgef\u00fchrt werden:
POST /api/settings/scripts/:scriptId/test\n Der Test-Aufruf bef\u00fcllt die Umgebungsvariablen mit Platzhalter-Werten.
"},{"location":"pipeline/post-encode-scripts/#ausfuhrungs-ergebnis","title":"Ausf\u00fchrungs-Ergebnis","text":"Das Ergebnis der Skript-Ausf\u00fchrung wird im Job-Datensatz gespeichert und in der History angezeigt:
{\n \"postEncodeScripts\": {\n \"configured\": 2,\n \"succeeded\": 2,\n \"failed\": 0,\n \"skipped\": 0,\n \"aborted\": false,\n \"results\": [\n {\n \"scriptId\": \"script-1\",\n \"name\": \"Zu Plex verschieben\",\n \"exitCode\": 0,\n \"stdout\": \"Verschoben: Inception nach /mnt/media/movies\",\n \"stderr\": \"\",\n \"durationMs\": 342\n },\n {\n \"scriptId\": \"script-2\",\n \"name\": \"Webhook ausl\u00f6sen\",\n \"exitCode\": 0,\n \"stdout\": \"\",\n \"stderr\": \"\",\n \"durationMs\": 128\n }\n ]\n }\n}\n Feld Beschreibung configured Anzahl ausgew\u00e4hlter Skripte succeeded Erfolgreich ausgef\u00fchrt (Exit-Code 0) failed Fehlgeschlagen skipped Nicht ausgef\u00fchrt (wegen vorherigem Fehler) aborted true, wenn die Kette abgebrochen wurde"},{"location":"pipeline/post-encode-scripts/#api-referenz","title":"API-Referenz","text":"Eine vollst\u00e4ndige API-Dokumentation der Skript-Endpunkte findest du unter:
Settings API \u2013 Skripte
"},{"location":"pipeline/workflow/","title":"Workflow & Zust\u00e4nde","text":"Der Ripping-Workflow von Ripster ist als State Machine implementiert. Jeder Zustand hat klar definierte \u00dcbergangsbedingungen und Aktionen.
"},{"location":"pipeline/workflow/#zustandsdiagramm","title":"Zustandsdiagramm","text":"flowchart LR\n START(( )) --> IDLE\n\n IDLE -->|Disc erkannt| DD[DISC_DETECTED]\n DD -->|Analyse starten| META[METADATA\\nSELECTION]\n\n META -->|1 Kandidat| RTS[READY_TO\\nSTART]\n META -->|mehrere Kandidaten| WUD[WAITING_FOR\\nUSER_DECISION]\n WUD -->|Playlist best\u00e4tigt| RTS\n\n RTS -->|Raw vorhanden| MIC[MEDIAINFO\\nCHECK]\n RTS -->|Ripping starten| RIP[RIPPING]\n RIP -->|MKV fertig| MIC\n RIP -->|Fehler| ERR\n\n MIC --> RTE[READY_TO\\nENCODE]\n RTE -->|Tracks + Skripte\\nbest\u00e4tigt| ENC[ENCODING]\n\n ENC -->|mit Skripten| PES[POST_ENCODE\\nSCRIPTS]\n ENC -->|ohne Skripte| FIN([FINISHED])\n ENC -->|Fehler| ERR\n\n PES -->|Erfolg| FIN\n PES -->|Fehler| ERR\n\n ERR([ERROR]) -->|Retry / Cancel| IDLE\n FIN -->|Neue Disc| IDLE\n\n style FIN fill:#e8f5e9,stroke:#66bb6a,color:#2e7d32\n style ERR fill:#ffebee,stroke:#ef5350,color:#c62828\n style WUD fill:#fff8e1,stroke:#ffa726,color:#e65100\n style PES fill:#f3e5f5,stroke:#ab47bc,color:#6a1b9a\n style ENC fill:#f3e5f5,stroke:#ab47bc,color:#6a1b9a\n style RIP fill:#e3f2fd,stroke:#42a5f5,color:#1565c0\n style MIC fill:#e3f2fd,stroke:#42a5f5,color:#1565c0"},{"location":"pipeline/workflow/#zustandsbeschreibungen","title":"Zustandsbeschreibungen","text":""},{"location":"pipeline/workflow/#idle","title":"IDLE","text":"Ausgangszustand. Ripster wartet auf eine Disc.
diskDetectionService pollt das Laufwerk im konfigurierten IntervallDISC_DETECTEDDISC_DETECTEDDisc erkannt, wartet auf Benutzeraktion.
\u00dcbergang: Benutzer klickt \"Analyse starten\" \u2192 METADATA_SELECTION
Disc-Info-Scan l\u00e4uft, danach Benutzer-Eingabe.
MetadataSelectionDialog \u00f6ffnet sich mit vorgeladenen Ergebnissen\u00dcbergang (automatisch nach Playlist-Analyse):
Ergebnis der Analyse N\u00e4chster Zustand Nur ein Kandidat nach Mindestl\u00e4ngeREADY_TO_START Mehrere Kandidaten nach Mindestl\u00e4nge WAITING_FOR_USER_DECISION"},{"location":"pipeline/workflow/#waiting_for_user_decision","title":"WAITING_FOR_USER_DECISION","text":"Playlist-Obfuskierung erkannt \u2013 manuelle Auswahl erforderlich.
Neu seit \u201eSkript Integration + UI Anpassungen\"
Dieser Zustand wurde eingef\u00fchrt, um Blu-rays mit mehreren Playlists \u00e4hnlicher L\u00e4nge korrekt zu behandeln.
Darstellung im Dashboard:
\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Playlist-Auswahl erforderlich \u2502\n\u2502 Es wurden mehrere Titel mit \u00e4hnlicher Laufzeit gefunden. \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Playlist \u2502 Laufzeit \u2502 Score \u2502 Bewertung \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 \u25cf 00800 \u2502 2:28:05 \u2502 +18 \u2502 wahrscheinlich korrekt \u2502\n\u2502 \u25cb 00801 \u2502 2:28:12 \u2502 \u22124 \u2502 Auff\u00e4llige Segmentfolge \u2502\n\u2502 \u25cb 00900 \u2502 2:28:05 \u2502 \u221232 \u2502 Fake-Struktur \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n [Playlist \u00fcbernehmen]\n \u00dcbergang: selectMetadata(jobId, { selectedPlaylist }) \u2192 READY_TO_START
Mehr Details: Playlist-Analyse
"},{"location":"pipeline/workflow/#ready_to_start","title":"READY_TO_START","text":"Metadaten und Playlist best\u00e4tigt, bereit zum Starten.
Sonderfall \u2013 Raw-Datei bereits vorhanden: Wenn f\u00fcr diesen Job bereits eine geri rippte Raw-Datei im raw_dir existiert (Pfad-Match \u00fcber Metadaten-Basis), \u00fcberspringt Ripster den Ripping-Schritt und springt direkt zum HandBrake-Scan.
\u00dcbergang: startJob(jobId) \u2192 RIPPING oder direkt MEDIAINFO_CHECK
MakeMKV rippt die Disc.
MKV-Modus (Standard)Backup-Modusmakemkvcon mkv disc:0 all /path/to/raw/ --minlength=900 -r\n Erstellt MKV-Datei(en) direkt aus den gew\u00e4hlten Titeln.
makemkvcon backup disc:0 /path/to/raw/backup/ --decrypt -r\n Erstellt vollst\u00e4ndiges Disc-Backup inkl. Men\u00fcs.
Live-Updates aus MakeMKV-Ausgabe:
PRGV:2048,0,65536 \u2192 Fortschritt-Berechnung\nPRGT:5011,0,\"...\" \u2192 Aktueller Task-Name\n Typische Dauer: DVD 20\u201345 min \u00b7 Blu-ray 45\u2013120 min
"},{"location":"pipeline/workflow/#mediainfo_check","title":"MEDIAINFO_CHECK","text":"HandBrake-Scan und Encode-Plan-Erstellung.
Dieser Zustand umfasst zwei Phasen:
HandBrakeCLI --scan) auf Disc oder Raw-DateiKein Benutzereingriff \u2013 l\u00e4uft automatisch durch.
\u00dcbergang: \u2192 READY_TO_ENCODE
Encode-Plan bereit, wartet auf Benutzer-Best\u00e4tigung.
Das MediaInfoReviewPanel zeigt:
\u00dcbergang: confirmEncodeReview(jobId, { tracks, scripts }) \u2192 ENCODING
HandBrake encodiert die Datei.
HandBrakeCLI \\\n -i <quelle> -o <ziel> \\\n -t <titelId> \\\n --preset \"H.265 MKV 1080p30\" \\\n -a 1,2 -E copy:ac3,av_aac \\\n -s 1 --subtitle-default 1\n Live-Updates aus HandBrake-stderr:
Encoding: task 1 of 1, 73.50 % (45.23 fps, avg 44.12 fps, ETA 00h12m34s)\n"},{"location":"pipeline/workflow/#post_encode_scripts","title":"POST_ENCODE_SCRIPTS","text":"Post-Encode-Skripte werden ausgef\u00fchrt.
Neu seit \u201eSkript Integration + UI Anpassungen\"
Post-Encode-Skripte erm\u00f6glichen es, nach erfolgreichem Encoding automatisch Aktionen auszuf\u00fchren.
{\n \"configured\": 2,\n \"succeeded\": 2,\n \"failed\": 0,\n \"skipped\": 0,\n \"aborted\": false\n}\n Dieser Zustand wird nur erreicht, wenn im Encode-Review mindestens ein Skript ausgew\u00e4hlt wurde.
\u00dcbergang: \u2192 FINISHED (alle Skripte erfolgreich) oder ERROR (Skript-Fehler)
Details: Post-Encode-Skripte
"},{"location":"pipeline/workflow/#finished","title":"FINISHED","text":"Job erfolgreich abgeschlossen.
movie_dirFINISHEDJOB_COMPLETEFehler aufgetreten.
POST /api/pipeline/cancel\n POST /api/pipeline/retry/:jobId\n READY_TO_STARTPOST /api/pipeline/reencode/:jobId\n Ripster ist ein Orchestrator \u2013 die eigentliche Arbeit erledigen diese bew\u00e4hrten Open-Source-Tools:
MakeMKV
Disc-Analyse und Ripping. Erstellt MKV-Dateien oder vollst\u00e4ndige Backups.
MakeMKV
HandBrake
Video-Encoding mit umfangreichen Preset-Optionen.
HandBrake
MediaInfo
Analyse von Track-Informationen in Mediendateien.
MediaInfo
HandBrake encodiert die rohen MKV-Dateien in das gew\u00fcnschte Format. Ripster nutzt HandBrakeCLI.
HandBrakeCLI \\\n --input \"/mnt/raw/Film_t00.mkv\" \\\n --output \"/mnt/movies/Film (2010).mkv\" \\\n --preset \"H.265 MKV 1080p30\" \\\n --audio 1,2 \\\n --aencoder copy:ac3,ffaac \\\n --subtitle 1 \\\n --subtitle-default 1\n"},{"location":"tools/handbrake/#presets","title":"Presets","text":"HandBrake verwendet Presets f\u00fcr vorkonfigurierte Encoding-Einstellungen.
"},{"location":"tools/handbrake/#empfohlene-presets","title":"Empfohlene Presets","text":"Preset Codec Aufl\u00f6sung F\u00fcrH.265 MKV 1080p30 HEVC/H.265 1080p Beste Qualit\u00e4t/Gr\u00f6\u00dfe H.265 MKV 720p30 HEVC/H.265 720p Kleinere Dateien H.264 MKV 1080p30 AVC/H.264 1080p Breiteste Kompatibilit\u00e4t HQ 1080p30 Surround HEVC/H.265 1080p Hohe Qualit\u00e4t mit Surround"},{"location":"tools/handbrake/#alle-presets-anzeigen","title":"Alle Presets anzeigen","text":"HandBrakeCLI --preset-list\n"},{"location":"tools/handbrake/#audio-encoding","title":"Audio-Encoding","text":""},{"location":"tools/handbrake/#copy-kompatible-codecs","title":"Copy-kompatible Codecs","text":"HandBrake kann folgende Codecs direkt kopieren (kein Qualit\u00e4tsverlust):
Codec--aencoder Wert AC-3 copy:ac3 AAC copy:aac MP3 copy:mp3 TrueHD copy:truehd E-AC-3 copy:eac3"},{"location":"tools/handbrake/#transcoding","title":"Transcoding","text":"Codecs die nicht kopiert werden k\u00f6nnen, werden zu AAC transcodiert:
Original Transcodiert zu DTS AAC (ffaac) DTS-HD AAC (ffaac)"},{"location":"tools/handbrake/#extra-argumente","title":"Extra-Argumente","text":"\u00dcber die Einstellung handbrake_extra_args k\u00f6nnen beliebige HandBrake-Argumente hinzugef\u00fcgt werden:
# Cropping deaktivieren\n--crop 0:0:0:0\n\n# Loose Anamorphic\n--loose-anamorphic\n\n# Bestimmte Qualit\u00e4t setzen\n--quality 20\n"},{"location":"tools/handbrake/#fortschritts-parsing","title":"Fortschritts-Parsing","text":"Ripster parst die HandBrake-Ausgabe auf stderr f\u00fcr die Fortschrittsanzeige:
Encoding: task 1 of 1, 73.50 % (45.23 fps, avg 44.12 fps, ETA 00h12m34s)\n progressParsers.js extrahiert: - Prozentzahl - Aktuelle FPS - ETA
handbrake_command Pfad/Befehl f\u00fcr HandBrakeCLI handbrake_preset Preset-Name handbrake_extra_args Zus\u00e4tzliche CLI-Argumente output_extension Dateiendung der Ausgabe"},{"location":"tools/handbrake/#troubleshooting","title":"Troubleshooting","text":""},{"location":"tools/handbrake/#handbrake-findet-preset-nicht","title":"HandBrake findet Preset nicht","text":"# Preset-Liste anzeigen\nHandBrakeCLI --preset-list 2>&1 | grep -i \"h.265\"\n Preset-Namen sind case-sensitive!
"},{"location":"tools/handbrake/#encoding-sehr-langsam","title":"Encoding sehr langsam","text":"# CPU-Encoding-Preset anpassen (schneller = schlechtere Qualit\u00e4t)\nhandbrake_extra_args = --encoder-preset fast\n Verf\u00fcgbare Presets: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow
handbrake_preset = H.265 NVENC 1080p\n Erfordert HandBrake-Build mit NVENC-Unterst\u00fctzung und NVIDIA-GPU.
"},{"location":"tools/makemkv/","title":"MakeMKV","text":"MakeMKV analysiert und rippt DVDs und Blu-rays. Ripster nutzt makemkvcon (die CLI-Version).
makemkvcon -r --cache=1 info disc:0\n Gibt alle Titel und Playlists der eingelegten Disc aus. Ripster parst diese Ausgabe um die verf\u00fcgbaren Tracks und Playlists zu bestimmen.
Parameter: - -r \u2013 Maschinen-lesbares Ausgabeformat - --cache=1 \u2013 Minimaler Disc-Cache - info disc:0 \u2013 Informationsabfrage f\u00fcr erstes Laufwerk
makemkvcon mkv disc:0 all /path/to/raw/ \\\n --minlength=900 \\\n -r\n Erstellt MKV-Dateien aus allen Titeln, die l\u00e4nger als 15 Minuten sind.
Parameter: - mkv \u2013 MKV-Ausgabemodus - disc:0 \u2013 Erstes Disc-Laufwerk - all \u2013 Alle passenden Titel (nicht nur einen bestimmten) - --minlength=900 \u2013 Mindestl\u00e4nge in Sekunden (entspricht 15 Minuten)
makemkvcon backup disc:0 /path/to/raw/backup/ \\\n --decrypt \\\n -r\n Erstellt ein vollst\u00e4ndiges Disc-Backup mit Men\u00fcs.
Parameter: - backup \u2013 Backup-Modus - --decrypt \u2013 Verschl\u00fcsselung entfernen
MakeMKV gibt Fortschritt und Status in einem strukturierten Format aus:
PRGV:current,total,max \u2192 Fortschrittsbalken-Werte\nPRGT:code,id,\"Beschreibung\" \u2192 Aktueller Task\nPRGC:code,id,\"Beschreibung\" \u2192 Aktueller Sub-Task\nMSG:code,flags,count,\"Text\" \u2192 Nachricht\n Ripster's progressParsers.js parst diese Ausgabe f\u00fcr die Live-Fortschrittsanzeige.
MakeMKV ist Beta-Software und kostenlos f\u00fcr den pers\u00f6nlichen Gebrauch w\u00e4hrend der Beta-Phase. Eine Beta-Lizenz ist regelm\u00e4\u00dfig im MakeMKV-Forum verf\u00fcgbar.
Ohne g\u00fcltige Lizenz k\u00f6nnen Blu-rays nicht entschl\u00fcsselt werden.
"},{"location":"tools/makemkv/#lizenz-eintragen","title":"Lizenz eintragen","text":"Die Lizenz wird in den MakeMKV-Einstellungen eingetragen (GUI) oder direkt in:
~/.MakeMKV/settings.conf\n app_Key = \"XXXX-XXXX-XXXX-XXXX-XXXX\"\n"},{"location":"tools/makemkv/#konfiguration-in-ripster","title":"Konfiguration in Ripster","text":"Einstellung Beschreibung makemkv_command Pfad/Befehl f\u00fcr makemkvcon makemkv_min_length_minutes Mindest-Titell\u00e4nge (Standard: 15 Min) makemkv_backup_mode Backup-Modus statt MKV"},{"location":"tools/makemkv/#troubleshooting","title":"Troubleshooting","text":""},{"location":"tools/makemkv/#makemkv-erkennt-disc-nicht","title":"MakeMKV erkennt Disc nicht","text":"# Laufwerk-Berechtigungen pr\u00fcfen\nls -la /dev/sr0\nsudo chmod a+rw /dev/sr0\n\n# Oder Benutzer zur Gruppe cdrom hinzuf\u00fcgen\nsudo usermod -a -G cdrom $USER\n"},{"location":"tools/makemkv/#langer-analyseprozess","title":"Langer Analyseprozess","text":"Blu-ray-Analyse kann bei Discs mit vielen Playlists 5+ Minuten dauern. Dies ist normal.
"},{"location":"tools/makemkv/#fehlermeldung-libmmbd","title":"Fehlermeldung: \"LibMMBD\"","text":"LibMMBD ist MakeMKVs interne Verschl\u00fcsselungsbibliothek. Bei Fehlern die MakeMKV-Version aktualisieren.
"},{"location":"tools/mediainfo/","title":"MediaInfo","text":"MediaInfo analysiert die Track-Struktur von Mediendateien. Ripster nutzt es nach dem Ripping um Audio- und Untertitelspuren zu identifizieren.
"},{"location":"tools/mediainfo/#verwendeter-befehl","title":"Verwendeter Befehl","text":"mediainfo --Output=JSON /path/to/raw/film.mkv\n Gibt vollst\u00e4ndige Track-Informationen als JSON zur\u00fcck.
"},{"location":"tools/mediainfo/#ausgabe-struktur","title":"Ausgabe-Struktur","text":"{\n \"media\": {\n \"track\": [\n {\n \"@type\": \"General\",\n \"Duration\": \"8885.042\",\n \"Format\": \"Matroska\"\n },\n {\n \"@type\": \"Video\",\n \"Format\": \"HEVC\",\n \"Width\": \"1920\",\n \"Height\": \"1080\",\n \"FrameRate\": \"23.976\"\n },\n {\n \"@type\": \"Audio\",\n \"StreamOrder\": \"1\",\n \"Format\": \"TrueHD\",\n \"Channels\": \"8\",\n \"Language\": \"en\"\n },\n {\n \"@type\": \"Audio\",\n \"StreamOrder\": \"2\",\n \"Format\": \"AC-3\",\n \"Channels\": \"6\",\n \"Language\": \"de\"\n },\n {\n \"@type\": \"Text\",\n \"StreamOrder\": \"1\",\n \"Format\": \"UTF-8\",\n \"Language\": \"de\"\n }\n ]\n }\n}\n"},{"location":"tools/mediainfo/#verarbeitung-in-ripster","title":"Verarbeitung in Ripster","text":"encodePlan.js verarbeitet die MediaInfo-Ausgabe:
{Sprache} ({Format}, {Kan\u00e4le})\n Beispiele: - Deutsch (AC-3, 5.1) - English (TrueHD, 7.1) - Fran\u00e7ais (AC-3, 2.0)
mediainfo_command Pfad/Befehl f\u00fcr mediainfo"},{"location":"tools/mediainfo/#troubleshooting","title":"Troubleshooting","text":""},{"location":"tools/mediainfo/#mediainfo-gibt-kein-json-aus","title":"MediaInfo gibt kein JSON aus","text":"# Version pr\u00fcfen\nmediainfo --Version\n\n# JSON-Ausgabe testen\nmediainfo --Output=JSON /path/to/test.mkv\n MediaInfo >= 17.10 wird empfohlen.
"},{"location":"tools/mediainfo/#sprache-als-und-angezeigt","title":"Sprache als \"und\" angezeigt","text":"und steht f\u00fcr \"undetermined\" \u2013 die Sprache ist in der MKV-Datei nicht getaggt. Dies ist bei manchen Rips normal. Der Track wird trotzdem angezeigt und kann manuell ausgew\u00e4hlt werden.