Files
ripster/docs/pipeline/encoding.md
2026-03-04 14:48:56 +00:00

330 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.
---
## Ablauf im Pipeline-Kontext
```
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
```
---
## Phase 1: Pre-Rip Track-Scan
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.
---
## Phase 2: Track-Selektor-Regeln (`buildTrackSelectors`)
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:
```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[ ]│
├──────┴──────────────────────────┴────────┴────────┴────────────┤
│ [Encode bestätigen] │
└─────────────────────────────────────────────────────────────────┘
```
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`)
Nach "Encode bestätigen" wird die Benutzer-Auswahl auf den Plan angewendet:
```json
Payload: {
"selectedEncodeTitleId": 1,
"selectedTrackSelection": {
"1": {
"audioTrackIds": [1, 2],
"subtitleTrackIds": [1]
}
}
}
```
Jeder Track erhält `selectedForEncode: true/false` entsprechend der Auswahl. Die Encoder-Aktionen (`encodeActions`) der nicht gewählten Tracks werden geleert.
---
## Phase 8: HandBrake-CLI-Befehl
Aus dem finalisierten Plan baut Ripster den HandBrake-Aufruf:
```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
```
| 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 |
---
## Dateiname-Template
| 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.
---
## Re-Encoding
Ein abgeschlossener Job kann ohne erneutes Ripping neu encodiert werden:
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
Nützlich bei geänderten Presets, anderen Sprach-Präferenzen oder nach einem Einstellungs-Update.