Bugfix and Docs

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

View File

@@ -1,329 +1,103 @@
# Encode-Planung & Track-Auswahl
`encodePlan.js` analysiert die HandBrake-Scan-Ausgabe, wählt Audio- und Untertitelspuren anhand von Regeln vor und erstellt einen vollständigen Encode-Plan für die Benutzer-Review.
Ripster erzeugt vor dem Encode einen `encodePlan` und lässt ihn im Review-Panel bestätigen.
---
## Ablauf im Pipeline-Kontext
## Ablauf
```
RIPPING abgeschlossen (oder Pre-Rip-Scan)
HandBrake --scan (alle Titel & Tracks einlesen)
buildTrackSelectors() ← Regeln aus Einstellungen ableiten
selectTrackIds() ← Tracks anhand Regeln vorauswählen
resolveAudioEncoderAction() ← Encoder-Aktion pro Track bestimmen
buildDiscScanReview() ← Vollständigen Encode-Plan erstellen
READY_TO_ENCODE ← Benutzer-Review im Frontend
applyManualTrackSelectionToPlan() ← Benutzer-Auswahl anwenden
ENCODING ← HandBrake-CLI mit finalem Plan starten
```text
Quelle bestimmen (Disc/RAW)
-> HandBrake-Scan (--scan --json)
-> Plan erstellen (Titel, Audio, Untertitel)
-> READY_TO_ENCODE
-> Benutzer bestätigt Auswahl
-> finaler HandBrake-Aufruf
```
---
## Phase 1: Pre-Rip Track-Scan
## Review-Inhalt (`READY_TO_ENCODE`)
Ripster führt einen **HandBrake-Scan** bereits **vor dem eigentlichen Ripping** durch:
```bash
HandBrakeCLI --scan -i /dev/sr0 -t 0
```
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ätigen.
!!! info "Pre-Rip vs. Post-Rip"
Ob der Scan vor oder nach dem Ripping passiert, hängt vom konfigurierten Modus ab. Bei direktem Disc-Zugriff ist Pre-Rip möglich; nach einem MakeMKV-Backup wird die entstandene `.mkv`-Datei gescannt.
- auswählbarer Encode-Titel
- Audio-Track-Selektion
- Untertitel-Track-Selektion inkl. Flags
- `burnIn`
- `forced`
- `defaultTrack`
- optionale User-Presets (HandBrake-Preset + Extra-Args)
- optionale Pre-/Post-Skripte und Ketten
---
## Phase 2: Track-Selektor-Regeln (`buildTrackSelectors`)
## Bestätigung (`confirm-encode`)
Die Regeln werden aus den HandBrake-Einstellungen abgeleitet. Es gibt fünf **Selektionsmodi**:
| Modus | Beschreibung |
|------|-------------|
| `none` | Keine Tracks dieser Art übernehmen |
| `first` | Nur den ersten Track übernehmen |
| `all` | Alle Tracks übernehmen |
| `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`) überschreiben die Basis-Konfiguration.
---
## Phase 3: Automatische Vorauswahl (`selectTrackIds`)
### Audio-Tracks
```
Modus 'none' → Keine Audio-Tracks
Modus 'all' → Alle Tracks (oder nur erster, wenn firstOnly)
Modus 'language' → Alle Tracks in den konfigurierten Sprachen
Modus 'explicit' → Nur die angegebenen Track-IDs
Modus 'first' → Nur Track 1
```
Jeder Audio-Track erhält das Feld `selectedByRule: true/false` dieses zeigt dem Benutzer, welche Tracks automatisch vorausgewählt wurden.
**Sprach-Normalisierung (`normalizeLanguage`):**
Alle Sprachcodes werden auf **ISO 639-2** (3-Buchstaben) normalisiert:
| Eingabe | Normalisiert |
|--------|-------------|
| `de`, `ger` | `deu` |
| `German` | `deu` |
| `en`, `eng` | `eng` |
| `English` | `eng` |
| `fr`, `fre` | `fra` |
| `ja`, `jpn` | `jpn` |
| Unbekannt | `und` |
### Untertitel-Tracks
Gleiche Modus-Logik wie Audio, aber mit **zusätzlichen Flags** pro Track:
| Flag | Bedeutung |
|------|-----------|
| `burnIn` | Untertitel in Video einbrennen (`--subtitle-burned`) |
| `forced` | Nur erzwungene Untertitel übernehmen (`--subtitle-forced`) |
| `defaultTrack` | Als Standard-Untertitelspur markieren (`--subtitle-default`) |
Diese Flags werden im Encode-Review als Checkboxen angezeigt.
---
## Phase 4: Encoder-Aktion bestimmen (`resolveAudioEncoderAction`)
Für jeden vorausgewählten Audio-Track bestimmt Ripster die Encoder-Aktion:
```
Encoder-Einstellung Codec-Support in Copy-Mask? Aktion
─────────────────────────────────────────────────────────────────────
Kein Encoder / 'preset-default' → preset-default HandBrake-Preset entscheidet
encoder.startsWith('copy')
UND Codec in audioCopyMask → copy Direktkopie (verlustfrei)
UND Codec NICHT in audioCopyMask→ fallback Transcode mit Fallback-Encoder
sonstiger Encoder → transcode Transcode mit explizitem Encoder
```
**Encoder-Aktionstypen:**
| Typ | Label (UI) | Qualität |
|----|-----------|---------|
| `preset-default` | `Preset-Default (HandBrake)` | HandBrake entscheidet |
| `copy` | `Copy (ac3)` | Verlustfrei |
| `fallback` | `Fallback Transcode (av_aac)` | Mit Qualitätsverlust |
| `transcode` | `Transcode (av_aac)` | Mit Qualitätsverlust |
**Copy-kompatible Codecs (Standard Copy-Mask):**
| Codec | Encoder-String |
|-------|---------------|
| AC-3 | `copy: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)* |
!!! warning "DTS im Standard-HandBrake"
Standard-HandBrake-Builds unterstützen kein DTS-Passthrough. DTS-Tracks werden dann automatisch auf den Fallback-Encoder umgestellt (Standard: `av_aac`).
---
## Phase 5: Encode-Plan-Struktur
Der vollständige Plan wird im Job-Datensatz als `encode_plan_json` gespeichert:
Typischer Payload:
```json
{
"mode": "pre_rip",
"preRip": true,
"encodeInputTitleId": 1,
"encodeInputPath": "disc-track-scan://title-1",
"selectors": {
"audio": { "mode": "language", "languages": ["deu", "eng"], "copyMask": ["copy:ac3", "copy:eac3"] },
"subtitle": { "mode": "none" }
},
"titles": [
{
"id": 1,
"fileName": "Disc Title 1",
"durationSeconds": 8885,
"selectedByMinLength": true,
"isEncodeInput": true,
"audioTracks": [
{
"id": 1,
"sourceTrackId": 1,
"language": "eng",
"languageLabel": "English",
"title": "5.1 Surround",
"format": "AC3",
"codecToken": "ac3",
"channels": "6",
"selectedByRule": true,
"selectedForEncode": true,
"encodePreviewActions": [
{ "type": "copy", "encoder": "copy:ac3", "label": "Copy (ac3)" }
],
"encodePreviewSummary": "Copy (ac3)"
},
{
"id": 2,
"sourceTrackId": 2,
"language": "deu",
"languageLabel": "Deutsch",
"format": "DTS",
"codecToken": "dts",
"channels": "6",
"selectedByRule": true,
"selectedForEncode": true,
"encodePreviewActions": [
{ "type": "fallback", "encoder": "av_aac", "label": "Fallback Transcode (av_aac)" }
],
"encodePreviewSummary": "Fallback Transcode (av_aac)"
},
{
"id": 3,
"language": "fra",
"languageLabel": "Français",
"selectedByRule": false,
"selectedForEncode": false,
"encodePreviewSummary": "Nicht übernommen"
}
],
"subtitleTracks": [
{
"id": 1,
"language": "deu",
"selectedByRule": true,
"selectedForEncode": true,
"burnIn": false,
"forced": false,
"defaultTrack": true,
"subtitlePreviewSummary": "Übernehmen",
"subtitlePreviewFlags": ["default"]
}
]
}
]
}
```
---
## Phase 6: Benutzer-Review im Frontend (`MediaInfoReviewPanel`)
Das Review-Panel zeigt:
```
┌─────────────────────────────────────────────────────────────────┐
│ Encode-Review Titel: Disc Title 1 │
│ Laufzeit: 2:28:05 │
├─────────────────────────────────────────────────────────────────┤
│ Audio-Spuren │
├──────┬──────────────────────────┬──────────────────────────────┤
│ [✓] │ Track 1: English (AC3) │ Copy (ac3) │
│ [✓] │ Track 2: Deutsch (DTS) │ Fallback Transcode (av_aac) │
│ [ ] │ Track 3: Français (DTS) │ Nicht übernommen │
├──────┴──────────────────────────┴──────────────────────────────┤
│ Untertitel-Spuren │
├──────┬──────────────────────────┬────────┬────────┬────────────┤
│ [✓] │ Track 1: Deutsch │Einbr.[ ]│Forced[ ]│Default[✓]│
│ [ ] │ Track 2: English │Einbr.[ ]│Forced[ ]│Default[ ]│
├──────┴──────────────────────────┴────────┴────────┴────────────┤
│ [Encoding starten] │
└─────────────────────────────────────────────────────────────────┘
```
Der Benutzer kann:
- **Audio-Tracks** per Checkbox aktivieren/deaktivieren
- **Untertitel-Flags** (Einbrennen, Forced, Default) setzen
- **Mehrere Titel** bei der Titleauswahl wechseln (für Discs mit mehreren Haupttiteln)
---
## Phase 7: Benutzer-Auswahl anwenden (`applyManualTrackSelectionToPlan`)
Im Frontend wird die Benutzer-Auswahl beim Klick auf **"Encoding starten"** (ggf. automatisch) bestätigt und dann auf den Plan angewendet:
```json
Payload: {
"selectedEncodeTitleId": 1,
"selectedTrackSelection": {
"1": {
"audioTrackIds": [1, 2],
"subtitleTrackIds": [1]
"subtitleTrackIds": [3]
}
}
},
"selectedPreEncodeScriptIds": [1],
"selectedPostEncodeScriptIds": [2],
"selectedPreEncodeChainIds": [3],
"selectedPostEncodeChainIds": [4],
"selectedUserPresetId": 5
}
```
Jeder Track erhält `selectedForEncode: true/false` entsprechend der Auswahl. Die Encoder-Aktionen (`encodeActions`) der nicht gewählten Tracks werden geleert.
Ripster speichert die bestätigte Auswahl in `jobs.encode_plan_json` und markiert `encode_review_confirmed = 1`.
---
## Phase 8: HandBrake-CLI-Befehl
## HandBrake-Aufruf
Aus dem finalisierten Plan baut Ripster den HandBrake-Aufruf:
Grundstruktur:
```bash
HandBrakeCLI \
-i /dev/sr0 \
-o "/mnt/movies/Inception (2010).mkv" \
-t 1 \
--preset "H.265 MKV 1080p30" \
-a 1,2 \
-E copy:ac3,av_aac \
-s 1 \
--subtitle-default 1
-i <input> \
-o <output> \
-t <titleId> \
-Z "<preset>" \
<extra-args> \
-a <audioTrackIds|none> \
-s <subtitleTrackIds|none>
```
| Argument | Quelle |
|---------|--------|
| `-i` | `encode_input_path` aus Job |
| `-o` | Ausgabepfad aus `filename_template` + `movie_dir` |
| `-t` | Gewählter Titel-Index |
| `-a` | Kommagetrennte Audio-Track-IDs der ausgewählten 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 |
Untertitel-Flags werden bei Bedarf ergänzt:
- `--subtitle-burned=<id>`
- `--subtitle-default=<id>`
- `--subtitle-forced=<id>` oder `--subtitle-forced`
---
## Dateiname-Template
## Pre-/Post-Encode-Ausführungen
| Platzhalter | Wert | Beispiel |
|------------|------|---------|
| `{title}` | Filmtitel von OMDb | `Inception` |
| `{year}` | Erscheinungsjahr | `2010` |
| `{imdb_id}` | IMDb-ID | `tt1375666` |
| `{type}` | `movie` oder `series` | `movie` |
- Pre-Encode läuft vor HandBrake
- Post-Encode läuft nach HandBrake
Sonderzeichen (`:`, `/`, `?`, `*` etc.) werden automatisch aus dem Dateinamen entfernt.
Verhalten bei Fehlern:
- Pre-Encode-Fehler: Job wird als `ERROR` beendet (Encode startet nicht)
- Post-Encode-Fehler: Job kann `FINISHED` bleiben, enthält aber Fehlerhinweis/Script-Summary
---
## Re-Encoding
## Dateinamen/Ordner
Ein abgeschlossener Job kann ohne erneutes Ripping neu encodiert werden:
Der finale Outputpfad wird aus Settings-Templates aufgebaut.
1. Job in der **History** öffnen
2. **"Re-Encode"** klicken
3. Track-Auswahl anpassen (oder bestehende übernehmen)
4. Encoding startet mit den aktuellen `handbrake_*`-Einstellungen
Platzhalter:
Nützlich bei geänderten Presets, anderen Sprach-Präferenzen oder nach einem Einstellungs-Update.
- `${title}`
- `${year}`
- `${imdbId}`
Ungültige Dateizeichen werden sanitisiert.

View File

@@ -1,6 +1,6 @@
# Pipeline
Der Pipeline-Abschnitt beschreibt den Kern-Workflow von Ripster.
Der Pipeline-Bereich beschreibt den Kern-Workflow von Ripster.
<div class="grid cards" markdown>
@@ -8,7 +8,7 @@ Der Pipeline-Abschnitt beschreibt den Kern-Workflow von Ripster.
---
Der vollständige Ripping-Workflow mit allen Zustandsübergängen.
Zustände, Übergänge und Queue-Verhalten.
[:octicons-arrow-right-24: Workflow](workflow.md)
@@ -16,7 +16,7 @@ Der Pipeline-Abschnitt beschreibt den Kern-Workflow von Ripster.
---
Wie Ripster Audio- und Untertitel-Tracks analysiert und Encode-Pläne erstellt.
Wie Titel/Tracks für HandBrake vorbereitet und bestätigt werden.
[:octicons-arrow-right-24: Encoding](encoding.md)
@@ -24,16 +24,16 @@ Der Pipeline-Abschnitt beschreibt den Kern-Workflow von Ripster.
---
Erkennung von Blu-ray Playlist-Obfuskierung und Auswahl der korrekten Playlist.
Bewertung mehrdeutiger Blu-ray-Playlists und manuelle Entscheidung.
[:octicons-arrow-right-24: Playlist-Analyse](playlist-analysis.md)
- :material-script-text: **Post-Encode-Skripte**
- :material-script-text: **Encode-Skripte (Pre & Post)**
---
Automatische Ausführung von Shell-Skripten nach erfolgreichem Encoding z. B. zum Verschieben oder Benachrichtigen.
Skripte/Ketten vor und nach dem Encode ausführen.
[:octicons-arrow-right-24: Post-Encode-Skripte](post-encode-scripts.md)
[:octicons-arrow-right-24: Encode-Skripte](post-encode-scripts.md)
</div>

View File

@@ -1,217 +1,65 @@
# Playlist-Analyse
Einige Blu-rays verwenden **Playlist-Obfuskierung** als Kopierschutz. Ripster analysiert automatisch alle MakeMKV-Titel und empfiehlt die korrekte Playlist auf Basis eines Segment-Scoring-Algorithmus aus `playlistAnalysis.js`.
Ripster analysiert bei Blu-ray-ähnlichen Quellen Playlists und fordert bei Mehrdeutigkeit eine manuelle Auswahl an.
---
## Das Problem: Playlist-Obfuskierung
## Ziel
Moderne Blu-rays können Dutzende bis Hunderte von Titeln/Playlists enthalten. Der eigentliche Film steckt in genau einer davon alle anderen sind:
- **Kurze Dummy-Titel** (wenige Sekunden bis Minuten)
- **Titel mit verschachtelten Segmenten** (absichtlich versetzte Reihenfolge, sodass der Film falsch gerippt wird)
- **Titel gleicher Länge** (mehrere Playlists mit identischer Laufzeit, aber unterschiedlicher Segment-Reihenfolge)
Das Ziel der Obfuskierung: Ein einfacher Ripper wählt den erstbesten langen Titel und bekommt ein zerstückeltes, unbrauchbares Video.
Erkennen, welche Playlist wahrscheinlich der Hauptfilm ist, statt versehentlich eine Fake-/Dummy-Playlist zu verwenden.
---
## Wann wird die Analyse ausgelöst?
## Eingabedaten
Die Playlist-Analyse wird automatisch gestartet **sobald der Benutzer Metadaten bestätigt** (nach dem Metadaten-Dialog). Ripster ruft `makemkvcon` im Info-Modus auf und parst die TINFO-Ausgabe.
```
TINFO:<titleId>,26,"<segment-list>"
```
Feld **26** enthält die kommagetrennte Liste der Segment-Nummern in der Abspielreihenfolge des Titels.
Die Analyse basiert auf MakeMKV-Infos (u. a. Playlist-/Segment-Struktur, Laufzeiten, Titelzuordnung).
---
## Algorithmus im Detail (`playlistAnalysis.js`)
## Auswertung (vereinfacht)
### Schritt 1 Segment-Nummern parsen
Für Kandidaten werden u. a. berücksichtigt:
```
TINFO:1,26,"00000,00001,00002,00003" → [0, 1, 2, 3] linearer Film
TINFO:2,26,"00100,00050,00100,00051" → [100, 50, 100, 51] Fake-Playlist
```
- Laufzeit
- Segment-Reihenfolge
- Rückwärtssprünge/große Sprünge
- Kohärenz linearer Segmentfolgen
- Duplikatgruppen mit ähnlicher Laufzeit
### Schritt 2 Metriken berechnen (`computeSegmentMetrics`)
Daraus entstehen:
Für jedes aufeinanderfolgende Segment-Paar `[a, b]` wird `diff = b a` berechnet:
| Metrik | Bedingung | Bedeutung |
|--------|----------|-----------|
| `directSequenceSteps` | `diff == 1` | Aufeinanderfolgende Segmente → linearer Film |
| `backwardJumps` | `b < a` | Rückwärtssprünge → verdächtig |
| `largeJumps` | `\|diff\| > 20` | Große Sprünge → verdächtig |
| `alternatingPairs` | Große Sprünge mit **wechselndem Vorzeichen** | Hin-und-her-Muster → starker Fake-Indikator |
**Score-Formel:**
```
score = (directSequenceSteps × 2) (backwardJumps × 3) (largeJumps × 2)
```
**Konkrete Beispiele:**
| Segmentfolge | directSeq | backward | large | score | Ergebnis |
|-------------|-----------|----------|-------|-------|---------|
| `0,1,2,3,4,5` | 5 | 0 | 0 | +10 | Echter Film |
| `0,1,100,2,101,3` | 2 | 0 | 4 | -4 | Verdächtig |
| `50,10,60,11,70,12` | 0 | 3 | 3 | -15 | Fake |
### Schritt 3 Bewertungslabel vergeben (`buildEvaluationLabel`)
```
alternatingRatio = alternatingPairs / largeJumps
if alternatingRatio >= 0.55 AND alternatingPairs >= 3:
→ "Fake-Struktur (alternierendes Sprungmuster)"
else if backwardJumps > 0 OR largeJumps > 0:
→ "Auffällige Segmentreihenfolge"
else:
→ "wahrscheinlich korrekt (lineare Segmentfolge)"
```
### Schritt 4 Duplikat-Gruppen bilden (`buildSimilarityGroups`)
Alle Titel werden nach **ähnlicher Laufzeit** gruppiert (±90 Sekunden Toleranz). Gibt es mehrere Kandidaten mit ähnlicher Laufzeit, ist das ein klares Zeichen für Obfuskierung:
```
8 Titel mit ~148 Minuten Laufzeit → Duplikat-Gruppe
→ obfuscationDetected = true
```
### Schritt 5 Besten Kandidaten empfehlen (`scoreCandidates`)
Innerhalb der größten Duplikat-Gruppe werden alle Kandidaten sortiert nach:
1. `score` (höher = besser)
2. `sequenceCoherence` (Anteil linearer Segmentschritte)
3. Laufzeit (länger = besser)
4. Dateigröße (größer = besser als Tiebreaker)
Der **erste Kandidat** der sortierten Liste ist die Empfehlung.
### Schritt 6 Entscheidung erzwingen bei mehreren Kandidaten
Sobald nach `MIN_LENGTH_MINUTES` **mehr als eine** Playlist übrig bleibt, wird immer eine manuelle Auswahl verlangt:
```
candidateCount > 1 → manualDecisionRequired = true
candidateCount <= 1 → manualDecisionRequired = false
```
- `candidates`
- `evaluatedCandidates` (inkl. Score/Label)
- `recommendation`
- `manualDecisionRequired`
---
## Wann greift der Benutzer ein?
## Wann muss der Benutzer entscheiden?
```
obfuscationDetected = duplicateDurationGroups.length > 0
manualDecisionRequired = candidates.length > 1
```
Wenn nach Filterung mehr als ein relevanter Kandidat übrig bleibt, setzt Ripster `manualDecisionRequired = true` und wechselt auf:
| Ergebnis | Nächster Pipeline-Zustand | Aktion |
|---------|--------------------------|--------|
| Nur ein Kandidat nach Mindestlänge | `READY_TO_START` | Automatische Übernahme möglich |
| Mehrere Kandidaten nach Mindestlänge | `WAITING_FOR_USER_DECISION` | Benutzer muss Playlist auswählen |
- `WAITING_FOR_USER_DECISION`
Dann muss eine Playlist bestätigt werden, bevor der Workflow weiterläuft.
---
## Benutzeroberfläche: Playlist-Auswahl-Dialog
## Konfigurationseinfluss
Wenn `manualDecisionRequired = true`, öffnet sich der Playlist-Dialog **nach** dem Metadaten-Dialog:
| Key | Wirkung |
|-----|---------|
| `makemkv_min_length_minutes` | Mindestlaufzeit für Kandidaten |
```
┌───────────────────────────────────────────────────────────────────┐
│ Playlist-Auswahl │
├──────────┬──────────┬──────────┬────────────────────────────────┤
│ Playlist │ Laufzeit │ Score │ Bewertung │
├──────────┼──────────┼──────────┼────────────────────────────────┤
│ ★ 00800 │ 2:28:05 │ +18 │ wahrscheinlich korrekt │
│ │ │ │ (lineare Segmentfolge) │
├──────────┼──────────┼──────────┼────────────────────────────────┤
│ 00801 │ 2:28:12 │ 4 │ Auffällige Segmentreihenfolge │
├──────────┼──────────┼──────────┼────────────────────────────────┤
│ 00900 │ 2:28:05 │ 32 │ Fake-Struktur │
│ │ │ │ (alternierendes Sprungmuster) │
└──────────┴──────────┴──────────┴────────────────────────────────┘
Hinweis: 847 Playlists insgesamt. 3 relevante Kandidaten (≥ 15 min).
Empfehlung: 00800 (★)
```
- **★** markiert die empfohlene Playlist (vorausgewählt)
- Nur Titel ≥ `makemkv_min_length_minutes` erscheinen in der Liste
- Der Benutzer wählt per Radio-Button und klickt "Bestätigen"
- Erst nach dieser Bestätigung wechselt die Pipeline zu `READY_TO_START`
Default ist aktuell `60` Minuten.
---
## Vollständige Datenstruktur (`analyzeContext.playlistAnalysis`)
## UI-Verhalten
```json
{
"titles": [
{ "titleId": 1, "playlistId": "00800", "durationSeconds": 8885, "durationLabel": "2:28:05", "chapters": 28 }
],
"candidates": [
{ "titleId": 1, "playlistId": "00800", "durationSeconds": 8885 },
{ "titleId": 2, "playlistId": "00801", "durationSeconds": 8892 }
],
"evaluatedCandidates": [
{
"titleId": 1,
"playlistId": "00800",
"score": 18,
"sequenceCoherence": 0.95,
"evaluationLabel": "wahrscheinlich korrekt (lineare Segmentfolge)",
"metrics": {
"directSequenceSteps": 12,
"backwardJumps": 0,
"largeJumps": 1,
"alternatingPairs": 0
}
}
],
"duplicateDurationGroups": [
[
{ "titleId": 1, "playlistId": "00800" },
{ "titleId": 2, "playlistId": "00801" }
]
],
"recommendation": {
"titleId": 1,
"playlistId": "00800",
"score": 18,
"reason": "Höchster Segment-Score in der größten Laufzeit-Gruppe"
},
"obfuscationDetected": true,
"manualDecisionRequired": true
}
```
Bei manueller Entscheidung zeigt das Dashboard Kandidaten inkl. Score/Bewertung und markiert eine Empfehlung.
---
Nach Bestätigung:
## Konfiguration
| Einstellung | Standard | Wirkung |
|------------|---------|---------|
| `makemkv_min_length_minutes` | `15` | Titel kürzer als dieser Wert werden als Kandidaten ignoriert |
---
## Tipps bei Fehlempfehlung
!!! tip "Falsche Playlist gewählt?"
Wenn das resultierende Video zerstückelt ist:
1. Job in der **History** öffnen
2. **Re-Encode** starten diesmal eine andere Playlist wählen
3. Alternativ: Korrekte Playlist im [MakeMKV-Forum](https://www.makemkv.com/forum/) recherchieren
!!! info "Keine Segment-Daten verfügbar"
Bei DVDs oder älteren Blu-rays liefert MakeMKV manchmal keine Segmentinfos (TINFO-Feld 26 fehlt). In diesem Fall entfällt die Analyse und der erste Titel über der Mindestlänge wird automatisch verwendet.
- mit vorhandenem RAW -> zurück zu `MEDIAINFO_CHECK`
- ohne RAW -> Startpfad über `READY_TO_START`/`RIPPING`

View File

@@ -1,173 +1,70 @@
# Encode-Skripte (Pre & Post)
Ripster unterstützt **Pre-Encode-** und **Post-Encode-Ausführungen**: Beliebige Shell-Skripte oder Skript-Ketten können automatisch vor und/oder nach dem Encoding-Schritt laufen z. B. zum Vorbereiten von Verzeichnissen, Verschieben von Dateien oder Benachrichtigen externer Dienste.
Ripster kann Skripte und Skript-Ketten vor und nach dem Encode ausführen.
---
## Funktionsweise
## Ablauf
```
```text
READY_TO_ENCODE
[Pre-Encode-Ausführungen] ← Fehler? → Abbruch
Skript/Kette 1, 2, …
ENCODING
[Post-Encode-Ausführungen] ← Fehler? → Abbruch
Skript/Kette 1, 2, …
FINISHED
```
!!! warning "Abbruch bei Fehler"
Schlägt eine Ausführung fehl (Exit-Code ≠ 0), werden alle nachfolgenden Ausführungen der gleichen Phase **nicht mehr ausgeführt**.
Der Job bleibt im Abschlusszustand `FINISHED`; der Fehler wird in Log/Status-Text und im Summary festgehalten.
---
## Skript- und Ketten-Verwaltung
Skripte und Skript-Ketten werden über die **Einstellungen-Seite** angelegt und verwaltet. Die Reihenfolge in der Liste kann per **Drag & Drop** geändert werden und bleibt persistent gespeichert.
### Skript anlegen
Navigiere zu **Einstellungen → 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ärung |
### Skript-Ketten
Eine **Skript-Kette** fasst mehrere Skripte zu einer benannten Einheit zusammen, die als ganzes ausgewählt werden kann. Nützlich für wiederkehrende Kombinationen (z. B. „Move + Notify Plex + Webhook"). Ketten werden genauso wie einzelne Skripte im Review-Panel ausgewählt.
### Verfügbare Umgebungsvariablen
Jedes Skript wird mit folgenden Umgebungsvariablen aufgerufen:
| Variable | Inhalt | Beispiel |
|---------|--------|---------|
| `RIPSTER_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` |
### Beispiel-Skript: Datei nach Jellyfin verschieben
```bash
#!/bin/bash
# /home/michael/scripts/move-to-jellyfin.sh
TARGET_DIR="/mnt/media/movies"
mkdir -p "$TARGET_DIR"
mv "$RIPSTER_OUTPUT_PATH" "$TARGET_DIR/"
echo "Verschoben: $RIPSTER_TITLE nach $TARGET_DIR"
```
### Beispiel-Skript: Webhook auslösen
```bash
#!/bin/bash
# /home/michael/scripts/notify-webhook.sh
curl -s -X POST https://mein-webhook.example.com/ripster \
-H "Content-Type: application/json" \
-d "{\"title\": \"$RIPSTER_TITLE\", \"year\": \"$RIPSTER_YEAR\", \"path\": \"$RIPSTER_OUTPUT_PATH\"}"
-> Pre-Encode Skripte/Ketten
-> HandBrake Encoding
-> Post-Encode Skripte/Ketten
-> FINISHED oder ERROR
```
---
## Im Encode-Review auswählen
## Auswahl im Review
Im `READY_TO_ENCODE`-Zustand zeigt das **MediaInfoReviewPanel** zwei Abschnitte:
Im Review-Panel kannst du getrennt wählen:
```
┌──────────────────────────────────────────────────────────┐
Pre-Encode Ausführungen (optional) │
├──────────────────────────────────────────────────────────┤
│ ≡ 1. Verzeichnis vorbereiten (Skript) [Entfernen]│
├──────────────────────────────────────────────────────────┤
│ Hinzufügen: [Skript/Kette auswählen ▾] [+ Hinzuf.]│
└──────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────┐
│ Post-Encode Ausführungen (optional) │
├──────────────────────────────────────────────────────────┤
│ ≡ 1. Zu Plex verschieben (Skript) [Entfernen]│
│ ≡ 2. Notify-Kette (Kette) [Entfernen]│
├──────────────────────────────────────────────────────────┤
│ Hinzufügen: [Skript/Kette auswählen ▾] [+ Hinzuf.]│
└──────────────────────────────────────────────────────────┘
```
- **Pre-Encode** und **Post-Encode** werden separat konfiguriert
- Sowohl **einzelne Skripte** als auch **Skript-Ketten** können in beiden Phasen ausgewählt werden
- **Reihenfolge** per Drag & Drop innerhalb jeder Phase ändern
- **Hinzufügen** aus der Dropdown-Liste aller konfigurierten Skripte und Ketten
- **Entfernen** einzelner Einträge
- Auswahl kann pro Job frei variiert werden
- `selectedPreEncodeScriptIds`
- `selectedPostEncodeScriptIds`
- `selectedPreEncodeChainIds`
- `selectedPostEncodeChainIds`
---
## Skript testen
## Fehlerverhalten
Über die Einstellungen kann jedes Skript mit einem Test-Job ausgeführt werden:
```http
POST /api/settings/scripts/:scriptId/test
```
Der Test-Aufruf befüllt die Umgebungsvariablen mit Platzhalter-Werten.
- Pre-Encode-Fehler stoppen die Kette und führen zu `ERROR`.
- Post-Encode-Fehler stoppen die restlichen Post-Schritte; Job kann dennoch `FINISHED` sein (mit Fehlerzusatz im Status/Log).
---
## Ausführungs-Ergebnis
## Verfügbare Umgebungsvariablen
Das Ergebnis der Skript-Ausführung wird im Job-Datensatz gespeichert und in der History angezeigt:
Beim Script-Run werden gesetzt:
```json
{
"postEncodeScripts": {
"configured": 2,
"attempted": 2,
"succeeded": 2,
"failed": 0,
"skipped": 0,
"aborted": false,
"results": [
{
"scriptId": 1,
"scriptName": "Zu Plex verschieben",
"status": "SUCCESS"
},
{
"scriptId": 2,
"scriptName": "Webhook auslösen",
"status": "SUCCESS"
}
]
}
}
```
| Feld | Beschreibung |
|------|-------------|
| `configured` | Anzahl ausgewählter Skripte |
| `attempted` | Anzahl tatsächlich gestarteter Skripte |
| `succeeded` | Erfolgreich ausgeführt (Exit-Code 0) |
| `failed` | Fehlgeschlagen |
| `skipped` | Nicht ausgeführt (wegen vorherigem Fehler) |
| `aborted` | `true`, wenn die Kette abgebrochen wurde |
- `RIPSTER_SCRIPT_RUN_AT`
- `RIPSTER_JOB_ID`
- `RIPSTER_JOB_TITLE`
- `RIPSTER_MODE`
- `RIPSTER_INPUT_PATH`
- `RIPSTER_OUTPUT_PATH`
- `RIPSTER_RAW_PATH`
- `RIPSTER_SCRIPT_ID`
- `RIPSTER_SCRIPT_NAME`
- `RIPSTER_SCRIPT_SOURCE`
---
## API-Referenz
## Skript-Ketten
Eine vollständige API-Dokumentation der Skript-Endpunkte findest du unter:
Ketten unterstützen zwei Step-Typen:
[:octicons-arrow-right-24: Settings API Skripte](../api/settings.md#skript-verwaltung)
- `script` (führt ein hinterlegtes Skript aus)
- `wait` (wartet `waitSeconds`)
Bei Fehler in einem Script-Step wird die Kette abgebrochen.
---
## Testläufe
- Skript testen: `POST /api/settings/scripts/:id/test`
- Kette testen: `POST /api/settings/script-chains/:id/test`
Ergebnisse enthalten Erfolg/Exit-Code, Laufzeit und stdout/stderr.

View File

@@ -1,329 +1,87 @@
# Workflow & Zustände
Der Ripping-Workflow von Ripster ist als **State Machine** implementiert. Jeder Zustand hat klar definierte Übergangsbedingungen und Aktionen.
Ripster steuert den Ablauf als State-Machine im `pipelineService`.
---
## Zustandsdiagramm
<div class="pipeline-diagram">
## Zustandsdiagramm (vereinfacht)
```mermaid
flowchart LR
START(( )) --> IDLE
IDLE -->|Disc erkannt| DD[DISC_DETECTED]
DD -->|Analyse starten| META[METADATA\nSELECTION]
META -->|Metadaten übernommen| RTS[READY_TO\nSTART]
META -->|vorhandenes RAW +\nPlaylist offen| WUD[WAITING_FOR\nUSER_DECISION]
RTS -->|Auto-Start| RIP[RIPPING]
RTS -->|Auto-Start mit RAW| MIC[MEDIAINFO\nCHECK]
RIP -->|MKV fertig| MIC
RIP -->|Fehler| ERR
RIP -->|Abbruch| CAN([CANCELLED])
MIC -->|Playlist offen (Backup)| WUD
WUD -->|Playlist bestätigt| MIC
WUD -->|Playlist bestätigt,\nnoch kein RAW| RTS
MIC --> RTE[READY_TO\nENCODE]
RTE -->|Encoding starten\n(bestätigt bei Bedarf automatisch)| ENC[ENCODING]
ENC -->|inkl. Post-Skripte| FIN([FINISHED])
ENC -->|Fehler| ERR
ENC -->|Abbruch| CAN
ERR([ERROR]) -->|Retry / Cancel| IDLE
CAN -->|Retry / Neu-Analyse| IDLE
FIN -->|Neue Disc| IDLE
style FIN fill:#e8f5e9,stroke:#66bb6a,color:#2e7d32
style ERR fill:#ffebee,stroke:#ef5350,color:#c62828
style CAN fill:#fff3e0,stroke:#fb8c00,color:#e65100
style WUD fill:#fff8e1,stroke:#ffa726,color:#e65100
style ENC fill:#f3e5f5,stroke:#ab47bc,color:#6a1b9a
style RIP fill:#e3f2fd,stroke:#42a5f5,color:#1565c0
style MIC fill:#e3f2fd,stroke:#42a5f5,color:#1565c0
IDLE --> DISC_DETECTED
DISC_DETECTED --> ANALYZING
ANALYZING --> METADATA_SELECTION
METADATA_SELECTION --> READY_TO_START
READY_TO_START --> RIPPING
READY_TO_START --> MEDIAINFO_CHECK
MEDIAINFO_CHECK --> WAITING_FOR_USER_DECISION
WAITING_FOR_USER_DECISION --> MEDIAINFO_CHECK
MEDIAINFO_CHECK --> READY_TO_ENCODE
READY_TO_ENCODE --> ENCODING
ENCODING --> FINISHED
ENCODING --> ERROR
RIPPING --> ERROR
RIPPING --> CANCELLED
```
</div>
---
## State-Liste
| State | Bedeutung |
|------|-----------|
| `IDLE` | Wartet auf Disc |
| `DISC_DETECTED` | Disc erkannt |
| `ANALYZING` | MakeMKV-Analyse läuft |
| `METADATA_SELECTION` | Benutzer wählt Metadaten |
| `WAITING_FOR_USER_DECISION` | Playlist-Auswahl nötig |
| `READY_TO_START` | Übergangszustand vor Start |
| `RIPPING` | MakeMKV-Rip läuft |
| `MEDIAINFO_CHECK` | Quelle/Tracks werden ausgewertet |
| `READY_TO_ENCODE` | Review ist bereit |
| `ENCODING` | HandBrake läuft |
| `FINISHED` | erfolgreich abgeschlossen |
| `CANCELLED` | abgebrochen |
| `ERROR` | fehlgeschlagen |
---
## UI-Badge-Bezeichnungen
## Typische Pfade
Die Status-Badges im Dashboard verwenden diese Labels:
### Standardfall (kein vorhandenes RAW)
| State | Badge-Label |
|------|-------------|
| `IDLE` | `Bereit` |
| `DISC_DETECTED` | `Medium erkannt` |
| `METADATA_SELECTION` | `Metadatenauswahl` |
| `WAITING_FOR_USER_DECISION` | `Warte auf Auswahl` |
| `READY_TO_START` | `Startbereit` |
| `RIPPING` | `Rippen` |
| `MEDIAINFO_CHECK` | `Mediainfo-Pruefung` |
| `READY_TO_ENCODE` | `Bereit zum Encodieren` |
| `ENCODING` | `Encodieren` |
| `FINISHED` | `Fertig` |
| `CANCELLED` | `Abgebrochen` |
| `ERROR` | `Fehler` |
| Queue (kein eigener State) | `In der Queue` |
1. Disc erkannt
2. Analyse + Metadaten
3. `RIPPING`
4. `MEDIAINFO_CHECK`
5. `READY_TO_ENCODE`
6. `ENCODING`
7. `FINISHED`
### Vorhandenes RAW
`READY_TO_START` springt direkt zu `MEDIAINFO_CHECK` (kein neuer Rip).
### Mehrdeutige Blu-ray-Playlist
`MEDIAINFO_CHECK` -> `WAITING_FOR_USER_DECISION` bis Benutzer Playlist bestätigt.
---
## Zustandsbeschreibungen
## Queue-Verhalten
### IDLE
Wenn `pipeline_max_parallel_jobs` erreicht ist:
**Ausgangszustand.** Ripster wartet auf eine Disc.
- `diskDetectionService` pollt das Laufwerk im konfigurierten Intervall
- Bei Disc-Erkennung: automatischer Übergang zu `DISC_DETECTED`
- WebSocket-Event: `DISC_DETECTED`
- Job-Aktionen werden als Queue-Einträge abgelegt
- Queue kann zusätzlich Nicht-Job-Einträge enthalten (`script`, `chain`, `wait`)
- Reihenfolge ist per API/UI änderbar
---
### DISC_DETECTED
## Abbruch, Retry, Restart
**Disc erkannt, wartet auf Benutzeraktion.**
- Dashboard-Badge: **"Medium erkannt"**
- Status-Text: **"Neue Disk erkannt"**
- **"Analyse starten"**-Button wird aktiv
- Kein Prozess läuft noch
**Übergang:** Benutzer klickt "Analyse starten" → `METADATA_SELECTION`
---
### METADATA_SELECTION
**Metadaten-Auswahl läuft.**
1. Job wird erstellt (`status = METADATA_SELECTION`)
2. OMDb-Vorsuche mit erkanntem Disc-Label
3. `MetadataSelectionDialog` öffnet sich mit vorgeladenen Ergebnissen
4. Benutzer wählt Filmtitel (oder gibt manuell ein)
5. Nach Bestätigung wird der Job automatisch für Start/Queue vorbereitet (`selectMetadata` + `startPreparedJob`)
**Übergang (automatisch nach Metadaten-Bestätigung):**
| Ergebnis | Nächster Zustand |
|--------------------|-----------------|
| Kein verwertbares RAW vorhanden | `READY_TO_START` → automatisch `RIPPING` (oder Queue) |
| Verwertbares RAW vorhanden | `READY_TO_START` → automatisch `MEDIAINFO_CHECK` (oder Queue) |
| Vorhandenes RAW + offene Playlist-Entscheidung | `WAITING_FOR_USER_DECISION` |
---
### WAITING_FOR_USER_DECISION
**Playlist-Obfuskierung erkannt manuelle Auswahl erforderlich.**
!!! info "Neu seit „Skript Integration + UI Anpassungen""
Dieser Zustand wurde eingeführt, um Blu-rays mit mehreren Playlists ähnlicher Länge korrekt zu behandeln.
- Playlist-Auswahl-Dialog wird im Dashboard angezeigt
- Alle Kandidaten mit Score, Laufzeit und Bewertungslabel
- Empfohlene Playlist ist vorausgewählt
- Benutzer bestätigt mit **"Playlist übernehmen"**
- Tritt häufig nach `MEDIAINFO_CHECK` auf (Backup-Analyse), seltener direkt nach `METADATA_SELECTION` bei vorhandenem RAW
**Darstellung im Dashboard:**
```
┌──────────────────────────────────────────────────────────┐
│ Playlist-Auswahl erforderlich │
│ Es wurden mehrere Titel mit ähnlicher Laufzeit gefunden. │
├──────────┬──────────┬────────┬──────────────────────────┤
│ Playlist │ Laufzeit │ Score │ Bewertung │
├──────────┼──────────┼────────┼──────────────────────────┤
│ ● 00800 │ 2:28:05 │ +18 │ wahrscheinlich korrekt │
│ ○ 00801 │ 2:28:12 │ 4 │ Auffällige Segmentfolge │
│ ○ 00900 │ 2:28:05 │ 32 │ Fake-Struktur │
└──────────┴──────────┴────────┴──────────────────────────┘
[Playlist übernehmen]
```
**Übergang:** `selectMetadata(jobId, { selectedPlaylist })` setzt die Pipeline automatisch fort:
- mit vorhandenem RAW nach `MEDIAINFO_CHECK`
- ohne RAW über `READY_TO_START` weiter Richtung `RIPPING`
Mehr Details: [Playlist-Analyse](playlist-analysis.md)
---
### READY_TO_START
**Übergangs-/Fallback-Zustand vor dem eigentlichen Start.**
- Wird nach Metadaten-Bestätigung kurz gesetzt
- `startPreparedJob()` wird danach automatisch ausgeführt
- Wenn Parallel-Limit erreicht ist, wird der Start stattdessen in die Queue eingereiht
- **"Job starten"** ist primär für Sonderfälle/Fallback sichtbar
**Sonderfall RAW-Datei bereits vorhanden:**
Wenn für diesen Job bereits ein verwertbares RAW unter `raw_dir` existiert, wird Ripping übersprungen und direkt `MEDIAINFO_CHECK` gestartet.
**Übergang:** `startPreparedJob(jobId)``RIPPING` oder direkt `MEDIAINFO_CHECK`
---
### RIPPING
**MakeMKV rippt die Disc.**
=== "MKV-Modus (Standard)"
```bash
makemkvcon mkv disc:0 all /path/to/raw/ --minlength=900 -r
```
Erstellt MKV-Datei(en) direkt aus den gewählten Titeln.
=== "Backup-Modus"
```bash
makemkvcon backup disc:0 /path/to/raw/backup/ --decrypt -r
```
Erstellt vollständiges Disc-Backup inkl. Menüs.
**Live-Updates** aus MakeMKV-Ausgabe:
```
PRGV:2048,0,65536 → Fortschritt-Berechnung
PRGT:5011,0,"..." → Aktueller Task-Name
```
**Typische Dauer:** DVD 2045 min · Blu-ray 45120 min
---
### MEDIAINFO_CHECK
**HandBrake-Scan und Encode-Plan-Erstellung.**
Dieser Zustand umfasst je nach Quelle mehrere Phasen:
1. Optional: Playlist-Auflösung bei Blu-ray-Backup (inkl. MakeMKV/HandBrake-Zuordnung)
2. **HandBrake-Scan** (`HandBrakeCLI --scan`) auf RAW-Input
3. **Encode-Plan-Erstellung** mit automatischer Track-Vorauswahl
Kein Benutzereingriff läuft automatisch durch.
**Übergänge:**
- Eindeutige Quelle/Titelwahl möglich → `READY_TO_ENCODE`
- Mehrdeutige Playlist erkannt → `WAITING_FOR_USER_DECISION`
---
### READY_TO_ENCODE
**Encode-Plan bereit.**
Das `MediaInfoReviewPanel` zeigt:
- **Titel-Auswahl** (bei Discs mit mehreren langen Titeln)
- **Audio-Tracks** mit Encoder-Vorschau (Copy/Transcode/Fallback)
- **Untertitel-Tracks** mit Flags (Einbrennen, Forced, Default)
- **Post-Encode-Skripte** Auswahl und Reihenfolge der auszuführenden Skripte
Im Frontend startet **"Encoding starten"** (bzw. **"Backup + Encoding starten"** im Pre-Rip-Modus) den nächsten Schritt.
Falls die Review noch nicht bestätigt wurde, wird `confirmEncodeReview(...)` automatisch vor dem Start aufgerufen.
**Übergang:** `startPreparedJob(jobId)` → `ENCODING` (oder im Pre-Rip-Fall zuerst `RIPPING`)
---
### ENCODING
**HandBrake encodiert die Datei.**
```bash
HandBrakeCLI \
-i <quelle> -o <ziel> \
-t <titelId> \
--preset "H.265 MKV 1080p30" \
-a 1,2 -E copy:ac3,av_aac \
-s 1 --subtitle-default 1
```
**Live-Updates** aus HandBrake-stderr:
```
Encoding: task 1 of 1, 73.50 % (45.23 fps, avg 44.12 fps, ETA 00h12m34s)
```
Post-Encode-Skripte werden innerhalb dieses Zustands sequenziell ausgeführt (kein separater Pipeline-State).
!!! note "Skriptfehler"
Skriptfehler führen zum Abbruch der Skriptkette, der Job bleibt jedoch im Abschlusszustand `FINISHED` mit entsprechendem Hinweis im Status-Text/Log.
---
### FINISHED
**Job erfolgreich abgeschlossen.**
- Ausgabedatei liegt im konfigurierten `movie_dir`
- Job-Status in Datenbank: `FINISHED`
- PushOver-Benachrichtigung (falls konfiguriert)
- WebSocket-Event: `PIPELINE_STATE_CHANGED` (State `FINISHED`)
---
### CANCELLED
**Job wurde vom Benutzer abgebrochen.**
- Entsteht bei aktivem Abbruch (`/api/pipeline/cancel`) während laufender Phase
- Job-Status in Datenbank: `CANCELLED`
- Im Dashboard stehen danach u. a. `Retry Rippen`, `Review neu starten` oder `Encode neu starten` (kontextabhängig) zur Verfügung
---
### ERROR
**Fehler aufgetreten.**
- Fehlerdetails im Job-Datensatz gespeichert
- Fehler-Logs in History abrufbar
- **Retry**: Neustart vom Fehlerzustand
- **Neu analysieren**: Disc erneut als neuer Job starten
---
## Abbrechen & Retry
### Pipeline abbrechen
```http
POST /api/pipeline/cancel
```
- SIGINT → graceful exit (Timeout: 10 s) → SIGKILL
- Laufender Job landet in `CANCELLED` (oder Queue-Eintrag wird entfernt, falls noch nicht gestartet)
### Job wiederholen
```http
POST /api/pipeline/retry/:jobId
```
- Startet den Job neu in `RIPPING` (oder reiht den Retry in die Queue ein)
- Metadaten bleiben erhalten; Encode-/Scan-Daten werden neu erzeugt
### Re-Encode
```http
POST /api/pipeline/reencode/:jobId
```
- Encodiert bestehende Raw-MKV neu
- Ermöglicht neue Track-Auswahl und andere Skripte
- Kein Ripping erforderlich
- `cancel`: laufenden Job abbrechen oder Queue-Eintrag entfernen
- `retry`: Fehler-/Abbruch-Job neu starten
- `reencode`: aus vorhandenem RAW neu encodieren
- `restart-review`: Review aus RAW neu aufbauen
- `restart-encode`: Encoding mit letzter bestätigter Auswahl neu starten