Bugfix and Docs

This commit is contained in:
michael committed 2026-03-10 13:12:57 +00:00
1 parent 3516ff8486
commit ac4d77dddf
75 files changed
+3510 -5141

No files matched your search

+6
View File
@@ -74,3 +74,9 @@ frontend/.env.*
Thumbs.db
.idea/
.vscode/
# ----------------------------
# Scripts
# ----------------------------
deploy-ripster.sh
build-handbrake-nvdec.sh
+54 -46
View File
@@ -1,35 +1,21 @@
# Ripster
Ripster ist eine lokale Web-Anwendung für halbautomatisches Disc-Ripping mit MakeMKV + HandBrake inklusive Metadaten-Auswahl, Titel-/Spurprüfung und Job-Historie.
---
> **Neu seit letztem Release**
>
> - **Profil-spezifische Einstellungen** – Separate Konfiguration für Blu-ray und DVD: eigene Pfade, HandBrake-Presets, Rip-Modi, Extra-Args, Dateinamen-Templates; automatische Auflösung anhand des erkannten Medientyps
> - **Cron-Job-System** – Skripte und Skript-Ketten zeitgesteuert ausführen; eigener Expression-Parser, Ausführungs-Logs, manuelle Auslösung, PushOver-Integration pro Job
> - **User-Presets** – benannte HandBrake-Preset-Sammlungen (Preset + Extra-Args) pro Medientyp anlegen und im Review-Panel auswählen
> - **DVD-/Blu-ray-Erkennung verbessert** – robuste Media-Profil-Erkennung aus UDF/ISO9660-Dateisystemtyp, Laufwerk-Modell und Disc-Label; Medientyp-Indikator in der UI
> - **Pre-Encode-Ausführungen** – Skripte und Ketten können nun auch *vor* dem Encode-Schritt ausgeführt werden (zusätzlich zu Post-Encode)
> - **Sortierbare Skripte & Ketten** – Reihenfolge über Drag & Drop festlegen; wird persistent gespeichert
> - **Granulares PushOver** – je Event konfigurierbar (Metadaten bereit, Rip-Start, Encode-Start, Fertig, Fehler, Abbruch, Re-Encode)
> - **`rip_successful`-Flag in Jobs** – separates Feld zur Nachverfolgung ob der Rip-Schritt abgeschlossen wurde (unabhängig vom Encode-Status)
> - **`handbrake_restart_delete_incomplete_output`** – unvollständige Ausgabe wird beim Encode-Neustart automatisch gelöscht
Ripster ist eine lokale Web-Anwendung für halbautomatisches Disc-Ripping mit MakeMKV + HandBrake inklusive Metadaten-Auswahl, Track-Review, Queue, Skripten/Ketten und Job-Historie.
---
## Was Ripster kann
- Disc-Erkennung mit Pipeline-Status in Echtzeit (WebSocket)
- Robuste Erkennung von Blu-ray, DVD und CD (UDF/ISO9660-Heuristik + Laufwerk-Modell)
- Medienprofil-Erkennung (`bluray`/`dvd`/`other`) aus Device-/Filesystem-Heuristik
- Metadaten-Suche und Zuordnung über OMDb
- MakeMKV-Analyse und Rip (MKV oder Backup-Modus)
- HandBrake-Encode mit Preset + Extra-Args + Track-Override
- Manuelle Playlist-/Titel-Auswahl bei komplexen Blu-rays
- Pre- und Post-Encode-Skripte & Skript-Ketten (inkl. Drag-and-Drop-Sortierung)
- Cron-Jobs: Skripte und Ketten zeitgesteuert ausführen (eigener Expression-Parser, Logs, PushOver)
- Historie mit Re-Encode, Löschfunktionen und Detailansicht
- Dateibasierte Logs (Backend + Job-Prozesslogs)
- MakeMKV-Analyse und Rip (`mkv` oder `backup`) mit profilspezifischen Settings
- HandBrake-Review und Encoding mit Track-Auswahl, User-Presets, Extra-Args
- Pre- und Post-Encode-Ausführungen (Skripte und/oder Skript-Ketten)
- Pipeline-Queue mit Job- und Nicht-Job-Einträgen (`script`, `chain`, `wait`)
- Cron-Jobs für Skripte/Ketten (inkl. Logs und manueller Auslösung)
- Historie mit Re-Encode, Review-Neustart, File-/Job-Löschung und Orphan-Import
- Hardware-Monitoring (CPU/RAM/GPU/Storage) im Dashboard
## Tech-Stack
@@ -39,7 +25,7 @@ Ripster ist eine lokale Web-Anwendung für halbautomatisches Disc-Ripping mit Ma
## Voraussetzungen
- Linux-System mit optischem Laufwerk (oder gemountete Quelle)
- Linux-System mit optischem Laufwerk (oder gemounteter Quelle)
- Node.js `>= 20.19.0` (siehe [.nvmrc](.nvmrc))
- Installierte CLI-Tools im `PATH`:
- `makemkvcon`
@@ -54,7 +40,7 @@ Ripster ist eine lokale Web-Anwendung für halbautomatisches Disc-Ripping mit Ma
`start.sh` erledigt:
1. Node-Version prüfen/umschalten (inkl. `nvm`/`node@20`-Fallback)
1. Node-Version prüfen/umschalten (`nvm`/`npx node@20` Fallback)
2. Dependencies installieren (Root, Backend, Frontend)
3. Dev-Umgebung starten (`backend` + `frontend`)
@@ -63,11 +49,7 @@ Danach:
- Frontend: `http://localhost:5173`
- Backend API: `http://localhost:3001/api`
Stoppen:
```bash
./kill.sh
```
Stoppen: laufenden Prozess mit `Ctrl+C` im Terminal beenden.
## Manueller Start
@@ -99,23 +81,23 @@ npm run start
## Konfiguration
### 1) UI-Settings (empfohlen)
### UI-Settings (empfohlen)
Die meisten Einstellungen werden in der App unter `Settings` gepflegt und in SQLite gespeichert:
- Pfade: `raw_dir`, `movie_dir`, `log_dir`
- Pfade: `raw_dir[_bluray/_dvd/_other]`, `movie_dir[_bluray/_dvd/_other]`, `log_dir`
- Tools: `makemkv_command`, `handbrake_command`, `mediainfo_command`
- Encode: `handbrake_preset`, `handbrake_extra_args`, `output_extension`, `filename_template`
- Laufwerk/Scan: `drive_mode`, `drive_device`, Polling
- Profile: `*_bluray` / `*_dvd` Varianten für Rip-/Encode-Verhalten
- Queue/Monitoring: `pipeline_max_parallel_jobs`, `hardware_monitoring_*`
- Benachrichtigungen: PushOver
### 2) Umgebungsvariablen
### Umgebungsvariablen
Backend (`backend/src/config.js`):
- `PORT` (Default: `3001`)
- `DB_PATH` (Default: `backend/data/ripster.db`)
- `LOG_DIR` (Fallback-Logpfad, Default: `backend/logs`)
- `LOG_DIR` (Default: `backend/logs`)
- `CORS_ORIGIN` (Default: `*`)
- `LOG_LEVEL` (`debug|info|warn|error`, Default: `info`)
@@ -123,7 +105,7 @@ Frontend (Vite):
- `VITE_API_BASE` (Default: `/api`)
- `VITE_WS_URL` (optional, überschreibt automatische WS-URL)
- `VITE_PUBLIC_ORIGIN`, `VITE_ALLOWED_HOSTS`, `VITE_HMR_*` (Remote-Dev/HMR)
- optional für Remote-Dev: `VITE_PUBLIC_ORIGIN`, `VITE_ALLOWED_HOSTS`, `VITE_HMR_PROTOCOL`, `VITE_HMR_HOST`, `VITE_HMR_CLIENT_PORT`
## Logs und Daten
@@ -131,9 +113,9 @@ Log-Ziel ist primär der in den Settings gepflegte `log_dir`.
- Backend-Logs: `<log_dir>/backend/backend-latest.log` und Tagesdateien
- Job-Logs: `<log_dir>/job-<id>.process.log`
- DB: `backend/data/ripster.db` (inkl. Job-/Settings-Daten)
- DB: `backend/data/ripster.db`
Hinweis: Beim DB-Init wird das Schema gegen die Soll-Struktur abgeglichen und migriert.
Hinweis: Beim DB-Init wird das Schema geprüft und fehlende Elemente werden migriert.
## Projektstruktur
@@ -150,28 +132,55 @@ ripster/
pages/
components/
api/
db/schema.sql
start.sh
kill.sh
deploy-ripster.sh
install.sh
install-dev.sh
```
## API-Überblick
**Health**
- `GET /api/health`
**Pipeline**
- `GET /api/pipeline/state`
- `POST /api/pipeline/analyze`
- `POST /api/pipeline/rescan-disc`
- `POST /api/pipeline/select-metadata`
- `POST /api/pipeline/start/:jobId`
- `POST /api/pipeline/confirm-encode/:jobId`
- `POST /api/pipeline/cancel`
- `POST /api/pipeline/retry/:jobId`
- `POST /api/pipeline/reencode/:jobId`
- `POST /api/pipeline/restart-review/:jobId`
- `POST /api/pipeline/restart-encode/:jobId`
- `POST /api/pipeline/resume-ready/:jobId`
- `GET /api/pipeline/queue`
- `POST /api/pipeline/queue/reorder`
- `POST /api/pipeline/queue/entry`
- `DELETE /api/pipeline/queue/entry/:entryId`
**History**
- `GET /api/history`
- `GET /api/history/:id`
- `GET /api/history/database`
- `GET /api/history/orphan-raw`
- `POST /api/history/orphan-raw/import`
- `POST /api/history/:id/omdb/assign`
- `POST /api/history/:id/delete-files`
- `POST /api/history/:id/delete`
**Settings**
- `GET /api/settings`
- `PUT /api/settings/:key`
- `PUT /api/settings`
- `GET/POST/PUT/DELETE /api/settings/scripts...`
- `GET/POST/PUT/DELETE /api/settings/script-chains...`
- `GET/POST/PUT/DELETE /api/settings/user-presets...`
- `POST /api/settings/pushover/test`
**Cron-Jobs** _(neu)_
**Cron-Jobs**
- `GET /api/crons`
- `POST /api/crons`
- `GET /api/crons/:id`
@@ -185,17 +194,16 @@ ripster/
- WebSocket verbindet nicht:
- prüfen, ob Frontend über Vite-Proxy läuft (`/ws` -> Backend)
- bei Reverse-Proxy `VITE_PUBLIC_ORIGIN`/HMR korrekt setzen
- bei Reverse-Proxy Upgrade-Header für `/ws` setzen
- Keine Disc erkannt:
- `drive_mode=explicit` testen und `drive_device` setzen (z. B. `/dev/sr0`)
- HandBrake/MakeMKV Fehler:
- CLI-Binaries im `PATH` prüfen
- Preset-Name exakt wie in `HandBrakeCLI -z` hinterlegen
- Preset-Name mit `HandBrakeCLI -z` prüfen
- Startfehler wegen Schema:
- sicherstellen, dass die erwartete Schema-Datei vorhanden ist (`db/schema.sql`)
- `db/schema.sql` vorhanden halten
## Sicherheit
- Keine echten Tokens/Passwörter ins Repository committen.
- Lokale Secrets in `.env` oder in Settings pflegen, aber nicht versionieren.
+51 -3
View File
@@ -21,6 +21,8 @@ function parseJsonSafe(raw, fallback = null) {
const PROCESS_LOG_TAIL_MAX_BYTES = 1024 * 1024;
const processLogStreams = new Map();
const PROFILE_PATH_SUFFIXES = ['bluray', 'dvd', 'other'];
const RAW_INCOMPLETE_PREFIX = 'Incomplete_';
const RAW_RIP_COMPLETE_PREFIX = 'Rip_Complete_';
function inspectDirectory(dirPath) {
if (!dirPath) {
@@ -430,9 +432,29 @@ function normalizeComparablePath(inputPath) {
return resolveSafe(String(inputPath || '')).replace(/[\\/]+$/, '');
}
function stripRawFolderStatePrefix(folderName) {
const rawName = String(folderName || '').trim();
if (!rawName) {
return '';
}
return rawName
.replace(new RegExp(`^${RAW_INCOMPLETE_PREFIX}`, 'i'), '')
.replace(new RegExp(`^${RAW_RIP_COMPLETE_PREFIX}`, 'i'), '')
.trim();
}
function applyRawFolderPrefix(folderName, prefix = '') {
const normalized = stripRawFolderStatePrefix(folderName);
if (!normalized) {
return normalized;
}
const safePrefix = String(prefix || '').trim();
return safePrefix ? `${safePrefix}${normalized}` : normalized;
}
function parseRawFolderMetadata(folderName) {
const rawName = String(folderName || '').trim();
const normalizedRawName = rawName.replace(/^Incomplete_/i, '').trim();
const normalizedRawName = stripRawFolderStatePrefix(rawName);
const folderJobIdMatch = normalizedRawName.match(/-\s*RAW\s*-\s*job-(\d+)\s*$/i);
const folderJobId = folderJobIdMatch ? Number(folderJobIdMatch[1]) : null;
let working = normalizedRawName.replace(/\s*-\s*RAW\s*-\s*job-\d+\s*$/i, '').trim();
@@ -1053,6 +1075,7 @@ class HistoryService {
detectedTitle: effectiveTitle
});
const renameSteps = [];
let finalRawPath = absRawPath;
const renamedRawPath = buildRawPathForJobId(absRawPath, created.id);
const shouldRenameRawFolder = normalizeComparablePath(renamedRawPath) !== absRawPath;
@@ -1067,6 +1090,7 @@ class HistoryService {
try {
fs.renameSync(absRawPath, renamedRawPath);
finalRawPath = normalizeComparablePath(renamedRawPath);
renameSteps.push({ from: absRawPath, to: finalRawPath });
} catch (error) {
await db.run('DELETE FROM jobs WHERE id = ?', [created.id]);
const wrapped = new Error(`RAW-Ordner konnte nicht auf neue Job-ID umbenannt werden: ${error.message}`);
@@ -1075,6 +1099,30 @@ class HistoryService {
}
}
const ripCompleteFolderName = applyRawFolderPrefix(path.basename(finalRawPath), RAW_RIP_COMPLETE_PREFIX);
const ripCompleteRawPath = path.join(path.dirname(finalRawPath), ripCompleteFolderName);
const shouldMarkRipComplete = normalizeComparablePath(ripCompleteRawPath) !== normalizeComparablePath(finalRawPath);
if (shouldMarkRipComplete) {
if (fs.existsSync(ripCompleteRawPath)) {
await db.run('DELETE FROM jobs WHERE id = ?', [created.id]);
const error = new Error(`RAW-Ordner für Rip_Complete-Zustand existiert bereits: ${ripCompleteRawPath}`);
error.statusCode = 409;
throw error;
}
try {
const previousRawPath = finalRawPath;
fs.renameSync(previousRawPath, ripCompleteRawPath);
finalRawPath = normalizeComparablePath(ripCompleteRawPath);
renameSteps.push({ from: previousRawPath, to: finalRawPath });
} catch (error) {
await db.run('DELETE FROM jobs WHERE id = ?', [created.id]);
const wrapped = new Error(`RAW-Ordner konnte nicht als Rip_Complete markiert werden: ${error.message}`);
wrapped.statusCode = 500;
throw wrapped;
}
}
await this.updateJob(created.id, {
status: 'FINISHED',
last_state: 'FINISHED',
@@ -1105,8 +1153,8 @@ class HistoryService {
await this.appendLog(
created.id,
'SYSTEM',
shouldRenameRawFolder
? `Historieneintrag aus RAW erstellt. Ordner umbenannt: ${absRawPath} -> ${finalRawPath}`
renameSteps.length > 0
? `Historieneintrag aus RAW erstellt. Ordner umbenannt: ${renameSteps.map((step) => `${step.from} -> ${step.to}`).join(' | ')}`
: `Historieneintrag aus bestehendem RAW-Ordner erstellt: ${finalRawPath}`
);
if (metadata.imdbId) {
File diff suppressed because it is too large. Load diff
+20 -2
View File
@@ -716,7 +716,16 @@ class SettingsService {
options?.mediaProfile || deviceInfo?.mediaProfile || null
);
const cmd = map.makemkv_command;
const args = ['-r', 'info', this.resolveSourceArg(map, deviceInfo), ...splitArgs(map.makemkv_analyze_extra_args)];
const extraArgs = splitArgs(map.makemkv_analyze_extra_args);
const hasExplicitMinLength = extraArgs.some((arg) => /^--minlength(?:=|$)/i.test(String(arg || '').trim()));
const minLengthMinutes = Number(map.makemkv_min_length_minutes || 0);
const minLengthSeconds = Number.isFinite(minLengthMinutes) && minLengthMinutes > 0
? Math.round(minLengthMinutes * 60)
: 0;
const minLengthArgs = (!hasExplicitMinLength && minLengthSeconds > 0)
? [`--minlength=${minLengthSeconds}`]
: [];
const args = ['-r', ...minLengthArgs, ...extraArgs, 'info', this.resolveSourceArg(map, deviceInfo)];
logger.debug('cli:makemkv:analyze', { cmd, args, deviceInfo });
return { cmd, args };
}
@@ -726,7 +735,16 @@ class SettingsService {
const map = this.resolveEffectiveToolSettings(rawMap, options?.mediaProfile || null);
const cmd = map.makemkv_command;
const sourceArg = `file:${sourcePath}`;
const args = ['-r', 'info', sourceArg, ...splitArgs(map.makemkv_analyze_extra_args)];
const extraArgs = splitArgs(map.makemkv_analyze_extra_args);
const hasExplicitMinLength = extraArgs.some((arg) => /^--minlength(?:=|$)/i.test(String(arg || '').trim()));
const minLengthMinutes = Number(map.makemkv_min_length_minutes || 0);
const minLengthSeconds = Number.isFinite(minLengthMinutes) && minLengthMinutes > 0
? Math.round(minLengthMinutes * 60)
: 0;
const minLengthArgs = (!hasExplicitMinLength && minLengthSeconds > 0)
? [`--minlength=${minLengthSeconds}`]
: [];
const args = ['-r', ...minLengthArgs, ...extraArgs, 'info', sourceArg];
const titleIdRaw = Number(options?.titleId);
// "makemkvcon info" supports only <source>; title filtering is done in app parser.
logger.debug('cli:makemkv:analyze:path', {
+3 -1
View File
@@ -444,7 +444,9 @@ function buildBaseTrackSelectors(settings, presetProfile = null) {
explicitIds: [],
firstOnly: baseSubtitleMode === 'first',
selectionSource: profile.source === 'preset-export' ? 'preset' : 'default',
burnBehavior: normalizeBurnBehavior(profile.subtitleBurnBehavior),
// Do not auto-burn subtitle tracks from exported preset metadata.
// Burn-in should only be activated via explicit CLI args/selection.
burnBehavior: 'none',
burnedTrackId: null,
defaultTrackId: null,
forcedTrackId: null,
+172 -11
View File
@@ -1,5 +1,7 @@
const LARGE_JUMP_THRESHOLD = 20;
const DEFAULT_DURATION_SIMILARITY_SECONDS = 90;
const RAW_MIRROR_DURATION_TOLERANCE_SECONDS = 2;
const RAW_MIRROR_SIZE_TOLERANCE_BYTES = 64 * 1024 * 1024;
function parseDurationSeconds(raw) {
const text = String(raw || '').trim();
@@ -151,6 +153,7 @@ function parseAnalyzeTitles(lines) {
chapters: 0,
segmentNumbers: [],
segmentFiles: [],
streams: {},
fields: {}
});
}
@@ -164,6 +167,57 @@ function parseAnalyzeTitles(lines) {
title.playlistIdFromMap = normalizePlaylistId(mapping.playlistId);
}
const sinfo = String(line || '').match(/^SINFO:(\d+),(\d+),(\d+),\d+,"([^"]*)"/i);
if (sinfo) {
const titleId = Number(sinfo[1]);
const streamIndex = Number(sinfo[2]);
const fieldId = Number(sinfo[3]);
const value = String(sinfo[4] || '').trim();
if (
Number.isFinite(titleId) && titleId >= 0
&& Number.isFinite(streamIndex) && streamIndex >= 0
&& Number.isFinite(fieldId)
) {
const title = ensureTitle(titleId);
const streamKey = String(Math.trunc(streamIndex));
if (!title.streams[streamKey]) {
title.streams[streamKey] = {
index: Math.trunc(streamIndex),
type: null,
language: null,
languageLabel: null,
format: null,
channels: null,
description: null
};
}
const stream = title.streams[streamKey];
if (fieldId === 1) {
const lowered = value.toLowerCase();
if (lowered.includes('audio')) {
stream.type = 'audio';
} else if (lowered.includes('subtitle') || lowered.includes('untertitel') || lowered.includes('text')) {
stream.type = 'subtitle';
}
} else if (fieldId === 3) {
stream.language = value ? value.toLowerCase() : null;
} else if (fieldId === 4) {
stream.languageLabel = value || null;
} else if (fieldId === 6 || fieldId === 7) {
if (!stream.format || fieldId === 6) {
stream.format = value || null;
}
} else if (fieldId === 14 || fieldId === 40) {
if (!stream.channels || fieldId === 40) {
stream.channels = value || null;
}
} else if (fieldId === 30) {
stream.description = value || null;
}
}
continue;
}
const tinfo = String(line || '').match(/^TINFO:(\d+),(\d+),\d+,"([^"]*)"/i);
if (!tinfo) {
continue;
@@ -242,20 +296,64 @@ function parseAnalyzeTitles(lines) {
const playlistId = normalizePlaylistId(item.playlistId);
const playlistIdFromMap = normalizePlaylistId(item.playlistIdFromMap);
const playlistIdFromField16 = normalizePlaylistId(item.playlistIdFromField16);
// Prefer explicit title<->playlist map lines from MakeMKV (MSG:3016).
const resolvedPlaylistId = playlistIdFromMap || playlistIdFromField16 || playlistId;
const field16Raw = String(item?.fields?.[16] || '').trim();
const hasField16 = field16Raw.length > 0;
const field16LooksPlaylist = /\.mpls$/i.test(field16Raw) || /^\d{1,5}$/i.test(field16Raw);
const field16LooksClip = /\.(?:m2ts|m2t|mts)$/i.test(field16Raw);
let resolvedPlaylistId = null;
// TINFO:16 is part of the final title block and is more reliable than MSG:3307
// lines, which can include pre-dedup title ids.
if (field16LooksPlaylist && playlistIdFromField16) {
resolvedPlaylistId = playlistIdFromField16;
} else if (!hasField16) {
resolvedPlaylistId = playlistIdFromField16 || playlistIdFromMap || playlistId;
} else if (!field16LooksClip && playlistIdFromField16) {
resolvedPlaylistId = playlistIdFromField16;
}
const segmentNumbers = Array.isArray(item.segmentNumbers) ? item.segmentNumbers : [];
const segmentFiles = segmentNumbers
.map((number) => toSegmentFile(number))
.filter(Boolean);
const streams = item?.streams && typeof item.streams === 'object' ? Object.values(item.streams) : [];
const sortedStreams = streams
.filter((stream) => Number.isFinite(Number(stream?.index)))
.sort((a, b) => Number(a.index) - Number(b.index));
const audioTracks = sortedStreams
.filter((stream) => String(stream?.type || '').toLowerCase() === 'audio')
.map((stream) => ({
id: Number(stream.index) + 1,
sourceTrackId: Number(stream.index) + 1,
language: stream.language || 'und',
languageLabel: stream.languageLabel || stream.language || 'und',
title: stream.description || null,
format: stream.format || null,
channels: stream.channels || null
}));
const subtitleTracks = sortedStreams
.filter((stream) => String(stream?.type || '').toLowerCase() === 'subtitle')
.map((stream) => ({
id: Number(stream.index) + 1,
sourceTrackId: Number(stream.index) + 1,
language: stream.language || 'und',
languageLabel: stream.languageLabel || stream.language || 'und',
title: stream.description || null,
format: stream.format || null,
channels: null
}));
const { streams: _omitStreams, ...restItem } = item;
return {
...item,
...restItem,
playlistId: resolvedPlaylistId,
playlistIdFromMap,
playlistIdFromField16,
playlistFile: resolvedPlaylistId ? `${resolvedPlaylistId}.mpls` : null,
durationLabel: item.durationLabel || formatDuration(item.durationSeconds),
audioTracks,
subtitleTracks,
audioTrackCount: audioTracks.length,
subtitleTrackCount: subtitleTracks.length,
segmentNumbers,
segmentFiles
};
@@ -277,6 +375,58 @@ function uniqueOrdered(values) {
return output;
}
function parseReportedTitleCount(lines) {
for (let index = (Array.isArray(lines) ? lines.length : 0) - 1; index >= 0; index -= 1) {
const line = String(lines[index] || '').trim();
const match = line.match(/^TCOUNT:(\d+)/i);
if (!match) {
continue;
}
const value = Number(match[1]);
if (Number.isFinite(value) && value >= 0) {
return Math.trunc(value);
}
}
return null;
}
function likelyRawMirrorOfPlaylist(rawTitle, playlistTitle) {
const rawDuration = Number(rawTitle?.durationSeconds || 0);
const playlistDuration = Number(playlistTitle?.durationSeconds || 0);
const rawSize = Number(rawTitle?.sizeBytes || 0);
const playlistSize = Number(playlistTitle?.sizeBytes || 0);
if (!Number.isFinite(rawDuration) || !Number.isFinite(playlistDuration) || rawDuration <= 0 || playlistDuration <= 0) {
return false;
}
if (Math.abs(rawDuration - playlistDuration) > RAW_MIRROR_DURATION_TOLERANCE_SECONDS) {
return false;
}
if (rawSize > 0 && playlistSize > 0) {
return Math.abs(rawSize - playlistSize) <= RAW_MIRROR_SIZE_TOLERANCE_BYTES;
}
return true;
}
function suppressRawMirrorCandidates(candidates) {
const rows = Array.isArray(candidates) ? candidates : [];
if (rows.length <= 1) {
return rows;
}
const playlistRows = rows.filter((item) => normalizePlaylistId(item?.playlistId));
if (playlistRows.length === 0) {
return rows;
}
return rows.filter((item) => {
if (normalizePlaylistId(item?.playlistId)) {
return true;
}
return !playlistRows.some((playlistRow) => likelyRawMirrorOfPlaylist(item, playlistRow));
});
}
function buildSimilarityGroups(candidates, durationSimilaritySeconds) {
const list = Array.isArray(candidates) ? [...candidates] : [];
const tolerance = Math.max(0, Math.round(Number(durationSimilaritySeconds || 0)));
@@ -506,37 +656,45 @@ function extractPlaylistMismatchWarnings(titles) {
.filter((title) => String(title.playlistIdFromMap) !== String(title.playlistIdFromField16))
.slice(0, 25)
.map((title) =>
`Titel #${title.titleId}: MSG-Playlist=${title.playlistIdFromMap}.mpls, TINFO16=${title.playlistIdFromField16}.mpls (MSG bevorzugt)`
`Titel #${title.titleId}: MSG-Playlist=${title.playlistIdFromMap}.mpls, TINFO16=${title.playlistIdFromField16}.mpls (TINFO16 bevorzugt)`
);
}
function analyzePlaylistObfuscation(lines, minLengthMinutes = 60, options = {}) {
const parsedTitles = parseAnalyzeTitles(lines);
const reportedTitleCount = parseReportedTitleCount(lines);
const minSeconds = Math.max(0, Math.round(Number(minLengthMinutes || 0) * 60));
const durationSimilaritySeconds = Math.max(
0,
Math.round(Number(options.durationSimilaritySeconds || DEFAULT_DURATION_SIMILARITY_SECONDS))
);
const candidates = parsedTitles
const candidatesRaw = parsedTitles
.filter((item) => Number(item.durationSeconds || 0) >= minSeconds)
.sort((a, b) => b.durationSeconds - a.durationSeconds || b.sizeBytes - a.sizeBytes || a.titleId - b.titleId);
const candidates = suppressRawMirrorCandidates(candidatesRaw)
.slice()
.sort((a, b) => b.durationSeconds - a.durationSeconds || b.sizeBytes - a.sizeBytes || a.titleId - b.titleId);
const playlistBackedCandidates = candidates
.filter((item) => normalizePlaylistId(item?.playlistId));
const candidatePlaylistsAll = uniqueOrdered(
playlistBackedCandidates.map((item) => item.playlistId).filter(Boolean)
);
const similarityGroups = buildSimilarityGroups(candidates, durationSimilaritySeconds);
const similarityGroups = buildSimilarityGroups(playlistBackedCandidates, durationSimilaritySeconds);
const obfuscationDetected = similarityGroups.length > 0;
const multipleCandidatesDetected = candidates.length > 1;
const multipleCandidatesDetected = candidatePlaylistsAll.length > 1;
const manualDecisionRequired = multipleCandidatesDetected;
const decisionPool = manualDecisionRequired ? candidates : [];
const decisionPool = manualDecisionRequired ? playlistBackedCandidates : [];
const evaluatedCandidates = decisionPool.length > 0 ? scoreCandidates(decisionPool) : [];
const recommendation = evaluatedCandidates[0] || null;
const candidatePlaylists = manualDecisionRequired
? uniqueOrdered(decisionPool.map((item) => item.playlistId).filter(Boolean))
: [];
const candidatePlaylists = manualDecisionRequired ? candidatePlaylistsAll : [];
const playlistSegments = buildPlaylistSegmentMap(decisionPool);
const playlistToTitleId = buildPlaylistToTitleIdMap(parsedTitles);
return {
generatedAt: new Date().toISOString(),
reportedTitleCount,
minLengthMinutes: Number(minLengthMinutes || 0),
minLengthSeconds: minSeconds,
durationSimilaritySeconds,
@@ -570,6 +728,9 @@ function analyzePlaylistObfuscation(lines, minLengthMinutes = 60, options = {})
},
warningLines: [
...extractWarningLines(lines),
...(reportedTitleCount !== null && reportedTitleCount !== parsedTitles.length
? [`Titel-Anzahl abweichend: TCOUNT=${reportedTitleCount}, geparst=${parsedTitles.length}`]
: []),
...extractPlaylistMismatchWarnings(parsedTitles)
].slice(0, 60)
};
+3 -3
View File
@@ -33,12 +33,12 @@ function parseMakeMkvProgress(line) {
const prgv = line.match(/PRGV:(\d+),(\d+),(\d+)/);
if (prgv) {
// Format: PRGV:current,total,max (official makemkv docs)
// progress = current / max
const current = Number(prgv[1]);
// current = per-file progress, total = overall progress across all files
const total = Number(prgv[2]);
const max = Number(prgv[3]);
if (max > 0) {
return { percent: clampPercent((current / max) * 100), eta: null };
return { percent: clampPercent((total / max) * 100), eta: null };
}
}
+2
View File
@@ -31,6 +31,8 @@ sshpass -p "$SSH_PASSWORD" ssh $SSH_OPTS "${REMOTE_USER}@${REMOTE_HOST}" "set -e
echo "Uebertrage lokalen Ordner ${LOCAL_PATH} nach ${REMOTE_TARGET} ..."
echo "backend/data wird weder uebertragen noch auf dem Ziel geloescht: ${DATA_RELATIVE_DIR}"
sshpass -p "$SSH_PASSWORD" rsync -az --progress --delete \
--exclude "${DATA_RELATIVE_DIR}" \
--filter "protect ${DATA_RELATIVE_DIR}" \
--filter "protect debug" \
-e "ssh $SSH_OPTS" \
"${LOCAL_PATH}/" "${REMOTE_TARGET}/"
+62 -123
View File
@@ -1,16 +1,12 @@
# Cron API
Ripster enthält ein eingebautes Cron-System, mit dem **Skripte** und **Skript-Ketten** zeitgesteuert oder manuell ausgeführt werden können. Der Cron-Dienst benötigt keine externen Pakete – der Cron-Expression-Parser ist vollständig im Backend implementiert.
Ripster enthält ein eingebautes Cron-System für Skripte und Skript-Ketten (`sourceType: script|chain`).
---
## Endpunkte
## GET /api/crons
### `GET /api/crons`
Alle konfigurierten Cron-Jobs auflisten.
**Antwort:**
Listet alle Cron-Jobs.
```json
{
@@ -21,13 +17,14 @@ Alle konfigurierten Cron-Jobs auflisten.
"cronExpression": "0 2 * * *",
"sourceType": "script",
"sourceId": 3,
"sourceName": "Backup-Skript",
"enabled": true,
"pushoverEnabled": true,
"lastRunAt": "2026-03-09T02:00:00.000Z",
"lastRunAt": "2026-03-10T02:00:00.000Z",
"lastRunStatus": "success",
"nextRunAt": "2026-03-10T02:00:00.000Z",
"nextRunAt": "2026-03-11T02:00:00.000Z",
"createdAt": "2026-03-01T10:00:00.000Z",
"updatedAt": "2026-03-09T02:00:00.000Z"
"updatedAt": "2026-03-10T02:00:05.000Z"
}
]
}
@@ -35,11 +32,9 @@ Alle konfigurierten Cron-Jobs auflisten.
---
### `POST /api/crons`
## POST /api/crons
Neuen Cron-Job anlegen.
**Body:**
Erstellt Cron-Job.
```json
{
@@ -52,16 +47,25 @@ Neuen Cron-Job anlegen.
}
```
| Feld | Typ | Pflicht | Beschreibung |
|------|-----|---------|-------------|
| `name` | string | ✓ | Anzeigename |
| `cronExpression` | string | ✓ | 5-Felder-Cron-Ausdruck (Minute Stunde Tag Monat Wochentag) |
| `sourceType` | string | ✓ | `"script"` oder `"chain"` |
| `sourceId` | number | ✓ | ID des Skripts bzw. der Kette |
| `enabled` | boolean | – | Aktiviert (default: `true`) |
| `pushoverEnabled` | boolean | – | PushOver-Benachrichtigung nach Ausführung (default: `true`) |
Response: `201` mit `{ "job": { ... } }`
**Antwort:** `201 Created`
---
## GET /api/crons/:id
Response:
```json
{ "job": { "id": 1, "name": "..." } }
```
---
## PUT /api/crons/:id
Aktualisiert Cron-Job. Felder wie bei `POST`.
Response:
```json
{ "job": { ... } }
@@ -69,53 +73,27 @@ Neuen Cron-Job anlegen.
---
### `GET /api/crons/:id`
## DELETE /api/crons/:id
Einzelnen Cron-Job abrufen.
**Antwort:**
Response:
```json
{ "job": { ... } }
{ "removed": { "id": 1, "name": "Nachtlauf Backup" } }
```
---
### `PUT /api/crons/:id`
## GET /api/crons/:id/logs
Cron-Job aktualisieren. Body-Felder entsprechen `POST /api/crons`.
**Antwort:**
```json
{ "job": { ... } }
```
---
### `DELETE /api/crons/:id`
Cron-Job löschen.
**Antwort:**
```json
{ "removed": { "id": 1 } }
```
---
### `GET /api/crons/:id/logs`
Ausführungs-Logs eines Cron-Jobs abrufen.
Liefert Ausführungs-Logs.
**Query-Parameter:**
| Parameter | Typ | Default | Beschreibung |
|-----------|-----|---------|-------------|
| `limit` | number | 20 | Anzahl Einträge (max. 100) |
| `limit` | number | `20` | Anzahl Einträge, max. `100` |
**Antwort:**
**Response:**
```json
{
@@ -123,62 +101,54 @@ Ausführungs-Logs eines Cron-Jobs abrufen.
{
"id": 42,
"cronJobId": 1,
"startedAt": "2026-03-09T02:00:01.000Z",
"finishedAt": "2026-03-09T02:00:05.000Z",
"startedAt": "2026-03-10T02:00:01.000Z",
"finishedAt": "2026-03-10T02:00:05.000Z",
"status": "success",
"exitCode": 0,
"stdout": "Backup abgeschlossen.",
"stderr": "",
"triggeredBy": "cron"
"output": "Backup abgeschlossen.",
"errorMessage": null
}
]
}
```
| Feld | Beschreibung |
|------|-------------|
| `status` | `"success"`, `"error"` oder `"running"` |
| `triggeredBy` | `"cron"` (zeitgesteuert) oder `"manual"` (manuell ausgelöst) |
`status`: `running` | `success` | `error`
---
### `POST /api/crons/:id/run`
## POST /api/crons/:id/run
Cron-Job sofort manuell auslösen (unabhängig vom Zeitplan).
Triggert Job manuell (asynchron).
**Antwort:**
**Response:**
```json
{
"status": "success",
"exitCode": 0,
"stdout": "...",
"stderr": ""
}
{ "triggered": true, "cronJobId": 1 }
```
Wenn Job bereits läuft: `409`.
---
### `POST /api/crons/validate-expression`
## POST /api/crons/validate-expression
Cron-Ausdruck validieren und nächsten Ausführungszeitpunkt berechnen.
Validiert 5-Felder-Cron-Ausdruck und berechnet nächsten Lauf.
**Body:**
**Request:**
```json
{ "cronExpression": "*/15 * * * *" }
```
**Antwort (gültig):**
**Gültige Response:**
```json
{
"valid": true,
"nextRunAt": "2026-03-09T14:15:00.000Z"
"nextRunAt": "2026-03-10T14:15:00.000Z"
}
```
**Antwort (ungültig):**
**Ungültige Response:**
```json
{
@@ -190,54 +160,23 @@ Cron-Ausdruck validieren und nächsten Ausführungszeitpunkt berechnen.
---
## Cron-Expression-Format
## Cron-Format
Ripster verwendet **5-Felder-Cron-Ausdrücke** (kein Sekunden-Feld):
Ripster unterstützt 5 Felder:
```
┌───────────── Minute (0-59)
│ ┌────────── Stunde (0-23)
│ │ ┌─────── Tag (1-31)
│ │ │ ┌──── Monat (1-12)
│ │ │ │ ┌─ Wochentag (0-7, 0 und 7 = Sonntag)
│ │ │ │ │
* * * * *
```text
Minute Stunde Tag Monat Wochentag
```
### Beispiele
Beispiele:
| Ausdruck | Beschreibung |
|----------|-------------|
| `0 2 * * *` | Täglich um 02:00 Uhr |
| `*/15 * * * *` | Alle 15 Minuten |
| `0 6 * * 1-5` | Montag–Freitag um 06:00 Uhr |
| `30 23 * * 0` | Sonntags um 23:30 Uhr |
| `0 0 1 * *` | Erster Tag des Monats um Mitternacht |
### Unterstützte Syntax
| Syntax | Bedeutung |
|--------|----------|
| `*` | Jeder Wert |
| `*/n` | Jeder n-te Wert (Step) |
| `a-b` | Bereich von a bis b |
| `a,b,c` | Liste von Werten |
| Kombinierbar | z. B. `1,5-10,*/3` |
- `0 2 * * *` täglich 02:00
- `*/15 * * * *` alle 15 Minuten
- `0 6 * * 1-5` Mo-Fr 06:00
---
## WebSocket-Event
## WebSocket-Events zu Cron
Bei Änderungen an Cron-Jobs (Anlegen, Aktualisieren, Löschen) wird ein `CRON_JOBS_UPDATED`-Event gesendet:
```json
{
"type": "CRON_JOBS_UPDATED",
"payload": {
"action": "created",
"id": 1
}
}
```
`action` ist `"created"`, `"updated"` oder `"deleted"`.
- `CRON_JOBS_UPDATED` bei Create/Update/Delete
- `CRON_JOB_UPDATED` bei Laufzeitstatus (`running` -> `success|error`)
+98 -79
View File
@@ -1,23 +1,23 @@
# History API
Endpunkte für die Job-Histoire, Dateimanagement und Orphan-Import.
Endpunkte für Job-Historie, Orphan-Import und Löschoperationen.
---
## GET /api/history
Gibt eine Liste aller Jobs zurück, optional gefiltert.
Liefert Jobs (optionale Filter).
**Query-Parameter:**
| Parameter | Typ | Beschreibung |
|----------|-----|-------------|
| `status` | string | Filtert nach Status (z.B. `FINISHED`, `ERROR`) |
| `search` | string | Sucht in Filmtiteln |
| `status` | string | Filter nach Job-Status |
| `search` | string | Suche in Titel-Feldern |
**Beispiel:**
```
```text
GET /api/history?status=FINISHED&search=Inception
```
@@ -30,17 +30,15 @@ GET /api/history?status=FINISHED&search=Inception
"id": 42,
"status": "FINISHED",
"title": "Inception",
"imdb_id": "tt1375666",
"omdb_year": "2010",
"omdb_type": "movie",
"omdb_poster": "https://...",
"raw_path": "/mnt/nas/raw/Inception_t00.mkv",
"output_path": "/mnt/nas/movies/Inception (2010).mkv",
"created_at": "2024-01-15T10:00:00.000Z",
"updated_at": "2024-01-15T12:30:00.000Z"
"raw_path": "/mnt/raw/Inception - RAW - job-42",
"output_path": "/mnt/movies/Inception (2010)/Inception (2010).mkv",
"mediaType": "bluray",
"ripSuccessful": true,
"encodeSuccess": true,
"created_at": "2026-03-10T08:00:00.000Z",
"updated_at": "2026-03-10T10:00:00.000Z"
}
],
"total": 1
]
}
```
@@ -48,34 +46,37 @@ GET /api/history?status=FINISHED&search=Inception
## GET /api/history/:id
Gibt Detail-Informationen für einen einzelnen Job zurück.
**URL-Parameter:** `id` – Job-ID
Liefert Job-Detail.
**Query-Parameter:**
| Parameter | Typ | Standard | Beschreibung |
|----------|-----|---------|-------------|
| `includeLogs` | boolean | `false` | Log-Inhalte einschließen |
| `includeLiveLog` | boolean | `false` | Aktuellen Live-Log einschließen |
| `includeLogs` | bool | `false` | Prozesslog laden |
| `includeLiveLog` | bool | `false` | alias-artig ebenfalls Prozesslog laden |
| `includeAllLogs` | bool | `false` | vollständiges Log statt Tail |
| `logTailLines` | number | `800` | Tail-Länge falls nicht `includeAllLogs` |
**Response:**
```json
{
"id": 42,
"status": "FINISHED",
"title": "Inception",
"imdb_id": "tt1375666",
"encode_plan": { ... },
"makemkv_output": { ... },
"mediainfo_output": { ... },
"handbrake_log": "/path/to/log",
"logs": {
"handbrake": "Encoding: task 1 of 1, 100.0%\n..."
},
"created_at": "2024-01-15T10:00:00.000Z",
"updated_at": "2024-01-15T12:30:00.000Z"
"job": {
"id": 42,
"status": "FINISHED",
"makemkvInfo": {},
"mediainfoInfo": {},
"handbrakeInfo": {},
"encodePlan": {},
"log": "...",
"log_count": 1,
"logMeta": {
"loaded": true,
"total": 800,
"returned": 800,
"truncated": true
}
}
}
```
@@ -83,14 +84,19 @@ Gibt Detail-Informationen für einen einzelnen Job zurück.
## GET /api/history/database
Gibt alle rohen Datenbankzeilen zurück (Debug-Ansicht).
Debug-Ansicht der DB-Zeilen (angereichert).
**Response:**
```json
{
"jobs": [ { "id": 1, "status": "FINISHED", ... } ],
"total": 15
"rows": [
{
"id": 42,
"status": "FINISHED",
"rawFolderName": "Inception - RAW - job-42"
}
]
}
```
@@ -98,18 +104,25 @@ Gibt alle rohen Datenbankzeilen zurück (Debug-Ansicht).
## GET /api/history/orphan-raw
Findet Raw-Ordner, die nicht als Jobs in der Datenbank registriert sind.
Sucht RAW-Ordner ohne zugehörigen Job.
**Response:**
```json
{
"orphans": [
"rawDir": "/mnt/raw",
"rawDirs": ["/mnt/raw", "/mnt/raw-bluray"],
"rows": [
{
"path": "/mnt/nas/raw/UnknownMovie_2023-12-01",
"size": "45.2 GB",
"modifiedAt": "2023-12-01T15:00:00.000Z",
"files": ["t00.mkv", "t01.mkv"]
"rawPath": "/mnt/raw/Inception (2010) [tt1375666] - RAW - job-99",
"folderName": "Inception (2010) [tt1375666] - RAW - job-99",
"title": "Inception",
"year": 2010,
"imdbId": "tt1375666",
"folderJobId": 99,
"entryCount": 4,
"hasBlurayStructure": true,
"lastModifiedAt": "2026-03-10T09:00:00.000Z"
}
]
}
@@ -119,35 +132,28 @@ Findet Raw-Ordner, die nicht als Jobs in der Datenbank registriert sind.
## POST /api/history/orphan-raw/import
Importiert einen Orphan-Raw-Ordner als Job in die Datenbank.
Importiert RAW-Ordner als FINISHED-Job.
**Request:**
```json
{
"path": "/mnt/nas/raw/UnknownMovie_2023-12-01"
}
{ "rawPath": "/mnt/raw/Inception (2010) [tt1375666] - RAW - job-99" }
```
**Response:**
```json
{
"ok": true,
"jobId": 99,
"message": "Orphan-Ordner als Job importiert"
"job": { "id": 77, "status": "FINISHED" },
"uiReset": { "reset": true, "state": "IDLE" }
}
```
Nach dem Import kann dem Job über `/api/history/:id/omdb/assign` Metadaten zugewiesen werden.
---
## POST /api/history/:id/omdb/assign
Weist einem bestehenden Job OMDb-Metadaten nachträglich zu.
**URL-Parameter:** `id` – Job-ID
Weist OMDb-/Metadaten nachträglich zu.
**Request:**
@@ -155,44 +161,42 @@ Weist einem bestehenden Job OMDb-Metadaten nachträglich zu.
{
"imdbId": "tt1375666",
"title": "Inception",
"year": "2010",
"type": "movie",
"poster": "https://..."
"year": 2010,
"poster": "https://...",
"fromOmdb": true
}
```
**Response:**
```json
{ "ok": true }
{ "job": { "id": 42, "imdb_id": "tt1375666" } }
```
---
## POST /api/history/:id/delete-files
Löscht die Dateien eines Jobs (Raw und/oder Output), behält den Job-Eintrag.
**URL-Parameter:** `id` – Job-ID
Löscht Dateien eines Jobs, behält DB-Eintrag.
**Request:**
```json
{
"deleteRaw": true,
"deleteOutput": false
}
{ "target": "both" }
```
`target`: `raw` | `movie` | `both`
**Response:**
```json
{
"ok": true,
"deleted": {
"raw": "/mnt/nas/raw/Inception_t00.mkv",
"output": null
}
"summary": {
"target": "both",
"raw": { "attempted": true, "deleted": true, "filesDeleted": 12, "dirsRemoved": 3, "reason": null },
"movie": { "attempted": true, "deleted": false, "filesDeleted": 0, "dirsRemoved": 0, "reason": "Movie-Datei/Pfad existiert nicht." }
},
"job": { "id": 42 }
}
```
@@ -200,23 +204,38 @@ Löscht die Dateien eines Jobs (Raw und/oder Output), behält den Job-Eintrag.
## POST /api/history/:id/delete
Löscht den Job-Eintrag aus der Datenbank, optional auch die Dateien.
**URL-Parameter:** `id` – Job-ID
Löscht Job aus DB; optional auch Dateien.
**Request:**
```json
{
"deleteFiles": true
}
{ "target": "none" }
```
`target`: `none` | `raw` | `movie` | `both`
**Response:**
```json
{ "ok": true, "message": "Job gelöscht" }
{
"deleted": true,
"jobId": 42,
"fileTarget": "both",
"fileSummary": {
"target": "both",
"raw": { "filesDeleted": 10 },
"movie": { "filesDeleted": 1 }
},
"uiReset": {
"reset": true,
"state": "IDLE"
}
}
```
!!! warning "Unwiderruflich"
Das Löschen von Jobs und Dateien ist nicht rückgängig zu machen.
---
## Hinweise
- Ein aktiver Pipeline-Job kann nicht gelöscht werden (`409`).
- Alle Löschoperationen sind irreversibel.
+44 -20
View File
@@ -1,16 +1,21 @@
# API-Referenz
Ripster bietet eine **REST-API** für alle Operationen sowie einen **WebSocket-Endpunkt** für Echtzeit-Updates.
Ripster bietet eine REST-API für Steuerung/Verwaltung sowie einen WebSocket-Endpunkt für Echtzeit-Updates.
---
## Basis-URL
```
```text
http://localhost:3001
```
Konfigurierbar über die Umgebungsvariable `PORT`.
API-Prefix: `/api`
Beispiele:
- `GET /api/health`
- `GET /api/pipeline/state`
---
@@ -18,11 +23,19 @@ Konfigurierbar über die Umgebungsvariable `PORT`.
<div class="grid cards" markdown>
- :material-heart-pulse: **Health**
---
Service-Liveness.
`GET /api/health`
- :material-pipe: **Pipeline API**
---
Pipeline-Steuerung: Analyse starten, Metadaten setzen, Ripping und Encoding steuern.
Analyse, Start/Retry/Cancel, Queue, Re-Encode.
[:octicons-arrow-right-24: Pipeline API](pipeline.md)
@@ -30,7 +43,7 @@ Konfigurierbar über die Umgebungsvariable `PORT`.
---
Einstellungen lesen und schreiben.
Einstellungen, Skripte/Ketten, User-Presets.
[:octicons-arrow-right-24: Settings API](settings.md)
@@ -38,7 +51,7 @@ Konfigurierbar über die Umgebungsvariable `PORT`.
---
Job-Geschichte abfragen, Jobs löschen, Orphan-Ordner importieren.
Job-Historie, Orphan-Import, Löschoperationen.
[:octicons-arrow-right-24: History API](history.md)
@@ -46,7 +59,7 @@ Konfigurierbar über die Umgebungsvariable `PORT`.
---
Cron-Jobs verwalten, manuell auslösen und Ausführungs-Logs abrufen.
Zeitgesteuerte Skript-/Kettenausführung.
[:octicons-arrow-right-24: Cron API](crons.md)
@@ -54,7 +67,7 @@ Konfigurierbar über die Umgebungsvariable `PORT`.
---
Echtzeit-Events für Pipeline-Status, Fortschritt und Disc-Erkennung.
Pipeline-, Queue-, Disk-, Settings-, Cron- und Monitoring-Events.
[:octicons-arrow-right-24: WebSocket](websocket.md)
@@ -64,30 +77,41 @@ Konfigurierbar über die Umgebungsvariable `PORT`.
## Authentifizierung
Die API hat **keine Authentifizierung**. Sie ist für den Einsatz im lokalen Netzwerk konzipiert.
!!! warning "Produktionsbetrieb"
Falls Ripster öffentlich erreichbar sein soll, schütze die API mit einem Reverse-Proxy (z. B. nginx mit Basic Auth oder OAuth).
Es gibt keine eingebaute Authentifizierung. Ripster ist für lokalen Betrieb gedacht.
---
## Fehlerformat
Alle API-Fehler werden im folgenden Format zurückgegeben:
Fehler werden zentral als JSON geliefert:
```json
{
"error": "Job nicht gefunden",
"details": "Kein Job mit ID 999 vorhanden"
"error": {
"message": "Job nicht gefunden.",
"statusCode": 404,
"reqId": "req_...",
"details": [
{
"field": "name",
"message": "Name darf nicht leer sein."
}
]
}
}
```
HTTP-Statuscodes:
`details` ist optional (z. B. bei Validierungsfehlern).
---
## Häufige Statuscodes
| Code | Bedeutung |
|-----|-----------|
|------|-----------|
| `200` | Erfolg |
| `400` | Ungültige Anfrage |
| `201` | Ressource erstellt |
| `400` | Ungültige Anfrage / Validierungsfehler |
| `404` | Ressource nicht gefunden |
| `409` | Konflikt (z.B. Pipeline bereits aktiv) |
| `500` | Interner Serverfehler |
| `409` | Konflikt (z. B. falscher Pipeline-Zustand, Job läuft bereits) |
| `500` | Interner Fehler |
+188 -86
View File
@@ -1,14 +1,14 @@
# Pipeline API
Alle Endpunkte zur Steuerung des Ripster-Workflows.
Endpunkte zur Steuerung des Pipeline-Workflows.
---
## GET /api/pipeline/state
Liefert den aktuellen Pipeline-Snapshot.
Liefert aktuellen Pipeline- und Hardware-Monitoring-Snapshot.
**Response:**
**Response (Beispiel):**
```json
{
@@ -17,45 +17,46 @@ Liefert den aktuellen Pipeline-Snapshot.
"activeJobId": 42,
"progress": 0,
"eta": null,
"statusText": "Mediainfo geladen - bitte bestätigen",
"statusText": "Mediainfo bestätigt - Encode manuell starten",
"context": {
"jobId": 42
},
"jobProgress": {
"42": {
"state": "MEDIAINFO_CHECK",
"progress": 68.5,
"eta": null,
"statusText": "MEDIAINFO_CHECK 68.50%"
}
},
"queue": {
"maxParallelJobs": 1,
"runningCount": 0,
"queuedCount": 0,
"runningCount": 1,
"queuedCount": 2,
"runningJobs": [],
"queuedJobs": []
}
},
"hardwareMonitoring": {
"enabled": true,
"intervalMs": 5000,
"updatedAt": "2026-03-10T09:00:00.000Z",
"sample": {
"cpu": {},
"memory": {},
"gpu": {},
"storage": {}
},
"error": null
}
}
```
**Pipeline-Zustände:**
| Wert | Beschreibung |
|------|-------------|
| `IDLE` | Wartet auf Medium |
| `DISC_DETECTED` | Medium erkannt, wartet auf Analyse-Start |
| `METADATA_SELECTION` | Metadaten-Dialog aktiv |
| `WAITING_FOR_USER_DECISION` | Manuelle Playlist-Auswahl erforderlich |
| `READY_TO_START` | Übergang/Fallback vor Start |
| `RIPPING` | MakeMKV läuft |
| `MEDIAINFO_CHECK` | HandBrake-Scan + Plan-Erstellung |
| `READY_TO_ENCODE` | Review bereit |
| `ENCODING` | HandBrake-Encoding läuft (inkl. Post-Skripte) |
| `FINISHED` | Abgeschlossen |
| `CANCELLED` | Vom Benutzer abgebrochen |
| `ERROR` | Fehler |
---
## POST /api/pipeline/analyze
Startet die Analyse für die aktuell erkannte Disc.
**Request:** kein Body
Startet Disc-Analyse und legt Job an.
**Response:**
@@ -73,14 +74,21 @@ Startet die Analyse für die aktuell erkannte Disc.
## POST /api/pipeline/rescan-disc
Erzwingt eine erneute Laufwerksprüfung.
Erzwingt erneute Laufwerksprüfung.
**Response (Beispiel):**
```json
{
"result": {
"emitted": "discInserted"
"present": true,
"changed": true,
"emitted": "discInserted",
"device": {
"path": "/dev/sr0",
"discLabel": "INCEPTION",
"mediaProfile": "bluray"
}
}
}
```
@@ -89,7 +97,7 @@ Erzwingt eine erneute Laufwerksprüfung.
## GET /api/pipeline/omdb/search?q=<query>
Sucht OMDb-Titel.
OMDb-Titelsuche.
**Response:**
@@ -111,7 +119,7 @@ Sucht OMDb-Titel.
## POST /api/pipeline/select-metadata
Setzt Metadaten (und optional Playlist-Entscheidung).
Setzt Metadaten (und optional Playlist) für einen Job.
**Request:**
@@ -127,38 +135,35 @@ Setzt Metadaten (und optional Playlist-Entscheidung).
}
```
**Response:** `{ "job": { ... } }`
**Response:**
!!! note "Startlogik"
Nach Metadaten-Bestätigung wird der nächste Schritt automatisch ausgelöst (`startPreparedJob`).
Der Job startet direkt oder wird in die Queue eingereiht.
```json
{ "job": { "id": 42, "status": "READY_TO_START" } }
```
---
## POST /api/pipeline/start/:jobId
Startet einen vorbereiteten Job manuell (z. B. Fallback/Queue-Szenario).
Startet vorbereiteten Job oder queued ihn (je nach Parallel-Limit).
**Response (Beispiel):**
**Mögliche Responses:**
```json
{
"result": {
"started": true,
"stage": "RIPPING"
}
}
{ "result": { "started": true, "stage": "RIPPING" } }
```
Mögliche `stage`-Werte sind u. a. `RIPPING`, `MEDIAINFO_CHECK`, `ENCODING`.
```json
{ "result": { "queued": true, "started": false, "queuePosition": 2, "action": "START_PREPARED" } }
```
---
## POST /api/pipeline/confirm-encode/:jobId
Bestätigt Review-Auswahl (Titel/Tracks/Post-Skripte).
Bestätigt Review-Auswahl (Tracks, Pre/Post-Skripte/Ketten, User-Preset).
**Request:**
**Request (typisch):**
```json
{
@@ -169,78 +174,70 @@ Bestätigt Review-Auswahl (Titel/Tracks/Post-Skripte).
"subtitleTrackIds": [3]
}
},
"selectedPreEncodeScriptIds": [1],
"selectedPostEncodeScriptIds": [2, 7],
"selectedPreEncodeChainIds": [3],
"selectedPostEncodeChainIds": [4],
"selectedUserPresetId": 5,
"skipPipelineStateUpdate": false
}
```
**Response:** `{ "job": { ... } }`
**Response:**
```json
{ "job": { "id": 42, "encode_review_confirmed": 1 } }
```
---
## POST /api/pipeline/cancel
Bricht laufenden Job ab oder entfernt einen Queue-Eintrag.
Bricht laufenden Job ab oder entfernt Queue-Eintrag.
**Request (optional):**
```json
{
"jobId": 42
}
{ "jobId": 42 }
```
**Response (Beispiel):**
**Mögliche Responses:**
```json
{
"result": {
"cancelled": true,
"queuedOnly": false,
"jobId": 42
}
}
{ "result": { "cancelled": true, "queuedOnly": true, "jobId": 42 } }
```
```json
{ "result": { "cancelled": true, "queuedOnly": false, "jobId": 42 } }
```
```json
{ "result": { "cancelled": true, "queuedOnly": false, "pending": true, "jobId": 42 } }
```
---
## POST /api/pipeline/retry/:jobId
Startet einen Job aus `ERROR`/`CANCELLED` erneut (oder reiht ihn in die Queue ein).
**Response:** `{ "result": { ... } }`
---
## POST /api/pipeline/resume-ready/:jobId
Lädt einen `READY_TO_ENCODE`-Job nach Neustart wieder in die aktive Session.
**Response:** `{ "job": { ... } }`
---
Retry für `ERROR`/`CANCELLED`-Jobs (oder Queue-Einreihung).
## POST /api/pipeline/reencode/:jobId
Startet Re-Encode aus bestehendem RAW.
**Response:** `{ "result": { ... } }`
---
## POST /api/pipeline/restart-review/:jobId
Berechnet die Review aus vorhandenem RAW neu.
**Response:** `{ "result": { ... } }`
---
Berechnet Review aus RAW neu.
## POST /api/pipeline/restart-encode/:jobId
Startet Encoding mit der zuletzt bestätigten Auswahl neu.
Startet Encoding mit letzter bestätigter Review neu.
**Response:** `{ "result": { ... } }`
## POST /api/pipeline/resume-ready/:jobId
Lädt `READY_TO_ENCODE`-Job nach Neustart wieder in aktive Session.
Alle Endpunkte liefern `{ result: ... }` bzw. `{ job: ... }`.
---
@@ -248,9 +245,51 @@ Startet Encoding mit der zuletzt bestätigten Auswahl neu.
### GET /api/pipeline/queue
Liefert den aktuellen Queue-Status.
Liefert Queue-Snapshot.
**Response:** `{ "queue": { ... } }`
```json
{
"queue": {
"maxParallelJobs": 1,
"runningCount": 1,
"queuedCount": 3,
"runningJobs": [
{
"jobId": 41,
"title": "Inception",
"status": "ENCODING",
"lastState": "ENCODING"
}
],
"queuedJobs": [
{
"entryId": 11,
"position": 1,
"type": "job",
"jobId": 42,
"action": "START_PREPARED",
"actionLabel": "Start",
"title": "Matrix",
"status": "READY_TO_ENCODE",
"lastState": "READY_TO_ENCODE",
"hasScripts": true,
"hasChains": false,
"enqueuedAt": "2026-03-10T09:00:00.000Z"
},
{
"entryId": 12,
"position": 2,
"type": "wait",
"waitSeconds": 30,
"title": "Warten 30s",
"status": "QUEUED",
"enqueuedAt": "2026-03-10T09:01:00.000Z"
}
],
"updatedAt": "2026-03-10T09:01:02.000Z"
}
}
```
### POST /api/pipeline/queue/reorder
@@ -260,8 +299,71 @@ Sortiert Queue-Einträge neu.
```json
{
"orderedJobIds": [42, 43, 41]
"orderedEntryIds": [12, 11]
}
```
**Response:** `{ "queue": { ... } }`
Legacy fallback wird akzeptiert:
```json
{
"orderedJobIds": [42, 43]
}
```
### POST /api/pipeline/queue/entry
Fügt Nicht-Job-Queue-Eintrag hinzu (`script`, `chain`, `wait`).
**Request-Beispiele:**
```json
{ "type": "script", "scriptId": 3 }
```
```json
{ "type": "chain", "chainId": 2, "insertAfterEntryId": 11 }
```
```json
{ "type": "wait", "waitSeconds": 45 }
```
**Response:**
```json
{
"result": { "entryId": 12, "type": "wait", "position": 2 },
"queue": { "...": "..." }
}
```
### DELETE /api/pipeline/queue/entry/:entryId
Entfernt Queue-Eintrag.
**Response:**
```json
{ "queue": { "...": "..." } }
```
---
## Pipeline-Zustände
| State | Bedeutung |
|------|-----------|
| `IDLE` | Wartet auf Medium |
| `DISC_DETECTED` | Medium erkannt |
| `ANALYZING` | MakeMKV-Analyse läuft |
| `METADATA_SELECTION` | Metadaten-Auswahl |
| `WAITING_FOR_USER_DECISION` | Playlist-Entscheidung erforderlich |
| `READY_TO_START` | Übergang vor Start |
| `RIPPING` | MakeMKV-Rip läuft |
| `MEDIAINFO_CHECK` | Titel-/Track-Auswertung |
| `READY_TO_ENCODE` | Review bereit |
| `ENCODING` | HandBrake-Encoding läuft |
| `FINISHED` | Abgeschlossen |
| `CANCELLED` | Abgebrochen |
| `ERROR` | Fehler |
+226 -242
View File
@@ -1,38 +1,36 @@
# Settings API
Endpunkte zum Lesen und Schreiben der Anwendungseinstellungen.
Endpunkte für Einstellungen, Skripte, Skript-Ketten und User-Presets.
---
## GET /api/settings
Gibt alle Einstellungen kategorisiert zurück.
Liefert alle Einstellungen kategorisiert.
**Response:**
**Response (Struktur):**
```json
{
"paths": {
"raw_dir": {
"value": "/mnt/nas/raw",
"schema": {
"type": "string",
"label": "Raw-Verzeichnis",
"description": "Speicherort für rohe MKV-Dateien",
"required": true
}
},
"movie_dir": {
"value": "/mnt/nas/movies",
"schema": { ... }
"categories": [
{
"category": "Pfade",
"settings": [
{
"key": "raw_dir",
"label": "Raw Ausgabeordner",
"type": "path",
"required": true,
"description": "...",
"defaultValue": "data/output/raw",
"options": [],
"validation": { "minLength": 1 },
"value": "data/output/raw",
"orderIndex": 100
}
]
}
},
"tools": { ... },
"encoding": { ... },
"drive": { ... },
"makemkv": { ... },
"omdb": { ... },
"notifications": { ... }
]
}
```
@@ -42,42 +40,44 @@ Gibt alle Einstellungen kategorisiert zurück.
Aktualisiert eine einzelne Einstellung.
**URL-Parameter:** `key` – Einstellungs-Schlüssel
**Request:**
```json
{
"value": "/mnt/storage/raw"
}
{ "value": "/mnt/storage/raw" }
```
**Response:**
```json
{ "ok": true, "key": "raw_dir", "value": "/mnt/storage/raw" }
{
"setting": {
"key": "raw_dir",
"value": "/mnt/storage/raw"
},
"reviewRefresh": {
"triggered": false,
"reason": "not_ready"
}
}
```
**Fehlerfälle:**
- `400` – Ungültiger Wert (Validierungsfehler)
- `404` – Einstellung nicht gefunden
!!! note "Encode-Review-Refresh"
Wenn eine encoding-relevante Einstellung geändert wird (z.B. `handbrake_preset`), wird der Encode-Plan für den aktuell wartenden Job automatisch neu berechnet.
`reviewRefresh` ist `null` oder ein Objekt mit Status der optionalen Review-Neuberechnung.
---
## PUT /api/settings
Aktualisiert mehrere Einstellungen auf einmal.
Aktualisiert mehrere Einstellungen atomar.
**Request:**
```json
{
"raw_dir": "/mnt/storage/raw",
"movie_dir": "/mnt/storage/movies",
"handbrake_preset": "H.265 MKV 720p30"
"settings": {
"raw_dir": "/mnt/storage/raw",
"movie_dir": "/mnt/storage/movies",
"handbrake_preset_bluray": "H.264 MKV 1080p30"
}
}
```
@@ -85,9 +85,36 @@ Aktualisiert mehrere Einstellungen auf einmal.
```json
{
"ok": true,
"updated": ["raw_dir", "movie_dir", "handbrake_preset"],
"errors": []
"changes": [
{ "key": "raw_dir", "value": "/mnt/storage/raw" },
{ "key": "movie_dir", "value": "/mnt/storage/movies" }
],
"reviewRefresh": {
"triggered": true,
"jobId": 42,
"relevantKeys": ["handbrake_preset_bluray"]
}
}
```
Bei Validierungsfehlern kommt `400` mit `error.details[]`.
---
## GET /api/settings/handbrake-presets
Liest Preset-Liste via `HandBrakeCLI -z` (mit Fallback auf konfigurierte Presets).
**Response (Beispiel):**
```json
{
"source": "handbrake-cli",
"message": null,
"options": [
{ "label": "General/", "value": "__group__general", "disabled": true, "category": "General" },
{ "label": " Fast 1080p30", "value": "Fast 1080p30", "category": "General" }
]
}
```
@@ -95,260 +122,217 @@ Aktualisiert mehrere Einstellungen auf einmal.
## POST /api/settings/pushover/test
Sendet eine Test-Benachrichtigung über PushOver.
Sendet Testnachricht über aktuelle PushOver-Settings.
**Request:** Kein Body erforderlich (verwendet gespeicherte Zugangsdaten)
**Response (Erfolg):**
```json
{ "ok": true, "message": "Test-Benachrichtigung gesendet" }
```
**Response (Fehler):**
```json
{ "ok": false, "error": "Ungültiger API-Token" }
```
---
## Skript-Verwaltung
Skripte werden über eigene Endpunkte unter `/api/settings/scripts` verwaltet. Jedes Skript hat eine `scriptBody`-Property (der Shell-Befehl oder mehrzeiliges Skript) und einen `orderIndex` für die Sortierung.
### GET /api/settings/scripts
Gibt alle Skripte zurück, sortiert nach `orderIndex`.
**Response:**
**Request (optional):**
```json
{
"scripts": [
{
"id": 1,
"name": "Zu Plex verschieben",
"scriptBody": "mv \"$RIPSTER_OUTPUT_PATH\" /mnt/plex/movies/",
"orderIndex": 1,
"createdAt": "2026-01-15T10:00:00.000Z",
"updatedAt": "2026-01-15T10:00:00.000Z"
}
]
"title": "Test",
"message": "Ripster Test"
}
```
---
### POST /api/settings/scripts
Legt ein neues Skript an.
**Request:**
```json
{
"name": "Zu Plex verschieben",
"scriptBody": "mv \"$RIPSTER_OUTPUT_PATH\" /mnt/plex/movies/"
}
```
| Feld | Typ | Pflicht | Beschreibung |
|------|-----|---------|-------------|
| `name` | string | ✅ | Anzeigename (eindeutig) |
| `scriptBody` | string | ✅ | Shell-Befehl oder mehrzeiliges Skript |
**Response:** `201 Created` – `{ "script": { ... } }`
---
### PUT /api/settings/scripts/:id
Aktualisiert ein vorhandenes Skript. Alle Felder optional.
---
### DELETE /api/settings/scripts/:id
Löscht ein Skript.
!!! warning "Referenzen"
Das Skript wird gelöscht, auch wenn es in Job-Historien referenziert ist. In zukünftigen Reviews erscheint es nicht mehr.
---
### POST /api/settings/scripts/:id/test
Führt ein Skript mit Platzhalter-Umgebungsvariablen aus (Testlauf).
**Response:**
```json
{
"ok": true,
"exitCode": 0,
"stdout": "Testausgabe des Skripts",
"stderr": "",
"durationMs": 245
}
```
**Platzhalter-Werte beim Testlauf:**
| Variable | Testwert |
|---------|---------|
| `RIPSTER_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` |
---
### POST /api/settings/scripts/reorder
Ändert die Reihenfolge der Skripte (persistiert in `order_index`).
**Request:**
```json
{ "orderedScriptIds": [3, 1, 2] }
```
**Response:** `{ "scripts": [ ... ] }` – alle Skripte in neuer Reihenfolge.
---
## Skript-Ketten-Verwaltung
Skript-Ketten werden unter `/api/settings/script-chains` verwaltet.
### GET /api/settings/script-chains
Gibt alle Ketten zurück (inkl. Schritte).
### POST /api/settings/script-chains
Legt eine neue Kette an.
```json
{ "name": "Nach Jellyfin deployen" }
```
### PUT /api/settings/script-chains/:id
Aktualisiert eine Kette (Name, Schritte).
### DELETE /api/settings/script-chains/:id
Löscht eine Kette und alle ihre Schritte.
### POST /api/settings/script-chains/:id/test
Führt eine Kette mit Platzhalter-Umgebungsvariablen aus (Testlauf).
**Response:**
```json
{
"result": {
"success": true,
"steps": [
{ "scriptId": 1, "scriptName": "Zu Plex verschieben", "success": true, "exitCode": 0 }
]
"sent": true,
"eventKey": "test",
"requestId": "..."
}
}
```
Wenn PushOver deaktiviert ist oder Credentials fehlen, kommt i. d. R. ebenfalls `200` mit `sent: false` + `reason`.
---
## Skripte
Basis: `/api/settings/scripts`
### GET /api/settings/scripts
```json
{ "scripts": [ { "id": 1, "name": "...", "scriptBody": "...", "orderIndex": 1, "createdAt": "...", "updatedAt": "..." } ] }
```
### POST /api/settings/scripts
```json
{ "name": "Move", "scriptBody": "mv \"$RIPSTER_OUTPUT_PATH\" /mnt/movies/" }
```
Response: `201` mit `{ "script": { ... } }`
### PUT /api/settings/scripts/:id
Body wie `POST`, Response `{ "script": { ... } }`.
### DELETE /api/settings/scripts/:id
Response `{ "removed": { ... } }`.
### POST /api/settings/scripts/reorder
```json
{ "orderedScriptIds": [3, 1, 2] }
```
Response `{ "scripts": [ ... ] }`.
### POST /api/settings/scripts/:id/test
Führt Skript als Testlauf aus.
```json
{
"result": {
"scriptId": 1,
"scriptName": "Move",
"success": true,
"exitCode": 0,
"signal": null,
"timedOut": false,
"durationMs": 120,
"stdout": "...",
"stderr": "...",
"stdoutTruncated": false,
"stderrTruncated": false
}
}
```
### Umgebungsvariablen für Skripte
Diese Variablen werden beim Ausführen gesetzt:
- `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`
---
## Skript-Ketten
Basis: `/api/settings/script-chains`
Eine Kette hat Schritte vom Typ:
- `script` (`scriptId` erforderlich)
- `wait` (`waitSeconds` 1..3600)
### GET /api/settings/script-chains
Response `{ "chains": [ ... ] }` (inkl. `steps[]`).
### GET /api/settings/script-chains/:id
Response `{ "chain": { ... } }`.
### POST /api/settings/script-chains
```json
{
"name": "After Encode",
"steps": [
{ "stepType": "script", "scriptId": 1 },
{ "stepType": "wait", "waitSeconds": 15 },
{ "stepType": "script", "scriptId": 2 }
]
}
```
Response: `201` mit `{ "chain": { ... } }`
### PUT /api/settings/script-chains/:id
Body wie `POST`, Response `{ "chain": { ... } }`.
### DELETE /api/settings/script-chains/:id
Response `{ "removed": { ... } }`.
### POST /api/settings/script-chains/reorder
Ändert die Reihenfolge der Ketten (persistiert in `order_index`).
**Request:**
```json
{ "orderedChainIds": [2, 1, 3] }
```
Response `{ "chains": [ ... ] }`.
### POST /api/settings/script-chains/:id/test
Response:
```json
{
"result": {
"chainId": 2,
"chainName": "After Encode",
"steps": 3,
"succeeded": 3,
"failed": 0,
"aborted": false,
"results": []
}
}
```
---
## User-Presets
Benannte HandBrake-Preset-Sammlungen, die im Encode-Review schnell angewendet werden können. Unter `/api/settings/user-presets` verwaltet.
Basis: `/api/settings/user-presets`
### GET /api/settings/user-presets
Gibt alle User-Presets zurück. Optional gefiltert per Query-Parameter `mediaType`.
**Query-Parameter:**
| Parameter | Werte | Beschreibung |
|-----------|-------|-------------|
| `mediaType` | `bluray`, `dvd`, `other`, `all` | Filtert Presets nach Medientyp |
**Response:**
Optionaler Query-Parameter: `media_type=bluray|dvd|other|all`
```json
{
"presets": [
{
"id": 1,
"name": "Blu-ray High Quality",
"name": "Blu-ray HQ",
"mediaType": "bluray",
"handbrakePreset": "H.265 MKV 1080p30",
"handbrakePreset": "H.264 MKV 1080p30",
"extraArgs": "--encoder-preset slow",
"description": "Langsam, aber beste Qualität",
"createdAt": "2026-01-15T10:00:00.000Z",
"updatedAt": "2026-01-15T10:00:00.000Z"
"description": "...",
"createdAt": "...",
"updatedAt": "..."
}
]
}
```
---
### POST /api/settings/user-presets
Legt ein neues User-Preset an.
**Request:**
```json
{
"name": "Blu-ray High Quality",
"name": "Blu-ray HQ",
"mediaType": "bluray",
"handbrakePreset": "H.265 MKV 1080p30",
"handbrakePreset": "H.264 MKV 1080p30",
"extraArgs": "--encoder-preset slow",
"description": "Langsam, aber beste Qualität"
"description": "optional"
}
```
| Feld | Typ | Pflicht | Beschreibung |
|------|-----|---------|-------------|
| `name` | string | ✅ | Anzeigename |
| `mediaType` | string | — | `bluray`, `dvd`, `other`, `all` (Standard: `all`) |
| `handbrakePreset` | string | — | HandBrake-Preset-Name (`-Z`) |
| `extraArgs` | string | — | Zusatz-CLI-Argumente |
| `description` | string | — | Optionale Beschreibung |
**Response:** `201 Created` – `{ "preset": { ... } }`
---
Response: `201` mit `{ "preset": { ... } }`
### PUT /api/settings/user-presets/:id
Aktualisiert ein User-Preset. Alle Felder optional.
---
Body mit beliebigen Feldern aus `POST`, Response `{ "preset": { ... } }`.
### DELETE /api/settings/user-presets/:id
Löscht ein User-Preset.
---
## Einstellungs-Schlüssel Referenz
Eine vollständige Übersicht aller Schlüssel:
[:octicons-arrow-right-24: Einstellungsreferenz](../configuration/settings-reference.md)
Response `{ "removed": { ... } }`.
+73 -114
View File
@@ -1,6 +1,6 @@
# WebSocket Events
Ripster sendet Echtzeit-Updates über WebSocket unter `/ws`.
Ripster sendet Echtzeit-Updates über `/ws`.
---
@@ -10,8 +10,8 @@ Ripster sendet Echtzeit-Updates über WebSocket unter `/ws`.
const ws = new WebSocket('ws://localhost:3001/ws');
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
console.log(message.type, message.payload);
const msg = JSON.parse(event.data);
console.log(msg.type, msg.payload);
};
```
@@ -19,36 +19,38 @@ ws.onmessage = (event) => {
## Nachrichtenformat
Alle Broadcasts haben dieses Schema:
Die meisten Broadcasts haben dieses Schema:
```json
{
"type": "EVENT_TYPE",
"payload": { },
"timestamp": "2026-03-05T10:00:00.000Z"
"payload": {},
"timestamp": "2026-03-10T09:00:00.000Z"
}
```
Ausnahme: `WS_CONNECTED` beim Verbindungsaufbau enthält kein `timestamp`.
---
## Event-Typen
### WS_CONNECTED
Wird direkt nach Verbindungsaufbau gesendet.
Sofort nach erfolgreicher Verbindung.
```json
{
"type": "WS_CONNECTED",
"payload": {
"connectedAt": "2026-03-05T10:00:00.000Z"
"connectedAt": "2026-03-10T09:00:00.000Z"
}
}
```
### PIPELINE_STATE_CHANGED
Snapshot bei Zustandswechsel.
Neuer Pipeline-Snapshot.
```json
{
@@ -56,14 +58,24 @@ Snapshot bei Zustandswechsel.
"payload": {
"state": "ENCODING",
"activeJobId": 42,
"progress": 73.5,
"progress": 62.5,
"eta": "00:12:34",
"statusText": "Encoding mit HandBrake",
"statusText": "ENCODING 62.50%",
"context": {},
"jobProgress": {
"42": {
"state": "ENCODING",
"progress": 62.5,
"eta": "00:12:34",
"statusText": "ENCODING 62.50%"
}
},
"queue": {
"maxParallelJobs": 1,
"runningCount": 1,
"queuedCount": 0
"queuedCount": 2,
"runningJobs": [],
"queuedJobs": []
}
}
}
@@ -71,7 +83,7 @@ Snapshot bei Zustandswechsel.
### PIPELINE_PROGRESS
Laufende Fortschrittsupdates während aktiver Phasen.
Laufende Fortschrittsupdates.
```json
{
@@ -79,33 +91,20 @@ Laufende Fortschrittsupdates während aktiver Phasen.
"payload": {
"state": "ENCODING",
"activeJobId": 42,
"progress": 73.5,
"progress": 62.5,
"eta": "00:12:34",
"statusText": "ENCODING 73.50% - task 1 of 1"
"statusText": "ENCODING 62.50%"
}
}
```
### PIPELINE_QUEUE_CHANGED
Aktualisierung der Job-Queue.
Queue-Snapshot aktualisiert.
```json
{
"type": "PIPELINE_QUEUE_CHANGED",
"payload": {
"maxParallelJobs": 1,
"runningCount": 1,
"queuedCount": 2,
"runningJobs": [],
"queuedJobs": []
}
}
```
### DISC_DETECTED / DISC_REMOVED
### DISC_DETECTED
Disc erkannt.
Disc-Insertion/-Removal.
```json
{
@@ -114,7 +113,6 @@ Disc erkannt.
"device": {
"path": "/dev/sr0",
"discLabel": "INCEPTION",
"label": "INCEPTION",
"model": "ASUS BW-16D1HT",
"fstype": "udf",
"mountpoint": null,
@@ -124,132 +122,93 @@ Disc erkannt.
}
```
`mediaProfile` ist `"bluray"`, `"dvd"`, `"other"` oder `null` (wenn nicht bestimmbar). Der Wert wird aus Dateisystemtyp (UDF/ISO9660), Laufwerk-Modell und Disc-Label abgeleitet.
`mediaProfile`: `bluray` | `dvd` | `other` | `null`
### DISC_REMOVED
### HARDWARE_MONITOR_UPDATE
Disc entfernt.
Snapshot aus Hardware-Monitoring.
```json
{
"type": "DISC_REMOVED",
"type": "HARDWARE_MONITOR_UPDATE",
"payload": {
"device": {
"path": "/dev/sr0"
}
"enabled": true,
"intervalMs": 5000,
"updatedAt": "2026-03-10T09:00:00.000Z",
"sample": {
"cpu": {},
"memory": {},
"gpu": {},
"storage": {}
},
"error": null
}
}
```
### PIPELINE_ERROR
Fehler bei Pipeline-Disc-Events im Backend.
```json
{
"type": "PIPELINE_ERROR",
"payload": {
"message": "..."
}
}
```
Fehler bei Disc-Event-Verarbeitung in Pipeline.
### DISK_DETECTION_ERROR
Fehler im Laufwerkserkennungsdienst.
Fehler in Laufwerkserkennung.
### SETTINGS_UPDATED
Einzelnes Setting wurde gespeichert.
### SETTINGS_BULK_UPDATED
Bulk-Settings gespeichert.
```json
{
"type": "DISK_DETECTION_ERROR",
"type": "SETTINGS_BULK_UPDATED",
"payload": {
"message": "..."
"count": 3,
"keys": ["raw_dir", "movie_dir", "handbrake_preset_bluray"]
}
}
```
### SETTINGS_SCRIPTS_UPDATED
Wird gesendet, wenn ein Skript angelegt, aktualisiert, gelöscht oder umsortiert wurde.
```json
{
"type": "SETTINGS_SCRIPTS_UPDATED",
"payload": {
"action": "reordered",
"count": 3
}
}
```
`action` ist `"created"`, `"updated"`, `"deleted"` oder `"reordered"`.
Skript geändert (`created|updated|deleted|reordered`).
### SETTINGS_SCRIPT_CHAINS_UPDATED
Wird gesendet bei Änderungen an Skript-Ketten.
```json
{
"type": "SETTINGS_SCRIPT_CHAINS_UPDATED",
"payload": {
"action": "created",
"id": 2
}
}
```
Skript-Kette geändert (`created|updated|deleted|reordered`).
### USER_PRESETS_UPDATED
Wird gesendet, wenn ein User-Preset angelegt, aktualisiert oder gelöscht wurde.
```json
{
"type": "USER_PRESETS_UPDATED",
"payload": {
"action": "created",
"id": 1
}
}
```
`action` ist `"created"`, `"updated"` oder `"deleted"`.
User-Preset geändert (`created|updated|deleted`).
### CRON_JOBS_UPDATED
Wird gesendet, wenn ein Cron-Job angelegt, aktualisiert oder gelöscht wurde.
Cron-Config geändert (`created|updated|deleted`).
### CRON_JOB_UPDATED
Laufzeitstatus eines Cron-Jobs geändert.
```json
{
"type": "CRON_JOBS_UPDATED",
"type": "CRON_JOB_UPDATED",
"payload": {
"action": "created",
"id": 1
"id": 1,
"lastRunStatus": "running",
"lastRunAt": "2026-03-10T10:00:00.000Z",
"nextRunAt": null
}
}
```
`action` ist `"created"`, `"updated"` oder `"deleted"`.
---
## Reconnect-Verhalten
`useWebSocket.js` versucht bei Verbindungsabbruch automatisch erneut zu verbinden.
`useWebSocket` verbindet bei Abbruch automatisch neu:
- fester Retry-Intervall: `1500ms`
- erneuter Versuch bis zum Unmount der Komponente
---
## React-Beispiel
```js
import { useWebSocket } from './hooks/useWebSocket';
useWebSocket({
onMessage: (msg) => {
if (msg.type === 'PIPELINE_STATE_CHANGED') {
setPipeline(msg.payload);
}
}
});
```
- Retry-Intervall: `1500ms`
- Wiederverbindung bis Komponente unmounted wird
+72 -261
View File
@@ -1,305 +1,116 @@
# Backend-Services
Das Backend ist in Node.js/Express geschrieben und in **Services** aufgeteilt, die jeweils eine klar abgegrenzte Verantwortlichkeit haben.
Das Backend ist in Services aufgeteilt, die von Express-Routen orchestriert werden.
---
## pipelineService.js
## `pipelineService.js`
**Der Kern von Ripster** – orchestriert den gesamten Ripping-Workflow.
Zentrale Workflow-Orchestrierung.
### Zuständigkeiten
Aufgaben:
- Verwaltung des Pipeline-Zustands als State Machine
- Koordination zwischen allen externen Tools
- Generierung von Encode-Plänen
- Fehlerbehandlung und Recovery
- Pipeline-State-Machine + Persistenz (`pipeline_state`)
- Disc-Analyse/Rip/Review/Encode
- Queue-Management (Jobs + `script|chain|wait` Einträge)
- Retry/Re-Encode/Restart-Flows
- WebSocket-Broadcasts für State/Progress/Queue
### Haupt-Methoden
Wichtige Methoden:
| Methode | Beschreibung |
|---------|-------------|
| `analyzeDisc()` | Legt Job an und öffnet Metadaten-Auswahl |
| `selectMetadata({...})` | Setzt Metadaten/Playlist und triggert Auto-Start |
| `startPreparedJob(jobId)` | Startet vorbereiteten Job (oder Queue) |
| `confirmEncodeReview(jobId, options)` | Bestätigt Review inkl. Track/Skript-Auswahl |
| `cancel(jobId)` | Bricht laufenden Job ab oder entfernt Queue-Eintrag |
| `retry(jobId)` | Startet fehlgeschlagenen/abgebrochenen Job neu |
| `reencodeFromRaw(jobId)` | Encodiert aus vorhandenem RAW neu |
| `restartReviewFromRaw(jobId)` | Berechnet Review aus RAW neu |
| `restartEncodeWithLastSettings(jobId)` | Neustart mit letzter bestätigter Auswahl |
| `resumeReadyToEncodeJob(jobId)` | Lädt READY_TO_ENCODE nach Neustart in die Session |
### Zustandsübergänge
<div class="pipeline-diagram">
```mermaid
flowchart LR
START(( )) --> IDLE
IDLE -->|analyzeDisc()| META[METADATA\nSELECTION]
META -->|selectMetadata()| RTS[READY_TO\nSTART]
RTS -->|Auto-Start/Queue| RIP[RIPPING]
RTS -->|Auto-Start mit RAW| MIC[MEDIAINFO\nCHECK]
RIP -->|MKV erstellt| MIC[MEDIAINFO\nCHECK]
MIC -->|Playlist offen| WUD[WAITING_FOR\nUSER_DECISION]
WUD -->|selectMetadata(selectedPlaylist)| MIC
MIC -->|Tracks analysiert| RTE[READY_TO\nENCODE]
RTE -->|confirmEncodeReview() + startPreparedJob()| ENC[ENCODING]
ENC -->|Pre-Encode → HandBrake → Post-Encode fertig| FIN([FINISHED])
ENC -->|Abbruch| CAN([CANCELLED])
ENC -->|Fehler| ERR([ERROR])
RIP -->|Fehler| ERR
RIP -->|Abbruch| CAN
ERR -->|retry() / cancel()| IDLE
CAN -->|retry() / analyzeDisc()| IDLE
FIN -->|cancel / neue Disc| IDLE
style FIN fill:#e8f5e9,stroke:#66bb6a,color:#2e7d32
style CAN fill:#fff3e0,stroke:#fb8c00,color:#e65100
style ERR fill:#ffebee,stroke:#ef5350,color:#c62828
style ENC fill:#f3e5f5,stroke:#ab47bc,color:#6a1b9a
style RIP fill:#e3f2fd,stroke:#42a5f5,color:#1565c0
style MIC fill:#e3f2fd,stroke:#42a5f5,color:#1565c0
```
</div>
- `analyzeDisc()`
- `selectMetadata()`
- `startPreparedJob()`
- `confirmEncodeReview()`
- `cancel()`
- `retry()`
- `reencodeFromRaw()`
- `restartReviewFromRaw()`
- `restartEncodeWithLastSettings()`
- `resumeReadyToEncodeJob()`
- `enqueueNonJobEntry()`, `reorderQueue()`, `removeQueueEntry()`
---
## diskDetectionService.js
## `diskDetectionService.js`
Überwacht das Disc-Laufwerk auf Disc-Einleger- und Auswurf-Ereignisse.
Pollt Laufwerk(e) und emittiert:
### Modi
- `discInserted`
- `discRemoved`
- `error`
| Modus | Beschreibung |
|------|-------------|
| `auto` | Erkennt verfügbare Laufwerke automatisch |
| `explicit` | Überwacht ein bestimmtes Gerät (z.B. `/dev/sr0`) |
Zusatz:
### Polling
Der Service pollt das Laufwerk im konfigurierten Intervall (`disc_poll_interval_ms`, Standard: 4000ms) und emittiert Events:
```js
// Ereignisse
emit('discInserted', { path: '/dev/sr0', mediaProfile: 'bluray', ... })
emit('discRemoved', { path: '/dev/sr0' })
```
### Media-Profil-Erkennung
Das erkannte Gerät enthält ein `mediaProfile`-Feld (`"bluray"`, `"dvd"`, `"other"` oder `null`). Die Erkennung nutzt eine Heuristik aus drei Quellen (absteigend nach Priorität):
1. Explizit gesetztes `media_profile` aus den Settings
2. Disc-Label und Laufwerks-Modell (Regex gegen bekannte Begriffe)
3. Dateisystemtyp: `udf` → bevorzugt DVD, kombiniert mit Modell; `iso9660/cdfs` → DVD oder CD
- Modus `auto` oder `explicit`
- heuristische `mediaProfile`-Erkennung (`bluray`/`dvd`/`other`)
- `rescanAndEmit()` für manuellen Trigger
---
## processRunner.js
## `settingsService.js`
Verwaltet externe CLI-Prozesse.
Settings-Layer mit Validation/Serialisierung.
### Features
Features:
- **Streaming**: stdout/stderr werden zeilenweise gelesen
- **Progress-Callbacks**: Ermöglicht Echtzeit-Fortschrittsanzeige
- **Graceful Shutdown**: SIGINT → Warte-Timeout → SIGKILL
- **Prozess-Registry**: Verfolgt aktive Prozesse für sauberes Beenden
### Nutzung
```js
const result = await runProcess(
'HandBrakeCLI',
['--input', rawFile, '--output', outputFile, '--preset', preset],
{
onStderr: (line) => parseHandBrakeProgress(line),
onStdout: (line) => logger.debug(line)
}
);
```
- `getCategorizedSettings()` für UI-Form
- `setSettingValue()` / `setSettingsBulk()`
- profilspezifische Auflösung (`resolveEffectiveToolSettings`)
- CLI-Config-Building für MakeMKV/HandBrake/MediaInfo
- HandBrake-Preset-Liste via `HandBrakeCLI -z`
- MakeMKV-Registration-Command aus `makemkv_registration_key`
---
## websocketService.js
## `historyService.js`
WebSocket-Server für Echtzeit-Client-Kommunikation.
Historie + Dateioperationen.
### Betrieb
Features:
- Läuft auf Pfad `/ws` des Express-Servers
- Hält eine Registry aller verbundenen Clients
- Ermöglicht Broadcast an alle Clients oder gezieltes Senden
### API
```js
broadcast('PIPELINE_STATE_CHANGED', { state, activeJobId });
broadcast('PIPELINE_PROGRESS', { state, progress, eta, statusText });
broadcast('PIPELINE_QUEUE_CHANGED', queueSnapshot);
```
- Job-Liste/Detail inkl. Log-Tail
- Orphan-RAW-Erkennung und Import
- OMDb-Nachzuweisung
- Dateilöschung (`raw|movie|both`)
- Job-Löschung (`none|raw|movie|both`)
---
## omdbService.js
## `cronService.js`
Integration mit der [OMDb API](https://www.omdbapi.com/).
Integriertes Cron-System ohne externe Parser-Library.
### Methoden
Features:
| Methode | Beschreibung |
|---------|-------------|
| `searchByTitle(title, type)` | Suche nach Titel (movie/series) |
| `fetchById(imdbId)` | Vollständige Metadaten per IMDb-ID |
### Zurückgegebene Daten
```json
{
"imdbId": "tt1375666",
"title": "Inception",
"year": "2010",
"type": "movie",
"poster": "https://...",
"plot": "...",
"director": "Christopher Nolan"
}
```
- 5-Feld-Cron-Parser + `nextRun`-Berechnung
- Quellen: `script` oder `chain`
- Laufzeitlogs (`cron_run_logs`)
- manuelles Triggern
- WebSocket-Events: `CRON_JOBS_UPDATED`, `CRON_JOB_UPDATED`
---
## settingsService.js
## Weitere Services
Verwaltet alle Anwendungseinstellungen.
### Features
- **Schema-getriebene Validierung**: Jede Einstellung hat Typ, Grenzen und Pflichtfeld-Flag
- **Kategorisierung**: Einstellungen sind in Kategorien gruppiert (Pfade, Tools, Metadaten, …)
- **Persistenz**: Werte in SQLite, Schema ebenfalls in SQLite
- **Profil-Auflösung**: `resolveEffectiveToolSettings(settingsMap, mediaProfile)` wählt automatisch die profil-spezifischen Werte (`_bluray`/`_dvd`) und fällt auf den globalen Wert zurück
### Profil-Auflösung
```js
// Löst alle profil-spezifischen Keys auf und gibt einen effektiven Einstellungs-Map zurück
const effective = await settingsService.getEffectiveSettingsMap('bluray');
// effective.handbrake_preset → Wert aus handbrake_preset_bluray (falls gesetzt)
// effective.raw_dir → Wert aus raw_dir_bluray (kein Fallback bei Pfaden)
```
### Einstellungs-Kategorien
| Kategorie | Ausgewählte Schlüssel |
|-----------|----------------------|
| `Pfade` | `raw_dir[_bluray/_dvd/_other]`, `movie_dir[_bluray/_dvd/_other]`, `log_dir` |
| `Laufwerk` | `drive_mode`, `drive_device`, `disc_poll_interval_ms`, `makemkv_source_index` |
| `Monitoring` | `hardware_monitoring_enabled`, `hardware_monitoring_interval_ms` |
| `Tools` | `makemkv_command`, `handbrake_command`, `mediainfo_command`, `pipeline_max_parallel_jobs` |
| `Tools – Blu-ray` | `handbrake_preset_bluray`, `makemkv_rip_mode_bluray`, … |
| `Tools – DVD` | `handbrake_preset_dvd`, `makemkv_rip_mode_dvd`, … |
| `Metadaten` | `omdb_api_key`, `omdb_default_type` |
| `Benachrichtigungen` | `pushover_enabled`, `pushover_token`, `pushover_notify_*` |
- `scriptService.js` (CRUD + Test + Wrapper-Ausführung)
- `scriptChainService.js` (CRUD + Step-Execution)
- `userPresetService.js` (HandBrake User-Presets)
- `hardwareMonitorService.js` (CPU/RAM/GPU/Storage)
- `websocketService.js` (Client-Registry + Broadcast)
- `notificationService.js` (PushOver)
- `logger.js` (rotierende Datei-Logs)
---
## userPresetService.js
## Bootstrapping (`src/index.js`)
Verwaltet benannte HandBrake-Preset-Sammlungen pro Medientyp.
Beim Start:
### Methoden
| Methode | Beschreibung |
|---------|-------------|
| `listPresets(mediaType?)` | Alle Presets; optional nach Medientyp filtern (`bluray`/`dvd`/`other`/`all`) |
| `getPresetById(id)` | Einzelnes Preset |
| `createPreset(payload)` | Neues Preset anlegen |
| `updatePreset(id, payload)` | Preset aktualisieren |
| `deletePreset(id)` | Preset löschen |
!!! info "mediaType = 'all'"
Presets mit `mediaType = 'all'` erscheinen bei Filterung nach jedem Medientyp.
---
## historyService.js
Datenbankoperationen für Job-Historie.
### Hauptoperationen
| Operation | Beschreibung |
|-----------|-------------|
| `listJobs(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äglich zuweisen |
| `deleteJob(id, deleteFiles)` | Job und optional Dateien löschen |
---
## cronService.js
Eingebautes Cron-System ohne externe Abhängigkeiten.
### Features
- **Eigener Expression-Parser**: Unterstützt alle Standard-5-Felder-Cron-Ausdrücke (`* /n`, Bereiche, Listen)
- **Skripte und Ketten**: Cron-Jobs können ein Skript (`sourceType: "script"`) oder eine Kette (`sourceType: "chain"`) ausführen
- **Log-Rotation**: Max. 50 Logs pro Job, Ausgabe auf 100.000 Zeichen begrenzt
- **PushOver-Integration**: Optionale Benachrichtigung nach jeder Ausführung
- **Manuelle Auslösung**: `triggerJobManually(id)` – läuft unabhängig vom Zeitplan
### Methoden
| Methode | Beschreibung |
|---------|-------------|
| `listJobs()` | Alle Cron-Jobs |
| `createJob(payload)` | Neuen Job anlegen |
| `updateJob(id, payload)` | Job aktualisieren |
| `deleteJob(id)` | Job löschen |
| `getJobLogs(id, limit)` | Ausführungs-Logs |
| `triggerJobManually(id)` | Sofortige Ausführung |
| `validateExpression(expr)` | Ausdruck validieren |
| `getNextRunTime(expr)` | Nächsten Ausführungszeitpunkt berechnen |
---
## notificationService.js
PushOver-Push-Benachrichtigungen.
```js
await notify({
title: 'Ripster: Job abgeschlossen',
message: 'Inception (2010) wurde erfolgreich encodiert'
});
```
---
## logger.js
Strukturiertes Logging mit täglicher Log-Rotation.
### Log-Level
| Level | Verwendung |
|-------|-----------|
| `debug` | Detaillierte Entwicklungs-Informationen |
| `info` | Normale Betriebsereignisse |
| `warn` | Warnungen, die Aufmerksamkeit benötigen |
| `error` | Fehler, die den Betrieb beeinträchtigen |
### Log-Dateien
```
logs/
├── ripster-2024-01-15.log ← Tages-Log
└── jobs/
└── job-42-handbrake.log ← Prozess-spezifische Logs
```
1. DB init/migrate
2. Pipeline-Init
3. Cron-Init
4. Express-Routes + Error-Handler
5. WebSocket-Server auf `/ws`
6. Hardware-Monitoring-Init
7. Disk-Detection-Start
+61 -222
View File
@@ -1,273 +1,112 @@
# Datenbank
Ripster verwendet **SQLite3** als Datenbank. Die Datenbankdatei liegt unter `backend/data/ripster.db`.
Ripster verwendet SQLite (`backend/data/ripster.db`).
---
## Schema-Übersicht
## Tabellen
```sql
settings_schema -- Einstellungs-Definitionen
settings_values -- Benutzer-Werte
jobs -- Rip-Job-Datensätze
pipeline_state -- Aktueller Pipeline-Zustand (Singleton)
scripts -- Shell-Skripte für Pre-/Post-Encode-Ausführung
script_chains -- Geordnete Ketten aus mehreren Skripten
script_chain_steps -- Einzelschritte einer Skript-Kette
user_presets -- Benannte HandBrake-Preset-Sammlungen pro Medientyp
cron_jobs -- Zeitgesteuerte Aufgaben (eigener Cron-Parser)
cron_run_logs -- Ausführungs-Protokolle für Cron-Jobs
```text
settings_schema
settings_values
jobs
pipeline_state
scripts
script_chains
script_chain_steps
user_presets
cron_jobs
cron_run_logs
```
---
## Tabelle: jobs
## `jobs`
Die wichtigste Tabelle – speichert alle Ripping-Jobs.
Speichert Pipeline-Lifecycle und Artefakte pro Job.
```sql
CREATE TABLE jobs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
status TEXT NOT NULL, -- Aktueller Status
title TEXT, -- Filmtitel (von OMDb)
imdb_id TEXT, -- IMDb-ID
omdb_year TEXT, -- Erscheinungsjahr
omdb_type TEXT, -- movie/series
omdb_poster TEXT, -- Poster-URL
raw_path TEXT, -- Pfad zur Raw-MKV
output_path TEXT, -- Pfad zur Ausgabedatei
playlist TEXT, -- Gewählte Blu-ray Playlist
rip_successful INTEGER NOT NULL DEFAULT 0, -- 1 wenn Rip abgeschlossen
makemkv_output TEXT, -- MakeMKV-Ausgabe (JSON)
mediainfo_output TEXT, -- MediaInfo-Ausgabe (JSON)
encode_plan TEXT, -- Encode-Plan (JSON)
handbrake_log TEXT, -- HandBrake Log-Pfad
error_message TEXT, -- Fehlermeldung bei ERROR
error_details TEXT -- Detaillierte Fehler-Infos
);
```
Zentrale Felder:
!!! info "rip_successful"
Das Feld `rip_successful` wird auf `1` gesetzt, sobald MakeMKV den Rip-Schritt erfolgreich abgeschlossen hat – unabhängig davon, ob danach ein Encode-Fehler auftritt. Damit lässt sich in der History unterscheiden, ob eine Raw-Datei vorhanden ist.
### Job-Status-Werte
| 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ätigung |
| `ENCODING` | HandBrake encodiert |
| `FINISHED` | Erfolgreich abgeschlossen |
| `ERROR` | Fehler aufgetreten |
- Metadaten: `title`, `year`, `imdb_id`, `poster_url`, `omdb_json`, `selected_from_omdb`
- Laufzeit: `start_time`, `end_time`, `status`, `last_state`
- Pfade: `raw_path`, `output_path`, `encode_input_path`
- Tool-Ausgaben: `makemkv_info_json`, `handbrake_info_json`, `mediainfo_info_json`, `encode_plan_json`
- Kontrolle: `encode_review_confirmed`, `rip_successful`, `error_message`
- Audit: `created_at`, `updated_at`
---
## Tabelle: pipeline_state
## `pipeline_state`
Singleton-Tabelle für den aktuellen Pipeline-Zustand (immer genau 1 Zeile).
Singleton-Tabelle (`id = 1`) für aktiven Snapshot:
```sql
CREATE TABLE pipeline_state (
id INTEGER PRIMARY KEY CHECK(id = 1),
state TEXT NOT NULL DEFAULT 'IDLE',
job_id INTEGER, -- Aktiver Job (NULL wenn IDLE)
progress REAL, -- Fortschritt 0-100
eta TEXT, -- Geschätzte Restzeit
updated_at TEXT NOT NULL
);
```
- `state`
- `active_job_id`
- `progress`
- `eta`
- `status_text`
- `context_json`
- `updated_at`
---
## Tabelle: settings_schema
## `settings_schema` + `settings_values`
Definiert alle verfügbaren Einstellungen mit Metadaten.
```sql
CREATE TABLE settings_schema (
key TEXT PRIMARY KEY,
category TEXT NOT NULL, -- paths, tools, encoding, ...
type TEXT NOT NULL, -- string, number, boolean, select
label TEXT NOT NULL, -- Anzeigename
description TEXT, -- Hilfetext
default_val TEXT, -- Standardwert
required INTEGER, -- 1 = Pflichtfeld
min_val REAL, -- Minimalwert (für number)
max_val REAL, -- Maximalwert (für number)
options TEXT -- JSON-Array für select-Typ
);
```
- `settings_schema`: Definition (Typ, Default, Validation, Reihenfolge)
- `settings_values`: aktueller Wert pro Key
---
## Tabelle: settings_values
## `scripts`, `script_chains`, `script_chain_steps`
Speichert benutzer-konfigurierte Werte.
```sql
CREATE TABLE settings_values (
key TEXT PRIMARY KEY REFERENCES settings_schema(key),
value TEXT NOT NULL,
updated_at TEXT NOT NULL
);
```
- `scripts`: Shell-Skripte (`name`, `script_body`, `order_index`)
- `script_chains`: Ketten (`name`, `order_index`)
- `script_chain_steps`: Schritte je Kette
- `step_type`: `script` oder `wait`
- `script_id` oder `wait_seconds`
---
## Tabelle: scripts
## `user_presets`
Verwaltet Shell-Skripte, die vor oder nach dem Encode-Schritt ausgeführt werden können.
Benannte HandBrake-Preset-Sets:
```sql
CREATE TABLE scripts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE,
script_body TEXT NOT NULL,
order_index INTEGER NOT NULL DEFAULT 0, -- Sortierposition in der UI
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
```
## Tabelle: script_chains
Geordnete Ketten, die mehrere Skripte sequenziell zusammenfassen.
```sql
CREATE TABLE script_chains (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE,
order_index INTEGER NOT NULL DEFAULT 0, -- Sortierposition in der UI
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE script_chain_steps (
id INTEGER PRIMARY KEY AUTOINCREMENT,
chain_id INTEGER NOT NULL REFERENCES script_chains(id) ON DELETE CASCADE,
script_id INTEGER NOT NULL REFERENCES scripts(id) ON DELETE CASCADE,
step_order INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
```
!!! info "Sortierung"
`order_index` in `scripts` und `script_chains` wird über die API (`reorderScripts` / `reorderChains`) per Drag & Drop in der UI gesetzt und bleibt persistent gespeichert.
- `name`
- `media_type` (`bluray|dvd|other|all`)
- `handbrake_preset`
- `extra_args`
- `description`
---
## Tabelle: user_presets
## `cron_jobs` + `cron_run_logs`
Speichert benannte HandBrake-Preset-Sammlungen pro Medientyp.
```sql
CREATE TABLE user_presets (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
media_type TEXT NOT NULL DEFAULT 'all', -- 'bluray', 'dvd', 'other', 'all'
handbrake_preset TEXT,
extra_args TEXT,
description TEXT,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
```
!!! info "Medientyp-Filter"
`GET /api/settings/user-presets?mediaType=bluray` gibt Presets mit `media_type = 'bluray'` **und** `media_type = 'all'` zurück.
- `cron_jobs`: Zeitplan + Status
- `cron_run_logs`: einzelne Läufe
- `status`: `running|success|error`
- `output`
- `error_message`
---
## Tabellen: cron_jobs & cron_run_logs
## Migration/Recovery
Speichern den Zeitplan und die Ausführungs-Historie des eingebauten Cron-Systems.
Beim Start werden Schema und Settings-Metadaten automatisch abgeglichen.
```sql
CREATE TABLE cron_jobs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
cron_expression TEXT NOT NULL, -- 5-Felder-Ausdruck (min h d m wd)
source_type TEXT NOT NULL, -- "script" oder "chain"
source_id INTEGER NOT NULL, -- ID des Skripts/der Kette
enabled INTEGER NOT NULL DEFAULT 1,
pushover_enabled INTEGER NOT NULL DEFAULT 1,
last_run_at TEXT,
last_run_status TEXT, -- "success", "error", "running"
next_run_at TEXT,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
Bei korruptem SQLite-File:
CREATE TABLE cron_run_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
cron_job_id INTEGER NOT NULL REFERENCES cron_jobs(id) ON DELETE CASCADE,
started_at TEXT NOT NULL,
finished_at TEXT,
status TEXT NOT NULL, -- "success", "error", "running"
exit_code INTEGER,
stdout TEXT,
stderr TEXT,
triggered_by TEXT NOT NULL DEFAULT 'cron', -- "cron" oder "manual"
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
```
!!! info "Log-Rotation"
Pro Cron-Job werden maximal **50 Log-Einträge** gespeichert; ältere Einträge werden automatisch gelöscht. Stdout/Stderr werden auf **100.000 Zeichen** begrenzt.
1. Datei wird nach `backend/data/corrupt-backups/` verschoben
2. neue DB wird initialisiert
3. Schema wird neu aufgebaut
---
## Schema-Migrationen
`database.js` implementiert **automatische Migrationen**:
1. Beim Start wird das aktuelle Schema geprüft
2. Fehlende Tabellen werden erstellt
3. Fehlende Spalten werden hinzugefügt
4. Neue Default-Einstellungen werden eingefügt
### Korruptions-Recovery
Falls die Datenbankdatei korrupt ist:
```
1. Korrupte Datei wird erkannt (Verbindungsfehler / Integritätsprüfung)
2. Datei wird in backend/data/corrupt-backups/ verschoben
3. Neue, leere Datenbank wird erstellt
4. Schema wird neu initialisiert
5. Log-Eintrag mit Warnung
```
---
## Datenbankpfad konfigurieren
Standard: `./data/ripster.db` (relativ zum Backend-Verzeichnis)
Über Umgebungsvariable anpassen:
```env
DB_PATH=/var/lib/ripster/ripster.db
```
---
## Direkte Datenbankinspektion
## Direkte Inspektion
```bash
# SQLite3-CLI
sqlite3 backend/data/ripster.db
# Alle Jobs anzeigen
.mode table
SELECT id, status, title, created_at FROM jobs ORDER BY created_at DESC;
# Einstellungen anzeigen
SELECT key, value FROM settings_values;
SELECT key, value FROM settings_values ORDER BY key;
```
+47 -157
View File
@@ -1,192 +1,82 @@
# Frontend-Komponenten
Das Frontend ist mit **React 18** und **PrimeReact** gebaut und kommuniziert über REST-API und WebSocket mit dem Backend.
Frontend: React + PrimeReact + Vite.
---
## Seiten (Pages)
## Hauptseiten
### DashboardPage.jsx
### `DashboardPage.jsx`
Die Hauptseite von Ripster – zeigt den aktuellen Pipeline-Status und ermöglicht alle Workflow-Aktionen.
Pipeline-Steuerung:
**Funktionen:**
- Anzeige des aktuellen Pipeline-Zustands (IDLE, DISC_DETECTED, METADATA_SELECTION, RIPPING, MEDIAINFO_CHECK, READY_TO_ENCODE, ENCODING, ...)
- Live-Fortschrittsbalken mit ETA
- Trigger für Metadaten-Dialog
- Playlist-Entscheidungs-UI (bei Blu-ray Obfuskierung)
- Encode-Review mit Track-Auswahl
- Job-Steuerung (Start, Abbruch, Retry, Queue-Interaktion)
- Status/Progress/ETA
- Metadaten-Dialog
- Playlist-Entscheidung
- Review-Panel
- Queue-Interaktion (reorder/add/remove)
- Job-Aktionen (Start/Cancel/Retry/Re-Encode)
- Hardware-Monitoring-Anzeige
**Zugehörige Komponenten:**
- `PipelineStatusCard` – Status-Widget
- `MetadataSelectionDialog` – OMDb-Suche und Playlist-Auswahl
- `MediaInfoReviewPanel` – Track-Auswahl vor dem Encoding
- Queue- und Job-Karten-UI direkt in `DashboardPage`
### `SettingsPage.jsx`
### SettingsPage.jsx
Konfiguration:
Konfigurationsoberfläche für alle Ripster-Einstellungen.
- dynamisches Settings-Formular (`DynamicSettingsForm`)
- Skripte/Ketten inkl. Reorder/Test
- User-Presets
- Cron-Jobs (`CronJobsTab`)
**Funktionen:**
- Dynamisch generiertes Formular aus dem Settings-Schema
- Echtzeit-Validierungsfeedback
- PushOver-Verbindungstest
- Automatische Aktualisierung des Encode-Reviews bei relevanten Änderungen
### `HistoryPage.jsx`
### DatabasePage.jsx (`/history`)
Historie:
Job-Historie und Datenbankansicht mit vollständigem Audit-Trail.
**Funktionen:**
- Sortier- und filterbares Job-Verzeichnis
- Statusfilter (FINISHED, ERROR, WAITING_FOR_USER_DECISION, ...)
- Job-Detail-Dialog mit vollständigen Logs
- Re-Encode, Löschen und Metadaten-Zuweisung
- Import von Orphan-Raw-Ordnern
- Job-Liste/Filter
- Job-Details + Logs
- OMDb-Nachzuweisung
- Re-Encode/Restart-Workflows
---
## Komponenten (Components)
## Wichtige Komponenten
### MetadataSelectionDialog.jsx
Dialog für die Metadaten-Auswahl nach der Disc-Analyse.
```
┌─────────────────────────────────────┐
│ Metadaten auswählen │
├─────────────────────────────────────┤
│ Suche: [Inception ] 🔍 │
├─────────────────────────────────────┤
│ Ergebnisse: │
│ ▶ Inception (2010) – Movie │
│ Inception: ... (2011) – Series │
├─────────────────────────────────────┤
│ Playlist (nur Blu-ray): │
│ ▶ 00800.mpls (2:30:15) ✓ Empfohlen │
│ 00801.mpls (0:01:23) │
├─────────────────────────────────────┤
│ [Bestätigen] │
└─────────────────────────────────────┘
```
### MediaInfoReviewPanel.jsx
Track-Auswahl-Panel vor dem Encoding.
```
┌─────────────────────────────────────┐
│ Encode-Review │
├─────────────────────────────────────┤
│ Audio-Tracks: │
│ ☑ Track 1: Deutsch (AC-3, 5.1) │
│ ☑ Track 2: English (TrueHD, 7.1) │
│ ☐ Track 3: Français (AC-3, 2.0) │
├─────────────────────────────────────┤
│ Untertitel: │
│ ☑ Track 1: Deutsch │
│ ☐ Track 2: English │
├─────────────────────────────────────┤
│ [Encoding starten] │
└─────────────────────────────────────┘
```
### DynamicSettingsForm.jsx
Wiederverwendbares Formular, das aus dem Settings-Schema generiert wird.
**Unterstützte Feldtypen:**
| Typ | UI-Element |
|----|-----------|
| `string` | Text-Input |
| `number` | Zahlen-Input mit Min/Max |
| `boolean` | Toggle/Checkbox |
| `select` | Dropdown |
| `password` | Passwort-Input |
### PipelineStatusCard.jsx
Status-Anzeige-Widget für die Dashboard-Seite.
### JobDetailDialog.jsx
Vollständiger Job-Detail-Dialog mit Logs-Viewer.
- `PipelineStatusCard.jsx`
- `MetadataSelectionDialog.jsx`
- `MediaInfoReviewPanel.jsx`
- `JobDetailDialog.jsx`
- `CronJobsTab.jsx`
---
## Hooks
## API-Client (`api/client.js`)
### useWebSocket.js
Zentraler Custom-Hook für die WebSocket-Verbindung.
```js
useWebSocket({
onMessage: (msg) => {
if (msg.type === 'PIPELINE_STATE_CHANGED') {
setPipelineState(msg.payload);
}
}
});
```
**Features:**
- Automatische Verbindung zu `/ws`
- Reconnect mit festem Intervall (`1500ms`)
- Message-Parsing (JSON)
- zentraler `request()` mit JSON-Handling
- Fehlerobjekt aus API wird auf `Error(message)` gemappt
- `VITE_API_BASE` default `/api`
---
## API-Client (client.js)
## WebSocket (`hooks/useWebSocket.js`)
Zentraler HTTP-Client für alle Backend-Anfragen.
- URL: `VITE_WS_URL` oder automatisch `ws(s)://<host>/ws`
- Auto-Reconnect mit 1500ms Intervall
```js
// Beispiel-Aufrufe
const state = await api.getPipelineState();
const results = await api.searchOmdb('Inception');
await api.selectMetadata({ jobId, title, year, imdbId, selectedPlaylist });
await api.confirmEncodeReview(jobId, {
selectedEncodeTitleId: 1,
selectedTrackSelection: { 1: { audioTrackIds: [1], subtitleTrackIds: [3] } }
});
```
In `App.jsx` werden u. a. verarbeitet:
**Features:**
- Zentralisierte Fehlerbehandlung
- Automatische JSON-Serialisierung
- Basis-URL aus Umgebungsvariable (`VITE_API_BASE`)
- `PIPELINE_STATE_CHANGED`
- `PIPELINE_PROGRESS`
- `PIPELINE_QUEUE_CHANGED`
- `DISC_DETECTED` / `DISC_REMOVED`
- `HARDWARE_MONITOR_UPDATE`
---
## Build & Entwicklung
### Entwicklungsserver
## Build/Run
```bash
cd frontend
npm run dev
# → http://localhost:5173
```
# dev
npm run dev --prefix frontend
### Vite-Proxy-Konfiguration
In der Entwicklungsumgebung proxied Vite API-Anfragen zum Backend:
```js
// vite.config.js
proxy: {
'/api': 'http://localhost:3001',
'/ws': { target: 'ws://localhost:3001', ws: true }
}
```
### Production-Build
```bash
cd frontend
npm run build
# → frontend/dist/
# prod build
npm run build --prefix frontend
```
+30 -71
View File
@@ -1,6 +1,6 @@
# Architektur
Ripster ist als klassische **Client-Server-Anwendung** mit Echtzeit-Kommunikation über WebSockets aufgebaut.
Ripster ist eine Client-Server-Anwendung mit REST + WebSocket.
---
@@ -9,104 +9,63 @@ Ripster ist als klassische **Client-Server-Anwendung** mit Echtzeit-Kommunikatio
```mermaid
graph TB
subgraph Browser["Browser (React)"]
Dashboard["Dashboard"]
Settings["Einstellungen"]
History["History"]
Dashboard[Dashboard]
Settings[Einstellungen]
History[Historie]
end
subgraph Backend["Node.js Backend"]
API["REST API\n(Express)"]
WS["WebSocket\nServer"]
Pipeline["Pipeline\nService"]
DB["SQLite\nDatenbank"]
API[REST API\nExpress]
WS[WebSocket\n/ws]
Pipeline[pipelineService]
Cron[cronService]
DB[(SQLite)]
end
subgraph ExternalTools["Externe Tools"]
MakeMKV["makemkvcon"]
HandBrake["HandBrakeCLI"]
MediaInfo["mediainfo"]
subgraph Tools["Externe Tools"]
MakeMKV[makemkvcon]
HandBrake[HandBrakeCLI]
MediaInfo[mediainfo]
end
subgraph ExternalAPIs["Externe APIs"]
OMDb["OMDb API"]
PushOver["PushOver"]
end
Browser <-->|HTTP REST| API
Browser <-->|HTTP| API
Browser <-->|WebSocket| WS
Pipeline --> MakeMKV
Pipeline --> HandBrake
Pipeline --> MediaInfo
Pipeline <-->|Metadaten| OMDb
Pipeline -->|Benachrichtigungen| PushOver
API --> DB
Pipeline --> DB
Cron --> DB
```
---
## Schichten-Architektur
## Schichten
### Backend
```
index.js (Express Server)
├── Routes (API-Endpunkte)
│ ├── pipelineRoutes.js
│ ├── settingsRoutes.js
│ └── historyRoutes.js
├── Services (Business Logic)
│ ├── pipelineService.js ← Kern-Orchestrierung
│ ├── diskDetectionService.js
│ ├── processRunner.js
│ ├── websocketService.js
│ ├── omdbService.js
│ ├── settingsService.js
│ ├── notificationService.js
│ ├── historyService.js
│ └── logger.js
├── Database
│ ├── database.js
│ └── defaultSettings.js
└── Utils
├── encodePlan.js
├── playlistAnalysis.js
├── progressParsers.js
└── files.js
```
- `src/index.js` (Bootstrapping, Routes, WS, Services)
- `src/routes/*` (Pipeline, Settings, History, Crons)
- `src/services/*` (Business-Logik)
- `src/db/database.js` (Init/Migration)
- `src/utils/*` (Parser, Dateifunktionen, Validierung)
### Frontend
```
App.jsx (React Router)
├── Pages
│ ├── DashboardPage.jsx ← Haupt-Interface
│ ├── SettingsPage.jsx
│ └── DatabasePage.jsx ← Historie/DB-Ansicht
├── Components
│ ├── PipelineStatusCard.jsx
│ ├── MetadataSelectionDialog.jsx
│ ├── MediaInfoReviewPanel.jsx
│ ├── DynamicSettingsForm.jsx
│ └── JobDetailDialog.jsx
├── Hooks
│ └── useWebSocket.js
└── API
└── client.js
```
- `App.jsx` + `pages/*` (Dashboard, Settings, History)
- `components/*` (Status-/Review-/Dialog-Komponenten)
- `api/client.js` (REST-Client)
- `hooks/useWebSocket.js` (WS-Reconnect)
---
## Weiterführende Dokumentation
## Weiterführend
<div class="grid cards" markdown>
- [:octicons-arrow-right-24: Übersicht](overview.md)
- [:octicons-arrow-right-24: Backend-Services](backend.md)
- [:octicons-arrow-right-24: Frontend-Komponenten](frontend.md)
- [:octicons-arrow-right-24: Datenbank](database.md)
- [:octicons-arrow-right-24: Übersicht](overview.md)
- [:octicons-arrow-right-24: Backend-Services](backend.md)
- [:octicons-arrow-right-24: Frontend-Komponenten](frontend.md)
- [:octicons-arrow-right-24: Datenbank](database.md)
</div>
+49 -100
View File
@@ -2,144 +2,93 @@
---
## Kern-Designprinzipien
## Kernprinzipien
### Event-Driven Pipeline
### Event-getriebene Pipeline
Der gesamte Ripping-Workflow ist als **State Machine** implementiert. Der `pipelineService` verwaltet den aktuellen Zustand und emittiert Ereignisse bei jedem Zustandswechsel. Der WebSocket-Service überträgt diese Ereignisse sofort an alle verbundenen Clients.
`pipelineService` hält einen Snapshot der State-Machine und broadcastet Änderungen sofort via WebSocket.
```
Zustandswechsel → Event → WebSocket → Frontend-Update
```text
State-Änderung -> PIPELINE_STATE_CHANGED/PIPELINE_PROGRESS -> Frontend-Update
```
### Service-Layer-Muster
### Service-Layer
```
HTTP-Route → Service → Datenbank
```text
Route -> Service -> DB/Tool-Execution
```
Routes delegieren die gesamte Business-Logik an Services. Services sind voneinander unabhängig und können einzeln getestet werden.
Routes enthalten kaum Business-Logik.
### Schema-getriebene Einstellungen
### Schema-getriebene Settings
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.
Settings sind DB-schema-getrieben (`settings_schema` + `settings_values`), UI rendert dynamisch aus diesen Daten.
---
## Echtzeit-Kommunikation
### WebSocket-Protokoll
WebSocket läuft auf `/ws`.
Der WebSocket-Server läuft unter dem Pfad `/ws`. Nachrichten werden als JSON übertragen:
Wichtige Events:
```json
{
"type": "PIPELINE_STATE_CHANGED",
"payload": {
"state": "ENCODING",
"activeJobId": 42,
"progress": 73.5,
"eta": "00:12:34"
}
}
```
**Nachrichtentypen:**
| Typ | Beschreibung |
|----|-------------|
| `PIPELINE_STATE_CHANGED` | Pipeline-Zustand hat gewechselt |
| `PIPELINE_PROGRESS` | Fortschritt (% und ETA) |
| `PIPELINE_QUEUE_CHANGED` | Queue-Status geändert |
| `DISC_DETECTED` | Disc wurde erkannt |
| `DISC_REMOVED` | Disc wurde entfernt |
| `PIPELINE_ERROR` | Pipeline-Fehler aufgetreten |
| `DISK_DETECTION_ERROR` | Laufwerkserkennung-Fehler |
### Reconnect-Logik
Der Frontend-Hook `useWebSocket.js` implementiert automatisches Reconnect mit festem Intervall von 1500ms bei Verbindungsabbrüchen.
- `PIPELINE_STATE_CHANGED`, `PIPELINE_PROGRESS`, `PIPELINE_QUEUE_CHANGED`
- `DISC_DETECTED`, `DISC_REMOVED`
- `HARDWARE_MONITOR_UPDATE`
- `SETTINGS_UPDATED`, `SETTINGS_BULK_UPDATED`
- `SETTINGS_SCRIPTS_UPDATED`, `SETTINGS_SCRIPT_CHAINS_UPDATED`, `USER_PRESETS_UPDATED`
- `CRON_JOBS_UPDATED`, `CRON_JOB_UPDATED`
- `PIPELINE_ERROR`, `DISK_DETECTION_ERROR`
---
## Prozess-Management
## Prozessausführung
### processRunner.js
Externe Tools werden als Child-Processes gestartet (`processRunner`):
Externe Tools (MakeMKV, HandBrake, MediaInfo) werden als **Child Processes** gestartet:
```js
spawn(command, args, { stdio: ['ignore', 'pipe', 'pipe'] })
```
- **stdout/stderr** werden zeilenweise gelesen und in Echtzeit verarbeitet
- **Progress-Parsing** erfolgt über reguläre Ausdrücke in `progressParsers.js`
- **Graceful Shutdown**: SIGINT → Timeout → SIGKILL
- **Prozess-Tracking**: Aktive Prozesse werden registriert für sauberes Beenden
- Streaming von stdout/stderr
- Progress-Parsing (`progressParsers.js`)
- kontrollierter Abbruch (SIGINT/SIGKILL-Fallback)
---
## Datenpersistenz
## Persistenz
### SQLite-Datenbank
SQLite-Datei: `backend/data/ripster.db`
Ripster verwendet eine **einzige SQLite-Datei** für alle persistenten Daten:
Kern-Tabellen:
```
backend/data/ripster.db
```
- `jobs`, `pipeline_state`
- `settings_schema`, `settings_values`
- `scripts`, `script_chains`, `script_chain_steps`
- `user_presets`
- `cron_jobs`, `cron_run_logs`
**Tabellen:**
| Tabelle | Inhalt |
|---------|--------|
| `jobs` | Alle Rip-Jobs mit Status, Logs, Metadaten |
| `pipeline_state` | Aktueller Pipeline-Zustand (Singleton) |
| `settings_schema` | Schema aller verfügbaren Einstellungen |
| `settings_values` | Benutzer-konfigurierte Werte |
### Migrations-Strategie
Beim Start prüft `database.js` automatisch, ob das Schema aktuell ist, und führt fehlende Migrationen aus. Korrupte Datenbankdateien werden in ein Quarantäne-Verzeichnis verschoben und eine neue Datenbank erstellt.
Beim Start werden Schema und Settings-Migrationen automatisch ausgeführt.
---
## Fehlerbehandlung
### Strukturierte Fehler
Zentrales Error-Handling liefert:
Alle Fehler werden mit Kontext-Metadaten protokolliert:
```js
logger.error('Encoding fehlgeschlagen', {
jobId: job.id,
command: cmd,
exitCode: code,
stderr: lastLines
});
```json
{
"error": {
"message": "...",
"statusCode": 400,
"reqId": "...",
"details": []
}
}
```
### Job-Fehler-Recovery
- Fehlgeschlagene Jobs bleiben in der Datenbank (Status `ERROR`)
- Vollständige Fehler-Logs werden im Job-Datensatz gespeichert
- **Retry-Funktion** ermöglicht Neustart von einem Fehler-Zustand
- **Re-Encode** erlaubt erneutes Encodieren ohne neu zu rippen
Fehlgeschlagene Jobs bleiben in der Historie (`ERROR` oder `CANCELLED`) und können erneut gestartet werden.
---
## Sicherheit
## CORS & Runtime-Konfig
### Eingabe-Validierung
- Alle Benutzer-Eingaben werden in `validators.js` validiert
- CLI-Argumente werden sicher über `commandLine.js` konstruiert (kein Shell-Injection-Risiko)
- Pfade werden sanitisiert bevor sie an externe Prozesse übergeben werden
### CORS-Konfiguration
```env
CORS_ORIGIN=http://localhost:5173
```
In Produktion sollte dieser Wert auf die tatsächliche Frontend-URL gesetzt werden.
- `CORS_ORIGIN` default: `*`
- `LOG_LEVEL` default: `info`
- DB-/Log-Pfade über `DB_PATH`/`LOG_DIR` konfigurierbar
+37 -66
View File
@@ -1,96 +1,67 @@
# Umgebungsvariablen
Umgebungsvariablen überschreiben die Standardwerte und eignen sich für Server-Deployments.
Umgebungsvariablen steuern Backend/Vite außerhalb der DB-basierten UI-Settings.
---
## Backend-Umgebungsvariablen
## Backend (`backend/.env`)
Konfigurationsdatei: `backend/.env`
| Variable | Default (Code) | Beschreibung |
|---------|------------------|-------------|
| `PORT` | `3001` | Express-Port |
| `DB_PATH` | `backend/data/ripster.db` | SQLite-Datei (relativ zu `backend/`) |
| `LOG_DIR` | `backend/logs` | Fallback-Logverzeichnis (wenn `log_dir`-Setting nicht gesetzt/lesbar) |
| `CORS_ORIGIN` | `*` | CORS-Origin für API |
| `LOG_LEVEL` | `info` | `debug`, `info`, `warn`, `error` |
| Variable | Standard | Beschreibung |
|---------|---------|-------------|
| `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ür Log-Dateien |
| `LOG_LEVEL` | `info` | Log-Level (`debug`, `info`, `warn`, `error`) |
### Beispiel: backend/.env
Beispiel:
```env
PORT=3001
DB_PATH=/var/lib/ripster/ripster.db
CORS_ORIGIN=http://192.168.1.100:5173
LOG_DIR=/var/log/ripster
CORS_ORIGIN=http://192.168.1.50:5173
LOG_LEVEL=info
```
Hinweis: `backend/.env.example` enthält bewusst dev-freundliche Werte (z. B. lokaler `CORS_ORIGIN`).
---
## Frontend-Umgebungsvariablen
## Frontend (`frontend/.env`)
Konfigurationsdatei: `frontend/.env`
| Variable | Standard | Beschreibung |
| Variable | Default | Beschreibung |
|---------|---------|-------------|
| `VITE_API_BASE` | `http://localhost:3001` | Backend-API-URL |
| `VITE_WS_URL` | `ws://localhost:3001` | WebSocket-URL |
| `VITE_PUBLIC_ORIGIN` | — | Öffentliche Origin-URL (für CORS) |
| `VITE_HMR_HOST` | — | Vite HMR-Host (für Remote-Entwicklung) |
| `VITE_HMR_PORT` | — | Vite HMR-Port |
| `VITE_API_BASE` | `/api` | API-Basis für Fetch-Client |
| `VITE_WS_URL` | automatisch aus `window.location` + `/ws` | Optional explizite WebSocket-URL |
| `VITE_PUBLIC_ORIGIN` | leer | Öffentliche Vite-Origin (Remote-Dev) |
| `VITE_ALLOWED_HOSTS` | `true` | Komma-separierte Hostliste für Vite `allowedHosts` |
| `VITE_HMR_PROTOCOL` | abgeleitet aus `VITE_PUBLIC_ORIGIN` | HMR-Protokoll (`ws`/`wss`) |
| `VITE_HMR_HOST` | abgeleitet aus `VITE_PUBLIC_ORIGIN` | HMR-Host |
| `VITE_HMR_CLIENT_PORT` | abgeleitet aus `VITE_PUBLIC_ORIGIN` | HMR-Client-Port |
### Beispiel: frontend/.env (Entwicklung)
Beispiele:
```env
VITE_API_BASE=http://localhost:3001
VITE_WS_URL=ws://localhost:3001
# lokal (mit Vite-Proxy)
VITE_API_BASE=/api
```
### Beispiel: frontend/.env (Netzwerk-Zugriff)
```env
VITE_API_BASE=http://192.168.1.100:3001
VITE_WS_URL=ws://192.168.1.100:3001
VITE_PUBLIC_ORIGIN=http://192.168.1.100:5173
# remote dev
VITE_API_BASE=http://192.168.1.50:3001/api
VITE_WS_URL=ws://192.168.1.50:3001/ws
VITE_PUBLIC_ORIGIN=http://192.168.1.50:5173
VITE_ALLOWED_HOSTS=192.168.1.50,ripster.local
VITE_HMR_PROTOCOL=ws
VITE_HMR_HOST=192.168.1.50
VITE_HMR_CLIENT_PORT=5173
```
---
## .env.example Dateien
## Priorität
Das Repository enthält Vorlagen für beide Konfigurationsdateien:
```bash
# Backend
cp backend/.env.example backend/.env
# Frontend
cp frontend/.env.example frontend/.env
```
---
## Priorität der Konfiguration
Einstellungen werden in folgender Reihenfolge geladen (höhere Priorität überschreibt niedrigere):
```
1. Systemumgebungsvariablen (export VAR=value)
2. .env-Datei
3. Hardcodierte Standardwerte in config.js
```
---
## LOG_LEVEL
| Level | Ausgabe |
|-------|---------|
| `debug` | Alle Meldungen inkl. Debugging |
| `info` | Normale Betriebsinformationen |
| `warn` | Warnungen + Fehler |
| `error` | Nur Fehler |
!!! tip "Produktionsempfehlung"
Für Produktionsumgebungen `LOG_LEVEL=info` oder `LOG_LEVEL=warn` verwenden. `debug` erzeugt sehr viele Log-Einträge.
1. Prozess-Umgebungsvariablen
2. `.env`
3. Code-Defaults
+133 -148
View File
@@ -1,180 +1,165 @@
# Einstellungsreferenz
Vollständige Übersicht aller Ripster-Einstellungen. Alle Einstellungen werden über die Web-Oberfläche unter **Einstellungen** verwaltet und in SQLite gespeichert.
Alle Settings liegen in `settings_schema`/`settings_values` und werden über die UI verwaltet.
---
## Profil-System
Ripster erkennt den Medientyp einer eingelegten Disc (Blu-ray / DVD / CD) und wählt automatisch die passenden profil-spezifischen Einstellungen. Für viele Schlüssel gibt es zusätzlich zur globalen Einstellung eine Variante pro Profil:
Ripster arbeitet mit Media-Profilen:
| Profil | Erkennungsmerkmale |
|--------|--------------------|
| `bluray` | UDF-Dateisystem, Laufwerk-Modell enthält „Blu-ray", Disc-Label wie BDMV |
| `dvd` | ISO9660/UDF, Laufwerk-Modell enthält „DVD", VIDEO_TS-Struktur |
| `other` | Alles andere (CD, unbekannt) |
- `bluray`
- `dvd`
- `other`
**Auflösungsreihenfolge für profil-spezifische Einstellungen:**
Viele Tool-/Pfad-Settings existieren als Profil-Varianten (`*_bluray`, `*_dvd`, `*_other`).
1. Profil-spezifischer Wert (`_bluray` / `_dvd`) – wenn gesetzt, hat dieser Vorrang
2. Alternativ-Profil als Fallback (Blu-ray → DVD-Wert als Fallback und umgekehrt)
Wichtig:
Pfad-Einstellungen (`raw_dir`, `movie_dir`) und Besitz-Einstellungen (`raw_dir_owner`, `movie_dir_owner`) werden **ausschließlich** aus dem passenden Profil bezogen – kein Cross-Profil-Fallback.
- Für `raw_dir`, `movie_dir` und die zugehörigen `*_owner`-Keys gibt es **kein Cross-Profil-Fallback**.
- Für viele Tool-Keys werden profilspezifische Varianten bevorzugt.
---
## Template-Platzhalter
Datei-/Ordner-Templates unterstützen:
- `${title}`
- `${year}`
- `${imdbId}`
Nicht gesetzte Werte werden zu `unknown`.
---
## Kategorie: Pfade
| Schlüssel | Typ | Pflicht | Beschreibung |
|-----------|-----|---------|-------------|
| `raw_dir` | string | ✅ | Verzeichnis für rohe MKV-Dateien (Fallback wenn kein Profil-Wert) |
| `raw_dir_bluray` | string | — | Raw-Verzeichnis für Blu-rays |
| `raw_dir_dvd` | string | — | Raw-Verzeichnis für DVDs |
| `raw_dir_other` | string | — | Raw-Verzeichnis für sonstige Medien |
| `raw_dir_owner` | string | — | Besitzer für Raw-Verzeichnis (`user:group`, Fallback) |
| `raw_dir_bluray_owner` | string | — | Besitzer für Raw-Verzeichnis (Blu-ray) |
| `raw_dir_dvd_owner` | string | — | Besitzer für Raw-Verzeichnis (DVD) |
| `raw_dir_other_owner` | string | — | Besitzer für Raw-Verzeichnis (Sonstiges) |
| `movie_dir` | string | ✅ | Ausgabeverzeichnis für Filme (Fallback) |
| `movie_dir_bluray` | string | — | Ausgabeverzeichnis für Blu-rays |
| `movie_dir_dvd` | string | — | Ausgabeverzeichnis für DVDs |
| `movie_dir_other` | string | — | Ausgabeverzeichnis für sonstige Medien |
| `movie_dir_owner` | string | — | Besitzer für Ausgabeverzeichnis (Fallback) |
| `movie_dir_bluray_owner` | string | — | Besitzer für Ausgabeverzeichnis (Blu-ray) |
| `movie_dir_dvd_owner` | string | — | Besitzer für Ausgabeverzeichnis (DVD) |
| `movie_dir_other_owner` | string | — | Besitzer für Ausgabeverzeichnis (Sonstiges) |
| `log_dir` | string | — | Verzeichnis für Log-Dateien (Standard: `./logs`) |
| Key | Typ | Default |
|-----|-----|---------|
| `raw_dir` | path | `data/output/raw` |
| `raw_dir_bluray` | path | `null` |
| `raw_dir_dvd` | path | `null` |
| `raw_dir_other` | path | `null` |
| `raw_dir_bluray_owner` | string | `null` |
| `raw_dir_dvd_owner` | string | `null` |
| `raw_dir_other_owner` | string | `null` |
| `movie_dir` | path | `data/output/movies` |
| `movie_dir_bluray` | path | `null` |
| `movie_dir_dvd` | path | `null` |
| `movie_dir_other` | path | `null` |
| `movie_dir_bluray_owner` | string | `null` |
| `movie_dir_dvd_owner` | string | `null` |
| `movie_dir_other_owner` | string | `null` |
| `log_dir` | path | `data/logs` |
---
## Kategorie: Laufwerk
| Schlüssel | Typ | Standard | Beschreibung |
|-----------|-----|---------|-------------|
| `drive_mode` | select | `auto` | `auto` = automatisch erkennen, `explicit` = festes Gerät |
| `drive_device` | string | `/dev/sr0` | Geräte-Pfad (nur bei `explicit`) |
| `disc_poll_interval_ms` | number | `4000` | Polling-Intervall in Millisekunden (1000–60000) |
| `makemkv_source_index` | number | `0` | Laufwerk-Index für MakeMKV (bei mehreren Laufwerken) |
---
## Kategorie: Tools (global)
| Schlüssel | 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 |
| `makemkv_min_length_minutes` | number | `15` | Mindest-Titellänge in Minuten (0–999) |
| `pipeline_max_parallel_jobs` | number | `1` | Maximale Anzahl parallel laufender Jobs (1–12) |
| `handbrake_restart_delete_incomplete_output` | boolean | `true` | Unvollständige Ausgabedatei beim Encode-Neustart löschen |
### Kategorie: Tools – Blu-ray
| Schlüssel | Typ | Standard | Beschreibung |
|-----------|-----|---------|-------------|
| `makemkv_rip_mode_bluray` | select | `backup` | Rip-Modus: `mkv` oder `backup` |
| `makemkv_analyze_extra_args_bluray` | string | — | Zusatz-CLI-Parameter für Analyse (Blu-ray) |
| `makemkv_rip_extra_args_bluray` | string | — | Zusatz-CLI-Parameter für Rip (Blu-ray) |
| `mediainfo_extra_args_bluray` | string | — | Zusatz-CLI-Parameter für mediainfo (Blu-ray) |
| `handbrake_preset_bluray` | string | `H.264 MKV 1080p30` | HandBrake-Preset für Blu-rays |
| `handbrake_extra_args_bluray` | string | — | Zusatz-CLI-Argumente für HandBrake (Blu-ray) |
| `output_extension_bluray` | select | `mkv` | Ausgabeformat: `mkv` oder `mp4` |
| `filename_template_bluray` | string | `${title} (${year})` | Dateiname-Template (Blu-ray) |
| `output_folder_template_bluray` | string | — | Ordnername-Template (Blu-ray, leer = Dateiname-Template) |
### Kategorie: Tools – DVD
| Schlüssel | Typ | Standard | Beschreibung |
|-----------|-----|---------|-------------|
| `makemkv_rip_mode_dvd` | select | `mkv` | Rip-Modus: `mkv` oder `backup` |
| `makemkv_analyze_extra_args_dvd` | string | — | Zusatz-CLI-Parameter für Analyse (DVD) |
| `makemkv_rip_extra_args_dvd` | string | — | Zusatz-CLI-Parameter für Rip (DVD) |
| `mediainfo_extra_args_dvd` | string | — | Zusatz-CLI-Parameter für mediainfo (DVD) |
| `handbrake_preset_dvd` | string | `H.264 MKV 480p30` | HandBrake-Preset für DVDs |
| `handbrake_extra_args_dvd` | string | — | Zusatz-CLI-Argumente für HandBrake (DVD) |
| `output_extension_dvd` | select | `mkv` | Ausgabeformat: `mkv` oder `mp4` |
| `filename_template_dvd` | string | `${title} (${year})` | Dateiname-Template (DVD) |
| `output_folder_template_dvd` | string | — | Ordnername-Template (DVD, leer = Dateiname-Template) |
### Globale Fallback-Einstellungen für Encode
Diese Werte werden verwendet, wenn kein profil-spezifischer Wert konfiguriert ist:
| Schlüssel | Typ | Standard | Beschreibung |
|-----------|-----|---------|-------------|
| `handbrake_preset` | string | `H.265 MKV 1080p30` | Fallback HandBrake-Preset |
| `handbrake_extra_args` | string | — | Fallback Extra-Args |
| `makemkv_rip_mode` | select | `mkv` | Fallback Rip-Modus |
| `makemkv_analyze_extra_args` | string | — | Fallback Analyse-Args |
| `makemkv_rip_extra_args` | string | — | Fallback Rip-Args |
| `mediainfo_extra_args` | string | — | Fallback MediaInfo-Args |
| `output_extension` | select | `mkv` | Fallback Ausgabeformat |
| `filename_template` | string | `${title} (${year})` | Fallback Dateiname-Template |
| `output_folder_template` | string | — | Fallback Ordnername-Template |
### Template-Platzhalter
| Platzhalter | Beispiel |
|------------|---------|
| `${title}` | `Inception` |
| `${year}` | `2010` |
| `${imdbId}` | `tt1375666` |
---
## Kategorie: Metadaten
| Schlüssel | Typ | Standard | Beschreibung |
|-----------|-----|---------|-------------|
| `omdb_api_key` | string | — | API-Key von [omdbapi.com](https://www.omdbapi.com/) |
| `omdb_default_type` | select | `movie` | Vorauswahl für OMDb-Suche: `movie`, `series`, `episode` |
---
## Kategorie: Benachrichtigungen (PushOver)
| Schlüssel | Typ | Standard | Beschreibung |
|-----------|-----|---------|-------------|
| `pushover_enabled` | boolean | `false` | Master-Schalter für PushOver |
| `pushover_token` | string | — | Application-Token |
| `pushover_user` | string | — | User-Key |
| `pushover_device` | string | — | Optionales Ziel-Device |
| `pushover_title_prefix` | string | `Ripster` | Präfix im Benachrichtigungstitel |
| `pushover_priority` | number | `0` | Priorität (-2 bis 2) |
| `pushover_timeout_ms` | number | `7000` | HTTP-Timeout für PushOver-Requests (ms) |
### Granulare Event-Schalter
| Schlüssel | Standard | Beschreibung |
|-----------|---------|-------------|
| `pushover_notify_metadata_ready` | `true` | Bei Metadaten-Auswahl benachrichtigen |
| `pushover_notify_rip_started` | `true` | Bei MakeMKV-Rip-Start |
| `pushover_notify_encoding_started` | `true` | Bei HandBrake-Start |
| `pushover_notify_job_finished` | `true` | Bei erfolgreichem Abschluss |
| `pushover_notify_job_error` | `true` | Bei Fehler |
| `pushover_notify_job_cancelled` | `true` | Bei manuellem Abbruch |
| `pushover_notify_reencode_started` | `true` | Bei Re-Encode-Start |
| `pushover_notify_reencode_finished` | `true` | Bei erfolgreichem Re-Encode |
| Key | Typ | Default | Hinweis |
|-----|-----|---------|--------|
| `drive_mode` | select | `auto` | `auto` oder `explicit` |
| `drive_device` | path | `/dev/sr0` | bei `explicit` relevant |
| `makemkv_source_index` | number | `0` | MakeMKV Source-Index |
| `disc_poll_interval_ms` | number | `4000` | 1000..60000 |
---
## Kategorie: Monitoring
| Schlüssel | Typ | Standard | Beschreibung |
|-----------|-----|---------|-------------|
| `hardware_monitoring_enabled` | boolean | `false` | Hardware-Monitoring aktivieren (CPU, RAM, Temp.) |
| `hardware_monitoring_interval_ms` | number | `5000` | Monitoring-Polling-Intervall (ms) |
| Key | Typ | Default |
|-----|-----|---------|
| `hardware_monitoring_enabled` | boolean | `true` |
| `hardware_monitoring_interval_ms` | number | `5000` |
---
## Standard-Einstellungen zurücksetzen
## Kategorie: Tools (global)
Einen einzelnen Wert über die Datenbank zurücksetzen:
| Key | Typ | Default |
|-----|-----|---------|
| `makemkv_command` | string | `makemkvcon` |
| `makemkv_registration_key` | string | `null` |
| `mediainfo_command` | string | `mediainfo` |
| `makemkv_min_length_minutes` | number | `60` |
| `handbrake_command` | string | `HandBrakeCLI` |
| `handbrake_restart_delete_incomplete_output` | boolean | `true` |
| `pipeline_max_parallel_jobs` | number | `1` |
```bash
sqlite3 backend/data/ripster.db \
"DELETE FROM settings_values WHERE key = 'handbrake_preset_bluray';"
```
### Blu-ray-spezifisch
Beim nächsten Laden wird der Standardwert aus `settings_schema.default_value` verwendet.
| Key | Typ | Default |
|-----|-----|---------|
| `mediainfo_extra_args_bluray` | string | `null` |
| `makemkv_rip_mode_bluray` | select | `backup` |
| `makemkv_analyze_extra_args_bluray` | string | `null` |
| `makemkv_rip_extra_args_bluray` | string | `null` |
| `handbrake_preset_bluray` | string | `H.264 MKV 1080p30` |
| `handbrake_extra_args_bluray` | string | `null` |
| `output_extension_bluray` | select | `mkv` |
| `filename_template_bluray` | string | `${title} (${year})` |
| `output_folder_template_bluray` | string | `null` |
### DVD-spezifisch
| Key | Typ | Default |
|-----|-----|---------|
| `mediainfo_extra_args_dvd` | string | `null` |
| `makemkv_rip_mode_dvd` | select | `mkv` |
| `makemkv_analyze_extra_args_dvd` | string | `null` |
| `makemkv_rip_extra_args_dvd` | string | `null` |
| `handbrake_preset_dvd` | string | `H.264 MKV 480p30` |
| `handbrake_extra_args_dvd` | string | `null` |
| `output_extension_dvd` | select | `mkv` |
| `filename_template_dvd` | string | `${title} (${year})` |
| `output_folder_template_dvd` | string | `null` |
---
## Kategorie: Metadaten
| Key | Typ | Default |
|-----|-----|---------|
| `omdb_api_key` | string | `null` |
| `omdb_default_type` | select | `movie` |
---
## Kategorie: Benachrichtigungen (PushOver)
| Key | Typ | Default |
|-----|-----|---------|
| `pushover_enabled` | boolean | `false` |
| `pushover_token` | string | `null` |
| `pushover_user` | string | `null` |
| `pushover_device` | string | `null` |
| `pushover_title_prefix` | string | `Ripster` |
| `pushover_priority` | number | `0` |
| `pushover_timeout_ms` | number | `7000` |
| `pushover_notify_metadata_ready` | boolean | `true` |
| `pushover_notify_rip_started` | boolean | `true` |
| `pushover_notify_encoding_started` | boolean | `true` |
| `pushover_notify_job_finished` | boolean | `true` |
| `pushover_notify_job_error` | boolean | `true` |
| `pushover_notify_job_cancelled` | boolean | `true` |
| `pushover_notify_reencode_started` | boolean | `true` |
| `pushover_notify_reencode_finished` | boolean | `true` |
---
## Entfernte Legacy-Keys
Diese Legacy-Keys werden bei Migration entfernt und sollten nicht mehr genutzt werden:
- `makemkv_backup_mode`
- `mediainfo_extra_args`
- `makemkv_rip_mode`
- `makemkv_analyze_extra_args`
- `makemkv_rip_extra_args`
- `handbrake_preset`
- `handbrake_extra_args`
- `output_extension`
- `filename_template`
- `output_folder_template`
- `pushover_notify_disc_detected`
+36 -88
View File
@@ -5,7 +5,7 @@
## Voraussetzungen
- Node.js >= 20.19.0
- Alle [externen Tools](../getting-started/prerequisites.md) installiert
- externe Tools installiert (`makemkvcon`, `HandBrakeCLI`, `mediainfo`)
---
@@ -15,15 +15,18 @@
./start.sh
```
Das Skript startet automatisch:
- **Backend** auf Port 3001 (mit Nodemon für Hot-Reload)
- **Frontend** auf Port 5173 (mit Vite HMR)
Startet:
- Backend (`http://localhost:3001`, mit nodemon)
- Frontend (`http://localhost:5173`, mit Vite HMR)
Stoppen: `Ctrl+C`.
---
## Manuelle Entwicklungsumgebung
## Manuell
### Terminal 1 – Backend
### Backend
```bash
cd backend
@@ -31,9 +34,7 @@ npm install
npm run dev
```
Backend läuft auf `http://localhost:3001` mit **Nodemon** – Neustart bei Dateiänderungen.
### Terminal 2 – Frontend
### Frontend
```bash
cd frontend
@@ -41,97 +42,44 @@ npm install
npm run dev
```
Frontend läuft auf `http://localhost:5173` mit **Vite HMR** – sofortige Browser-Updates.
---
## Vite-Proxy (Dev)
`frontend/vite.config.js` proxied standardmäßig:
- `/api` -> `http://127.0.0.1:3001`
- `/ws` -> `ws://127.0.0.1:3001`
---
## Vite-Proxy
## Remote-Dev (optional)
Im Entwicklungsmodus proxied Vite alle API- und WebSocket-Anfragen zum Backend:
```js
// frontend/vite.config.js
server: {
proxy: {
'/api': {
target: 'http://localhost:3001',
changeOrigin: true
},
'/ws': {
target: 'ws://localhost:3001',
ws: true
}
}
}
```
Das bedeutet: Im Browser macht das Frontend Anfragen an `localhost:5173/api/...` – Vite leitet diese an `localhost:3001/api/...` weiter.
---
## Remote-Entwicklung
Falls Ripster auf einem entfernten Server entwickelt wird (z.B. Homeserver), muss die Vite-Konfiguration angepasst werden:
Beispiel `frontend/.env.local`:
```env
# frontend/.env.local
VITE_API_BASE=http://192.168.1.100:3001
VITE_WS_URL=ws://192.168.1.100:3001
VITE_API_BASE=http://192.168.1.100:3001/api
VITE_WS_URL=ws://192.168.1.100:3001/ws
VITE_PUBLIC_ORIGIN=http://192.168.1.100:5173
VITE_ALLOWED_HOSTS=192.168.1.100,ripster.local
VITE_HMR_PROTOCOL=ws
VITE_HMR_HOST=192.168.1.100
VITE_HMR_PORT=5173
VITE_HMR_CLIENT_PORT=5173
```
---
## Log-Level für Entwicklung
```env
# backend/.env
LOG_LEVEL=debug
```
Im Debug-Modus werden alle Ausgaben der externen Tools (MakeMKV, HandBrake) vollständig geloggt.
---
## Stoppen
## Nützliche Kommandos
```bash
./kill.sh
# Root dev (backend + frontend)
npm run dev
# einzeln
npm run dev:backend
npm run dev:frontend
# Frontend Build
npm run build:frontend
```
---
## Linting & Type-Checking
```bash
# Frontend (ESLint)
cd frontend && npm run lint
# Backend hat keine separaten Lint-Scripts,
# nutze direkt eslint falls konfiguriert
```
---
## Deployment-Script
Das `deploy-ripster.sh`-Script überträgt Code auf einen Remote-Server per SSH:
```bash
./deploy-ripster.sh
```
**Was das Script tut:**
1. `rsync` synchronisiert den Code (Backend-Quellcode ohne `data/`)
2. Die Datenbank (`backend/data/`) wird **nicht** überschrieben
3. Verbindung via SSH (konfigurierbar im Script)
**Anpassung des Scripts:**
```bash
# deploy-ripster.sh
REMOTE_HOST="192.168.1.100"
REMOTE_USER="michael"
REMOTE_PATH="/home/michael/ripster"
```
+42 -117
View File
@@ -4,42 +4,44 @@
## Empfohlene Architektur
```text
Client
-> nginx (Reverse Proxy + statisches Frontend)
-> Backend API/WebSocket (Node.js, Port 3001)
```
Internet / Heimnetz
↓
nginx (Reverse Proxy)
↓
┌────┴────┐
│ │
Backend Frontend
:3001 (statische Dateien)
```
Wichtig: Das Backend serviert im aktuellen Stand keine `frontend/dist`-Dateien automatisch.
---
## systemd-Service
Für ein dauerhaftes Betreiben als systemd-Service:
## 1) Frontend builden
```bash
sudo nano /etc/systemd/system/ripster.service
cd frontend
npm install
npm run build
```
Artefakte liegen in `frontend/dist/`.
---
## 2) Backend als systemd-Service
Beispiel `/etc/systemd/system/ripster-backend.service`:
```ini
[Unit]
Description=Ripster - Disc Ripping Service
Description=Ripster Backend
After=network.target
[Service]
Type=simple
User=michael
WorkingDirectory=/home/michael/ripster
ExecStart=/bin/bash /home/michael/ripster/start.sh
ExecStop=/bin/bash /home/michael/ripster/kill.sh
User=ripster
WorkingDirectory=/opt/ripster/backend
ExecStart=/usr/bin/env node src/index.js
Restart=on-failure
RestartSec=10s
# Umgebungsvariablen
RestartSec=5
Environment=NODE_ENV=production
Environment=PORT=3001
Environment=LOG_LEVEL=info
@@ -48,61 +50,40 @@ Environment=LOG_LEVEL=info
WantedBy=multi-user.target
```
Aktivieren:
```bash
# Service aktivieren und starten
sudo systemctl daemon-reload
sudo systemctl enable ripster
sudo systemctl start ripster
# Status prüfen
sudo systemctl status ripster
# Logs anzeigen
journalctl -u ripster -f
sudo systemctl enable --now ripster-backend
sudo systemctl status ripster-backend
```
---
## Frontend-Build
## 3) nginx konfigurieren
Für Produktion das Frontend bauen:
```bash
cd frontend
npm run build
```
Die statischen Dateien landen in `frontend/dist/`.
---
## nginx-Konfiguration
Beispiel `/etc/nginx/sites-available/ripster`:
```nginx
# /etc/nginx/sites-available/ripster
server {
listen 80;
server_name ripster.local;
# Statisches Frontend
root /home/michael/ripster/frontend/dist;
root /opt/ripster/frontend/dist;
index index.html;
# SPA Fallback (React Router)
location / {
try_files $uri $uri/ /index.html;
}
# API-Proxy zum Backend
location /api/ {
proxy_pass http://localhost:3001;
proxy_pass http://127.0.0.1:3001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# WebSocket-Proxy
location /ws {
proxy_pass http://localhost:3001;
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
@@ -111,83 +92,27 @@ server {
}
```
Aktivieren:
```bash
sudo ln -s /etc/nginx/sites-available/ripster /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo nginx -t
sudo systemctl reload nginx
```
---
## Nur-Backend-Produktion (ohne nginx)
Falls kein Reverse Proxy gewünscht ist, kann das Backend die Frontend-Dateien direkt ausliefern:
```bash
# Frontend bauen
cd frontend && npm run build
# Backend startet und serviert frontend/dist/
cd backend && NODE_ENV=production npm start
```
Das Backend ist so konfiguriert, dass es im Produktionsmodus die `frontend/dist/`-Dateien als statische Assets ausliefert.
---
## Datenbank-Backup
```bash
# Datenbank sichern
cp backend/data/ripster.db backend/data/ripster.db.backup.$(date +%Y%m%d)
# Oder mit SQLite-eigenem Backup-Befehl
sqlite3 backend/data/ripster.db ".backup '/mnt/backup/ripster.db'"
```
!!! tip "Automatisches Backup"
Cron-Job für tägliches Backup:
```cron
0 3 * * * sqlite3 /home/michael/ripster/backend/data/ripster.db ".backup '/mnt/backup/ripster-$(date +\%Y\%m\%d).db'"
```
---
## Log-Rotation
Ripster rotiert Logs automatisch täglich. Falls zusätzlich systemd-Journal-Rotation gewünscht ist:
```bash
# /etc/logrotate.d/ripster
/home/michael/ripster/backend/logs/*.log {
daily
rotate 14
compress
missingok
notifempty
}
sqlite3 /opt/ripster/backend/data/ripster.db \
".backup '/var/backups/ripster-$(date +%Y%m%d).db'"
```
---
## Sicherheitshinweise
## Sicherheit
!!! warning "Heimnetz-Einsatz"
Ripster ist für den Einsatz im **lokalen Heimnetz** konzipiert und enthält **keine Authentifizierung**. Stelle sicher, dass der Dienst nicht öffentlich erreichbar ist.
Falls öffentlicher Zugang benötigt wird:
1. **Basic Auth** via nginx:
```bash
sudo htpasswd -c /etc/nginx/.htpasswd michael
```
```nginx
location / {
auth_basic "Ripster";
auth_basic_user_file /etc/nginx/.htpasswd;
# ...
}
```
2. **VPN-Zugang** (empfohlen): Zugriff nur über WireGuard/OpenVPN
3. **SSL/TLS**: Let's Encrypt mit certbot für HTTPS
- Ripster hat keine eingebaute Authentifizierung.
- Für externen Zugriff mindestens Basic Auth + TLS + Netzwerksegmentierung/VPN einsetzen.
- Secrets nicht ins Repo committen (`.env`, Settings-Felder).
+55 -74
View File
@@ -1,118 +1,99 @@
# Konfiguration
Alle Einstellungen werden über die Web-Oberfläche unter **Einstellungen** verwaltet und in der SQLite-Datenbank gespeichert.
Die Hauptkonfiguration erfolgt über die UI (`Settings`) und wird in SQLite gespeichert.
---
## Pflichteinstellungen
## Pflichteinstellungen vor dem ersten Rip
Diese Einstellungen müssen vor dem ersten Rip konfiguriert werden:
### Pfade
### 1) Pfade
| Einstellung | Beschreibung | Beispiel |
|------------|-------------|---------|
| `raw_dir` | Verzeichnis für rohe MKV-Dateien | `/mnt/nas/raw` |
| `movie_dir` | Ausgabeverzeichnis für kodierte Filme | `/mnt/nas/movies` |
| `log_dir` | Verzeichnis für Log-Dateien | `/var/log/ripster` |
| `raw_dir` | Basisverzeichnis für RAW-Rips | `/mnt/ripster/raw` |
| `movie_dir` | Basisverzeichnis für finale Encodes | `/mnt/ripster/movies` |
| `log_dir` | Verzeichnis für Prozess-/Backend-Logs | `/mnt/ripster/logs` |
!!! warning "Berechtigungen"
Der Ripster-Prozess benötigt **Schreibrechte** auf alle konfigurierten Verzeichnisse.
Optional profilspezifisch:
```bash
# Verzeichnisse erstellen und Berechtigungen setzen
sudo mkdir -p /mnt/nas/{raw,movies}
sudo chown $USER:$USER /mnt/nas/{raw,movies}
```
- `raw_dir_bluray`, `raw_dir_dvd`, `raw_dir_other`
- `movie_dir_bluray`, `movie_dir_dvd`, `movie_dir_other`
### OMDb API
### 2) Tools
| Einstellung | Standard |
|------------|---------|
| `makemkv_command` | `makemkvcon` |
| `handbrake_command` | `HandBrakeCLI` |
| `mediainfo_command` | `mediainfo` |
### 3) OMDb
| Einstellung | Beschreibung |
|------------|-------------|
| `omdb_api_key` | API-Key von omdbapi.com |
| `omdb_default_type` | Standard-Suchtyp: `movie` oder `series` |
| `omdb_default_type` | `movie`, `series`, `episode` |
---
## Tool-Konfiguration
## Encode-Konfiguration (wichtig)
| Einstellung | Standard | Beschreibung |
|------------|---------|-------------|
| `makemkv_command` | `makemkvcon` | Pfad oder Befehl für MakeMKV |
| `handbrake_command` | `HandBrakeCLI` | Pfad oder Befehl für HandBrake |
| `mediainfo_command` | `mediainfo` | Pfad oder Befehl für MediaInfo |
Ripster arbeitet profilspezifisch, typischerweise über:
!!! tip "Absolute Pfade"
Falls die Tools nicht im `PATH` sind, verwende absolute Pfade:
```
/usr/local/bin/HandBrakeCLI
```
- Blu-ray: `handbrake_preset_bluray`, `handbrake_extra_args_bluray`, `output_extension_bluray`, `filename_template_bluray`
- DVD: `handbrake_preset_dvd`, `handbrake_extra_args_dvd`, `output_extension_dvd`, `filename_template_dvd`
---
### Template-Platzhalter
## Encoding-Konfiguration
Verfügbar in `filename_template_*` und `output_folder_template_*`:
| Einstellung | Standard | Beschreibung |
|------------|---------|-------------|
| `handbrake_preset` | `H.265 MKV 1080p30` | HandBrake-Preset-Name |
| `handbrake_extra_args` | _(leer)_ | Zusätzliche HandBrake-Argumente |
| `output_extension` | `mkv` | Dateiendung der Ausgabedatei |
| `filename_template` | `{title} ({year})` | Template für Dateinamen |
- `${title}`
- `${year}`
- `${imdbId}`
### Dateiname-Template
Beispiel:
Das Template unterstützt 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})
→ Inception (2010).mkv
```text
${title} (${year})
-> Inception (2010).mkv
```
---
## Laufwerk-Konfiguration
## MakeMKV-spezifisch
| Einstellung | Standard | Beschreibung |
|------------|---------|-------------|
| `drive_mode` | `auto` | `auto` (automatisch erkennen) oder `explicit` (festes Gerät) |
| `drive_device` | `/dev/sr0` | Geräte-Pfad (nur bei `explicit`) |
| `disc_poll_interval_ms` | `4000` | Polling-Intervall in Millisekunden |
| Einstellung | Standard | Hinweis |
|------------|---------|--------|
| `makemkv_min_length_minutes` | `60` | Kandidaten-Filter |
| `makemkv_rip_mode_bluray` | `backup` | `mkv` oder `backup` |
| `makemkv_rip_mode_dvd` | `mkv` | `mkv` oder `backup` |
| `makemkv_registration_key` | leer | optional, wird via `makemkvcon reg` gesetzt |
---
## MakeMKV-Konfiguration
## Monitoring & Queue
| Einstellung | Standard | Beschreibung |
|------------|---------|-------------|
| `makemkv_min_length_minutes` | `15` | Mindestlänge für Titel in Minuten |
| `makemkv_backup_mode` | `false` | Backup-Modus statt MKV-Modus |
!!! info "Backup-Modus"
Im Backup-Modus erstellt MakeMKV eine vollständige Kopie der Disc (inkl. Menüs). Der Standardmodus erstellt direkt MKV-Dateien.
| Einstellung | Standard |
|------------|---------|
| `hardware_monitoring_enabled` | `true` |
| `hardware_monitoring_interval_ms` | `5000` |
| `pipeline_max_parallel_jobs` | `1` |
---
## Benachrichtigungen (PushOver)
## PushOver (optional)
| Einstellung | Beschreibung |
|------------|-------------|
| `pushover_user_key` | Dein PushOver User-Key |
| `pushover_api_token` | API-Token deiner PushOver-App |
Basis:
Nach der Eingabe kann die Verbindung mit dem **Test-Button** geprüft werden.
- `pushover_enabled`
- `pushover_token`
- `pushover_user`
Zusätzlich pro Event ein/aus (z. B. `pushover_notify_job_finished`).
---
## Vollständige Einstellungsreferenz
## Verwandte Doku
Eine vollständige Liste aller Einstellungen mit Typen, Validierung und Standardwerten findest du unter:
[:octicons-arrow-right-24: Einstellungsreferenz](../configuration/settings-reference.md)
- [Einstellungsreferenz](../configuration/settings-reference.md)
- [Umgebungsvariablen](../configuration/environment.md)
+36 -73
View File
@@ -11,50 +11,46 @@ cd ripster
---
## Automatischer Start
Ripster enthält ein `start.sh`-Skript, das alle Abhängigkeiten installiert und Backend + Frontend gleichzeitig startet:
## Dev-Start (empfohlen)
```bash
./start.sh
```
Das Skript führt automatisch folgende Schritte durch:
`start.sh`:
1. **Node.js-Versionscheck** – prüft ob >= 20.19.0 verfügbar ist (mit nvm/npx-Fallback)
2. **Abhängigkeiten installieren** – `npm install` für Root, Backend und Frontend
3. **Dienste starten** – Backend und Frontend werden parallel gestartet
1. prüft Node-Version (`>= 20.19.0`)
2. installiert Dependencies (Root/Backend/Frontend)
3. startet Backend + Frontend parallel
!!! success "Erfolgreich gestartet"
- Backend läuft auf `http://localhost:3001`
- Frontend läuft auf `http://localhost:5173`
Danach:
- Backend: `http://localhost:3001`
- Frontend: `http://localhost:5173`
Stoppen: mit `Ctrl+C` im laufenden Terminal.
---
## Manuelle Installation
Falls du mehr Kontrolle benötigst:
## Manuell starten
```bash
# Root-Abhängigkeiten
npm install
npm --prefix backend install
npm --prefix frontend install
npm run dev
```
# Backend-Abhängigkeiten
cd backend && npm install && cd ..
Oder getrennt:
# Frontend-Abhängigkeiten
cd frontend && npm install && cd ..
# Backend starten (Terminal 1)
cd backend && npm run dev
# Frontend starten (Terminal 2)
cd frontend && npm run dev
```bash
npm run dev:backend
npm run dev:frontend
```
---
## Umgebungsvariablen konfigurieren
## Optional: .env-Dateien anlegen
### Backend
@@ -62,13 +58,13 @@ cd frontend && npm run dev
cp backend/.env.example backend/.env
```
Bearbeite `backend/.env`:
Beispiel:
```env
PORT=3001
DB_PATH=./data/ripster.db
CORS_ORIGIN=http://localhost:5173
LOG_DIR=./logs
CORS_ORIGIN=http://localhost:5173
LOG_LEVEL=info
```
@@ -78,63 +74,30 @@ LOG_LEVEL=info
cp frontend/.env.example frontend/.env
```
Bearbeite `frontend/.env`:
Beispiel:
```env
VITE_API_BASE=http://localhost:3001
VITE_WS_URL=ws://localhost:3001
```
!!! tip "Alle Umgebungsvariablen"
Eine vollständige Übersicht aller Umgebungsvariablen findest du unter [Umgebungsvariablen](../configuration/environment.md).
---
## Datenbank initialisieren
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/
└── ripster.db ← Wird automatisch angelegt
VITE_API_BASE=/api
# optional:
# VITE_WS_URL=ws://localhost:3001/ws
```
---
## Stoppen
## Datenbank
```bash
./kill.sh
SQLite wird automatisch beim Backend-Start initialisiert:
```text
backend/data/ripster.db
```
Das Skript beendet Backend- und Frontend-Prozesse graceful.
---
## Verzeichnisstruktur nach Installation
```
ripster/
├── backend/
│ ├── data/ ← SQLite-Datenbank (nach erstem Start)
│ ├── logs/ ← Log-Dateien
│ ├── node_modules/ ← Backend-Abhängigkeiten
│ └── .env ← Backend-Konfiguration
├── frontend/
│ ├── node_modules/ ← Frontend-Abhängigkeiten
│ ├── dist/ ← Production-Build (nach npm run build)
│ └── .env ← Frontend-Konfiguration
└── node_modules/ ← Root-Abhängigkeiten (concurrently etc.)
```
Schema-Quelle: `db/schema.sql`
---
## Nächste Schritte
Nach erfolgreicher Installation:
1. Öffne [http://localhost:5173](http://localhost:5173)
2. Navigiere zu **Einstellungen**
3. Konfiguriere Pfade, API-Keys und Encoding-Presets
[:octicons-arrow-right-24: Zur Konfiguration](configuration.md)
1. Browser öffnen: `http://localhost:5173`
2. In `Settings` Pfade/Tools/API-Keys prüfen
3. Erste Disc einlegen und Workflow starten
-9
View File
@@ -145,15 +145,6 @@ Für mobile Push-Benachrichtigungen bei Fertigstellung oder Fehlern:
- App kaufen auf [pushover.net](https://pushover.net) (~5 USD einmalig)
- **User Key** und **API Token** notieren
### SSH-Zugang (Deployment)
Für Remote-Deployment via `deploy-ripster.sh`:
```bash
# sshpass installieren
sudo apt-get install sshpass
```
---
## Checkliste
+92 -389
View File
@@ -1,411 +1,114 @@
# Schnellstart – Vollständiger Workflow
# Schnellstart – Erster kompletter Job
Nach der [Installation](installation.md) und [Konfiguration](configuration.md) führt diese Seite Schritt für Schritt durch den ersten Rip – mit allen Details aus dem Code.
Diese Seite führt durch den typischen ersten Lauf.
---
## Übersicht: Pipeline-Ablauf
<div class="pipeline-steps">
<div class="pipeline-step">
<div class="pipeline-step-badge step-idle">●</div>
<div class="pipeline-step-label">IDLE</div>
<div class="pipeline-step-sub">Warten</div>
</div>
<div class="pipeline-step">
<div class="pipeline-step-badge step-idle">1</div>
<div class="pipeline-step-label">DISC_DETECTED</div>
<div class="pipeline-step-sub">Disc erkannt</div>
</div>
<div class="pipeline-step">
<div class="pipeline-step-badge step-running">2</div>
<div class="pipeline-step-label">METADATA_SELECTION</div>
<div class="pipeline-step-sub">OMDb &amp; Dialog</div>
</div>
<div class="pipeline-step">
<div class="pipeline-step-badge step-wait">⚠</div>
<div class="pipeline-step-label">WAITING_FOR_USER_DECISION</div>
<div class="pipeline-step-sub">Playlist wählen<br><em>(nur bei Obfusk.)</em></div>
</div>
<div class="pipeline-step">
<div class="pipeline-step-badge step-user">3</div>
<div class="pipeline-step-label">READY_TO_START</div>
<div class="pipeline-step-sub">Bereit</div>
</div>
<div class="pipeline-step">
<div class="pipeline-step-badge step-running">4</div>
<div class="pipeline-step-label">RIPPING</div>
<div class="pipeline-step-sub">MakeMKV</div>
</div>
<div class="pipeline-step">
<div class="pipeline-step-badge step-running">5</div>
<div class="pipeline-step-label">MEDIAINFO_CHECK</div>
<div class="pipeline-step-sub">HandBrake-Scan</div>
</div>
<div class="pipeline-step">
<div class="pipeline-step-badge step-user">6</div>
<div class="pipeline-step-label">READY_TO_ENCODE</div>
<div class="pipeline-step-sub">Track-Review</div>
</div>
<div class="pipeline-step">
<div class="pipeline-step-badge step-encode">7</div>
<div class="pipeline-step-label">ENCODING</div>
<div class="pipeline-step-sub">HandBrake</div>
</div>
<div class="pipeline-step">
<div class="pipeline-step-badge step-encode">8*</div>
<div class="pipeline-step-label">POST-ENCODE</div>
<div class="pipeline-step-sub">Skripte<br><em>(innerhalb ENCODING)</em></div>
</div>
<div class="pipeline-step">
<div class="pipeline-step-badge step-done">✓</div>
<div class="pipeline-step-label">FINISHED</div>
<div class="pipeline-step-sub">Fertig</div>
</div>
</div>
**Legende:** <span style="color:#546e7a">● Warten</span> &nbsp;|&nbsp; <span style="color:#1565c0">■ Läuft automatisch</span> &nbsp;|&nbsp; <span style="color:#3949ab">■ Benutzeraktion</span> &nbsp;|&nbsp; <span style="color:#e65100">⚠ Optional</span> &nbsp;|&nbsp; <span style="color:#6a1b9a">■ Encodierung</span> &nbsp;|&nbsp; <span style="color:#2e7d32">✓ Fertig</span>
??? note "Vollständiges Zustandsdiagramm (inkl. Fehler- &amp; Alternativpfade)"
<div class="pipeline-diagram">
```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
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| ENC[ENCODING]
ENC -->|inkl. Post-Skripte| FIN([FINISHED])
ENC -->|Fehler| ERR
ERR([ERROR]) -->|Retry / Cancel| IDLE
style FIN fill:#e8f5e9,stroke:#66bb6a,color:#2e7d32
style ERR fill:#ffebee,stroke:#ef5350,color:#c62828
style WUD fill:#fff8e1,stroke:#ffa726,color:#e65100
style ENC fill:#f3e5f5,stroke:#ab47bc,color:#6a1b9a
```
</div>
---
## Schritt 1 – Ripster starten
## 1) Starten
```bash
cd ripster
./start.sh
```
Öffne [http://localhost:5173](http://localhost:5173) im Browser. Das Dashboard zeigt `IDLE`.
Öffne `http://localhost:5173`.
---
## Schritt 2 – Disc einlegen → `DISC_DETECTED`
## 2) Disc einlegen
Lege eine DVD oder Blu-ray ein. Der `diskDetectionService` pollt das Laufwerk alle `disc_poll_interval_ms` Millisekunden (Standard: 4 Sekunden).
Pipeline wechselt auf `DISC_DETECTED`.
**Was passiert im Code:**
- `diskDetectionService` emittiert `discInserted` mit Geräteinformationen
- `pipelineService.onDiscInserted()` wird aufgerufen
- Dashboard-Status-Badge zeigt **"Medium erkannt"**
- Status-Text zeigt **"Neue Disk erkannt"**
- Der **"Analyse starten"**-Button wird aktiv
!!! tip "Manuelle Auslösung"
Falls die automatische Erkennung nicht greift:
```bash
curl -X POST http://localhost:3001/api/pipeline/analyze
```
---
## Schritt 3 – Analyse starten → `METADATA_SELECTION`
Klicke auf **"Analyse starten"**.
**Was passiert im Code:**
1. Ein neuer Job-Datensatz wird in der Datenbank angelegt (`status: METADATA_SELECTION`)
2. Ripster versucht, den Titel automatisch aus dem Disc-Label/Modell zu ermitteln
3. Mit diesem erkannten Titel wird sofort eine **OMDb-Suche** ausgelöst
4. Der `MetadataSelectionDialog` öffnet sich im Frontend mit den vorgeladenen Suchergebnissen
**Erkannter Titel:** Der Disc-Label (z. B. `INCEPTION`) wird als Suchbegriff verwendet. Falls kein Label vorhanden, bleibt das Suchfeld leer.
---
## Schritt 4 – Metadaten auswählen (`MetadataSelectionDialog`)
Der Dialog zeigt vorgeladene OMDb-Suchergebnisse. Du kannst:
### 4a) OMDb-Suchergebnis wählen
```
┌─────────────────────────────────────────────────┐
│ Suche: [Inception ] 🔍 │
├─────────────────────────────────────────────────┤
│ ▶ Inception (2010) · Movie · tt1375666 │
│ Inception: ... · Series · ... │
├─────────────────────────────────────────────────┤
│ [Auswahl übernehmen] │
└─────────────────────────────────────────────────┘
```
- Suche durch Titel anpassen und Enter drücken
- Typ-Filter: `movie` / `series` umschalten möglich
- Einen Eintrag anklicken, dann **"Auswahl übernehmen"**
### 4b) Manuelle Eingabe (ohne OMDb)
Falls kein passendes Ergebnis gefunden wird:
- Titel, Jahr und IMDb-ID manuell eingeben
- OMDb-Poster wird übersprungen
**Was passiert nach Bestätigung:**
Ripster ruft `pipelineService.selectMetadata()` auf und startet den nächsten Schritt automatisch:
- Job wird auf `READY_TO_START` gesetzt (kurzer Übergangszustand)
- Falls bereits RAW vorhanden: direkter Sprung zu `MEDIAINFO_CHECK`
- Falls kein RAW vorhanden: automatischer Start von `RIPPING`
- Wenn bereits andere Jobs laufen, landet der Start stattdessen in der Queue
---
## Schritt 5 – Optional: Playlist-Auswahl → `WAITING_FOR_USER_DECISION`
Dieser Zustand erscheint nur bei mehrdeutigen Blu-ray-Playlists (typisch nach RAW-Analyse im Backup-Modus).
Der **Playlist-Auswahl-Dialog** erscheint **zusätzlich** (nach dem Metadaten-Dialog):
```
┌───────────────────────────────────────────────────────────────┐
│ Playlist-Auswahl │
│ Es wurden mehrere Titel mit ähnlicher Laufzeit gefunden. │
│ Bitte wähle die korrekte Playlist: │
├───────────┬──────────┬────────┬──────────────────────────────┤
│ 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) │
└───────────┴──────────┴────────┴──────────────────────────────┘
847 Playlists insgesamt · 3 relevante Kandidaten (≥ 15 min)
Empfehlung: 00800 (vorausgewählt)
[Playlist übernehmen]
```
- Die empfohlene Playlist ist **vorausgewählt** (Checkbox)
- Score und Bewertungslabel helfen bei der Entscheidung
- Nach **"Playlist übernehmen"** setzt Ripster automatisch fort:
- mit vorhandenem RAW in `MEDIAINFO_CHECK`
- ohne RAW über `READY_TO_START` weiter Richtung `RIPPING`
!!! info "Scoring-Details"
Wie die Scores berechnet werden, erklärt die [Playlist-Analyse](../pipeline/playlist-analysis.md)-Seite.
---
## Schritt 6 – Ripping → `RIPPING`
**Vorher prüft Ripster:** Existiert bereits eine Raw-Datei für diesen Job?
- **Ja, Raw-Datei vorhanden** → Direkt zu Schritt 7 (Track-Review), kein erneutes Ripping
- **Nein** → MakeMKV-Ripping startet
Im Standardfall startet Ripster diesen Schritt automatisch nach der Metadaten-Auswahl.
Der Button **"Job starten"** ist hauptsächlich für Sonderfälle sichtbar (z. B. Fallback/Queue).
**Was MakeMKV ausführt (MKV-Modus):**
Falls nötig manuell neu scannen:
```bash
makemkvcon mkv disc:0 all /mnt/raw/Inception-2010/ \
--minlength=900 -r
curl -X POST http://localhost:3001/api/pipeline/rescan-disc
```
**Was MakeMKV ausführt (Backup-Modus):**
---
## 3) Analyse starten
Klicke im Dashboard auf `Analyse starten`.
Intern:
- Job wird angelegt
- MakeMKV-Analyse läuft (`ANALYZING`)
- UI wechselt in Metadatenauswahl (`METADATA_SELECTION`)
---
## 4) Metadaten bestätigen
Im Dialog:
- OMDb-Ergebnis wählen oder manuell eintragen
- bei Playlist-Abfrage ggf. `selectedPlaylist` wählen
Nach Bestätigung startet Ripster automatisch weiter.
---
## 5) Pipeline-Pfade
Abhängig von Job/RAW-Situation:
- **kein RAW vorhanden** -> `RIPPING`
- **RAW vorhanden** -> `MEDIAINFO_CHECK`
- **mehrdeutige Playlist** -> `WAITING_FOR_USER_DECISION`
Wenn Parallel-Limit erreicht ist, wird der Job in die Queue eingereiht.
---
## 6) Review (`READY_TO_ENCODE`)
Im Review-Panel:
- Titel auswählen (falls mehrere)
- Audio-/Subtitle-Tracks auswählen
- optional User-Preset anwenden
- optional Pre-/Post-Skripte und Ketten hinzufügen
Mit `Encoding starten` wird `confirm-encode` + Start ausgelöst.
---
## 7) Encoding (`ENCODING`)
Während Encoding:
- Live-Fortschritt/ETA über WebSocket
- Pre-Encode-Ausführungen laufen vor HandBrake
- Post-Encode-Ausführungen laufen nach HandBrake
Wichtig:
- Pre-Encode-Fehler -> Job endet in `ERROR`
- Post-Encode-Fehler -> Job kann `FINISHED` bleiben, aber mit Fehlerhinweis im Status/Log
---
## 8) Abschluss (`FINISHED`)
Ergebnis:
- Ausgabe in `movie_dir` (ggf. profilspezifisch)
- Job in Historie sichtbar
- Logs im konfigurierten `log_dir`
---
## Nützliche API-Shortcuts
```bash
makemkvcon backup disc:0 /mnt/raw/Inception-2010-backup/ \
--decrypt -r
# Pipeline-Snapshot
curl http://localhost:3001/api/pipeline/state
# Queue-Snapshot
curl http://localhost:3001/api/pipeline/queue
# Jobs
curl http://localhost:3001/api/history
```
**Live-Fortschritt** wird aus der MakeMKV-Ausgabe geparst:
```
PRGV:2048,0,65536 → Fortschritt wird berechnet und per WebSocket gesendet
PRGT:5011,0,"Sichern..." → Aktueller Task-Name
```
**Typische Dauer:**
- DVD: 20–45 Minuten
- Blu-ray: 45–120 Minuten
---
## Schritt 7 – Track-Review → `READY_TO_ENCODE`
Nach dem Ripping, nach Playlist-Übernahme oder direkt bei vorhandenem RAW startet der **HandBrake-Scan**:
```bash
HandBrakeCLI --scan -i <quelle> -t 0
```
Dieser Scan liest alle Tracks aus ohne zu encodieren. Ripster baut daraus den Encode-Plan mit automatischer Vorauswahl:
**Status: `MEDIAINFO_CHECK`** – läuft automatisch, kein Benutzereingriff
Danach öffnet sich das **Encode-Review-Panel** (`READY_TO_ENCODE`):
```
┌─────────────────────────────────────────────────────────────────┐
│ Encode-Review │
│ Titel: Disc Title 1 · Laufzeit: 2:28:05 · 28 Kapitel │
├─────────────────────────────────────────────────────────────────┤
│ Audio-Spuren │
├──────┬─────────────────────────────┬───────────────────────────┤
│ ☑ │ Track 1: English (AC3, 5.1) │ Copy (ac3) │
│ ☑ │ Track 2: Deutsch (DTS, 5.1) │ Fallback Transcode (av_aac)│
│ ☐ │ Track 3: Français (AC3, 2.0) │ Nicht übernommen │
├──────┴─────────────────────────────┴───────────────────────────┤
│ Untertitel-Spuren │
├──────┬─────────────────────────────┬────────┬──────┬──────────┤
│ ☑ │ Track 1: Deutsch │ Einbr.☐ │Forc.☐│Default☑ │
│ ☐ │ Track 2: English │ Einbr.☐ │Forc.☐│Default☐ │
├──────┴─────────────────────────────┴────────┴──────┴──────────┤
│ [Encoding starten] │
└─────────────────────────────────────────────────────────────────┘
```
### Audio-Track-Aktionen verstehen
| Symbol/Text | Bedeutung |
|------------|-----------|
| `Copy (ac3)` | Track wird **verlustfrei** direkt übernommen |
| `Copy (truehd)` | TrueHD-Track wird direkt übernommen |
| `Transcode (av_aac)` | Track wird zu AAC umgewandelt |
| `Fallback Transcode (av_aac)` | Copy nicht möglich → automatisch zu AAC |
| `Preset-Default (HandBrake)` | HandBrake-Preset entscheidet |
| `Nicht übernommen` | Track ist nicht ausgewählt |
### Untertitel-Flags
| Flag | Bedeutung |
|------|-----------|
| **Einbrennen** | Untertitel werden fest ins Video gebrannt (nur ein Track möglich) |
| **Forced** | Nur erzwungene Untertitel-Einblendungen übernehmen |
| **Default** | Diese Spur wird beim Abspielen automatisch aktiviert |
### Vorauswahl-Regeln
Die Tracks mit `☑` wurden nach der Regel aus den Einstellungen automatisch vorausgewählt (`selectedByRule: true`). Die Auswahl kann frei geändert werden.
Klicke **"Encoding starten"** (bzw. im Pre-Rip-Modus **"Backup + Encoding starten"**), um fortzufahren.
Falls die Auswahl noch nicht bestätigt wurde, übernimmt das Frontend die Bestätigung automatisch beim Start.
---
## Schritt 8 – Encoding → `ENCODING`
HandBrake startet mit dem finalisierten Plan:
```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
```
**Live-Fortschritt** wird aus HandBrake-stderr geparst:
```
Encoding: task 1 of 1, 73.50 % (45.23 fps, avg 44.12 fps, ETA 00h12m34s)
```
Das Dashboard zeigt:
- Fortschrittsbalken (0–100 %)
- Aktuelle Encoding-Geschwindigkeit (FPS)
- Geschätzte Restzeit (ETA)
**Typische Dauer (abhängig von CPU/GPU und Preset):**
- Schnelles Preset (`fast`): 0.5× Echtzeit
- Standard-Preset: 1–3× Echtzeit
- Langsames Preset (`slow`): 5–10× Echtzeit
---
## Schritt 9 – Fertig! → `FINISHED`
```
/mnt/nas/movies/
└── Inception (2010).mkv ✓ Encodierung abgeschlossen
```
- Job-Status in der Datenbank: `FINISHED`
- PushOver-Benachrichtigung (falls konfiguriert)
- Eintrag in der [History](http://localhost:5173/history) mit vollständigen Logs
---
## Fehlerbehandlung
### Job im Status `ERROR`
1. **Dashboard**: Details-Button → Log-Ausgabe prüfen
2. **Retry**: Job vom Fehlerzustand neu starten (behält Metadaten)
3. **History**: Vollständige Logs und Fehlerdetails
### Häufige Fehlerursachen
| Fehler | Ursache | Lösung |
|-------|---------|--------|
| MakeMKV: Lizenzfehler | Abgelaufene Beta-Lizenz | Neue Lizenz im [MakeMKV-Forum](https://www.makemkv.com/forum/viewtopic.php?t=1053) |
| HandBrake: Preset nicht gefunden | Preset-Name falsch | `HandBrakeCLI --preset-list` prüfen |
| Keine Disc erkannt | Laufwerk-Berechtigungen | `sudo chmod a+rw /dev/sr0` |
| Falsches Video (zerstückelt) | Falsche Playlist | Job re-encodieren mit anderer Playlist |
| OMDb: Keine Ergebnisse | API-Key fehlt oder Titel nicht gefunden | Einstellungen prüfen; manuell eingeben |
---
## Kurzübersicht aller Schritte
| # | 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ählen | Start automatisch einplanen/auslösen |
| 4 | `READY_TO_START` | meist keine | Übergangszustand vor Auto-Start |
| 5 | `RIPPING` | Warten | MakeMKV rippt, Fortschritt streamen |
| 6 | `MEDIAINFO_CHECK` | Warten | HandBrake-Scan, Encode-Plan bauen |
| 7 | `WAITING_FOR_USER_DECISION` (optional) | Playlist manuell wählen | Auf Bestätigung warten |
| 8 | `READY_TO_ENCODE` | Tracks prüfen + "Encoding starten" | Auswahl übernehmen, Start auslösen |
| 9 | `ENCODING` | Warten | HandBrake encodiert, inkl. Post-Skripte |
| 10 | `FINISHED` | — | Datei fertig, Benachrichtigung senden |
+57 -283
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.
+7 -7
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>
+32 -184
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`
+43 -146
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.
+63 -305
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 20–45 min · Blu-ray 45–120 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
+38 -106
View File
@@ -1,137 +1,69 @@
# HandBrake
HandBrake encodiert die rohen MKV-Dateien in das gewünschte Format. Ripster nutzt `HandBrakeCLI`.
Ripster verwendet `HandBrakeCLI` für Scan und Encode.
---
## Verwendeter Befehl
## Verwendete Aufrufe
### Scan (Review-Aufbau)
```bash
HandBrakeCLI --scan --json -i <input> -t 0
```
### Encode (vereinfacht)
```bash
HandBrakeCLI \
--input "/mnt/raw/Film_t00.mkv" \
--output "/mnt/movies/Film (2010).mkv" \
--preset "H.265 MKV 1080p30" \
--audio 1,2 \
--aencoder copy:ac3,ffaac \
--subtitle 1 \
--subtitle-default 1
-i <input> \
-o <output> \
-t <titleId> \
-Z "<preset>" \
<extra-args> \
-a <audioTrackIds|none> \
-s <subtitleTrackIds|none>
```
Optional ergänzt Ripster:
- `--subtitle-burned=<id>`
- `--subtitle-default=<id>`
- `--subtitle-forced=<id>` oder `--subtitle-forced`
---
## Presets
## Presets auslesen
HandBrake verwendet **Presets** für vorkonfigurierte Encoding-Einstellungen.
### Empfohlene Presets
| Preset | Codec | Auflösung | Für |
|--------|-------|----------|-----|
| `H.265 MKV 1080p30` | HEVC/H.265 | 1080p | Beste Qualität/Größe |
| `H.265 MKV 720p30` | HEVC/H.265 | 720p | Kleinere Dateien |
| `H.264 MKV 1080p30` | AVC/H.264 | 1080p | Breiteste Kompatibilität |
| `HQ 1080p30 Surround` | HEVC/H.265 | 1080p | Hohe Qualität mit Surround |
### Alle Presets anzeigen
Ripster liest Presets mit:
```bash
HandBrakeCLI --preset-list
HandBrakeCLI -z
```
---
## Audio-Encoding
## Relevante Settings
### Copy-kompatible Codecs
HandBrake kann folgende Codecs direkt kopieren (kein Qualitätsverlust):
| Codec | `--aencoder` Wert |
|-------|-----------------|
| AC-3 | `copy:ac3` |
| AAC | `copy:aac` |
| MP3 | `copy:mp3` |
| TrueHD | `copy:truehd` |
| E-AC-3 | `copy:eac3` |
### Transcoding
Codecs die nicht kopiert werden können, werden zu AAC transcodiert:
| Original | Transcodiert zu |
|---------|----------------|
| DTS | AAC (`ffaac`) |
| DTS-HD | AAC (`ffaac`) |
---
## Extra-Argumente
Über die Einstellung `handbrake_extra_args` können beliebige HandBrake-Argumente hinzugefügt werden:
```
# Cropping deaktivieren
--crop 0:0:0:0
# Loose Anamorphic
--loose-anamorphic
# Bestimmte Qualität setzen
--quality 20
```
| Key | Bedeutung |
|-----|-----------|
| `handbrake_command` | CLI-Binary |
| `handbrake_preset_bluray` / `handbrake_preset_dvd` | profilspezifisches Preset |
| `handbrake_extra_args_bluray` / `handbrake_extra_args_dvd` | profilspezifische Zusatzargumente |
| `output_extension_bluray` / `output_extension_dvd` | Ausgabeformat |
| `handbrake_restart_delete_incomplete_output` | unvollständige Ausgabe bei Neustart löschen |
---
## Fortschritts-Parsing
Ripster parst die HandBrake-Ausgabe auf stderr für die Fortschrittsanzeige:
```
Encoding: task 1 of 1, 73.50 % (45.23 fps, avg 44.12 fps, ETA 00h12m34s)
```
`progressParsers.js` extrahiert:
- Prozentzahl
- Aktuelle FPS
- ETA
---
## Konfiguration in Ripster
| Einstellung | Beschreibung |
|------------|-------------|
| `handbrake_command` | Pfad/Befehl für `HandBrakeCLI` |
| `handbrake_preset` | Preset-Name |
| `handbrake_extra_args` | Zusätzliche CLI-Argumente |
| `output_extension` | Dateiendung der Ausgabe |
Ripster parst HandBrake-Stderr (Prozent/ETA/Detail) und sendet WebSocket-Progress (`PIPELINE_PROGRESS`).
---
## Troubleshooting
### HandBrake findet Preset nicht
- Preset nicht gefunden: Preset-Namen mit `HandBrakeCLI -z` prüfen
- sehr langsames Encoding: Preset/Extra-Args prüfen (z. B. `--encoder-preset`)
```bash
# Preset-Liste anzeigen
HandBrakeCLI --preset-list 2>&1 | grep -i "h.265"
```
Preset-Namen sind case-sensitive!
### Encoding sehr langsam
```bash
# CPU-Encoding-Preset anpassen (schneller = schlechtere Qualität)
handbrake_extra_args = --encoder-preset fast
```
Verfügbare Presets: `ultrafast`, `superfast`, `veryfast`, `faster`, `fast`, `medium`, `slow`, `slower`, `veryslow`
### GPU-Encoding nutzen (NVIDIA)
```
handbrake_preset = H.265 NVENC 1080p
```
Erfordert HandBrake-Build mit NVENC-Unterstützung und NVIDIA-GPU.
Das Produktions-Installer-Script `install.sh` bietet eine Option zur Installation eines gebündelten HandBrakeCLI-Binaries mit NVDEC-Unterstützung (NVIDIA GPU-Dekodierung). Diese Option erscheint interaktiv während der Installation.
+31 -130
View File
@@ -1,160 +1,61 @@
# MakeMKV
MakeMKV analysiert und rippt DVDs und Blu-rays. Ripster nutzt `makemkvcon` (die CLI-Version).
Ripster nutzt `makemkvcon` für Disc-Analyse und Rip.
---
## Verwendete Befehle
## Verwendete Aufrufe
### Disc-Analyse
### Analyse
```bash
makemkvcon -r --cache=1 info disc:0
makemkvcon -r info <source>
```
Gibt alle Titel und Playlists der eingelegten Disc aus. Ripster parst diese Ausgabe um die verfügbaren Tracks und Playlists zu bestimmen.
`<source>` ist typischerweise:
**Parameter:**
- `-r` – Maschinen-lesbares Ausgabeformat
- `--cache=1` – Minimaler Disc-Cache
- `info disc:0` – Informationsabfrage für erstes Laufwerk
- `disc:<index>` (Auto-Modus)
- `dev:/dev/sr0` (explicit)
- `file:<path>` (Datei/Ordner-Analyse)
### MKV-Modus (Standard)
### Rip (MKV-Modus)
```bash
makemkvcon mkv disc:0 all /path/to/raw/ \
--minlength=900 \
-r
makemkvcon mkv <source> <title-or-all> <rawDir> [--minlength=...] [...extraArgs]
```
Erstellt MKV-Dateien aus allen Titeln, die länger als 15 Minuten sind.
**Parameter:**
- `mkv` – MKV-Ausgabemodus
- `disc:0` – Erstes Disc-Laufwerk
- `all` – Alle passenden Titel (nicht nur einen bestimmten)
- `--minlength=900` – Mindestlänge in Sekunden (entspricht 15 Minuten)
### Backup-Modus
### Rip (Backup-Modus)
```bash
makemkvcon backup disc:0 /path/to/raw/backup/ \
--decrypt \
-r
```
Erstellt ein vollständiges Disc-Backup mit Menüs.
**Parameter:**
- `backup` – Backup-Modus
- `--decrypt` – Verschlüsselung entfernen
---
## Ausgabeformat
MakeMKV gibt Fortschritt und Status in einem strukturierten Format aus:
```
PRGV:current,total,max → Fortschrittsbalken-Werte
PRGT:code,id,"Beschreibung" → Aktueller Task
PRGC:code,id,"Beschreibung" → Aktueller Sub-Task
MSG:code,flags,count,"Text" → Nachricht
```
Ripster's `progressParsers.js` parst diese Ausgabe für die Live-Fortschrittsanzeige.
---
## LibDriveIO-Modus (Pflicht)
!!! danger "Laufwerk muss im LibDriveIO-Modus betrieben werden"
MakeMKV greift auf Discs über **LibDriveIO** zu – eine Bibliothek, die direkt auf Rohdaten des Laufwerks zugreift und den Standard-OS-Treiber umgeht. Ohne diesen Modus kann MakeMKV verschlüsselte Blu-rays (insbesondere UHD) **nicht lesen**.
### Was ist LibDriveIO?
LibDriveIO ist MakeMKVs interne Treiberschicht für den direkten Laufwerkszugriff. Sie ermöglicht:
- Lesen von verschlüsselten Blu-ray-Sektoren (AACS, BD+, AACS2)
- Zugriff auf Disc-Strukturen, die über Standard-OS-APIs nicht erreichbar sind
- UHD-Blu-ray-Entschlüsselung ohne externe Bibliotheken
### Voraussetzungen für den LibDriveIO-Modus
Das Laufwerk muss **LibDriveIO-kompatibel** sein und entsprechend betrieben werden:
1. **Kompatibles Laufwerk** – Nicht alle Laufwerke unterstützen den Rohdatenzugriff. UHD-kompatible Laufwerke (z. B. LG, Pioneer bestimmter Firmware-Versionen) sind erforderlich.
2. **Laufwerk-Berechtigungen** – Der Prozess benötigt direkten Zugriff auf das Blockdevice:
```bash
sudo chmod a+rw /dev/sr0
# oder dauerhaft über udev-Regel
```
3. **Kein OS-seitiger Disc-Mount** – Das Laufwerk darf beim Ripping **nicht** durch das OS automatisch gemountet sein (AutoMount deaktivieren):
```bash
# Automount temporär deaktivieren (GNOME)
gsettings set org.gnome.desktop.media-handling automount false
```
### How-To: LibDriveIO einrichten
Die vollständige Anleitung zur Einrichtung und zu kompatiblen Laufwerken findet sich im offiziellen MakeMKV-Forum:
[:octicons-link-external-24: MakeMKV Forum – LibDriveIO How-To](https://www.makemkv.com/forum/viewtopic.php?t=18856){ .md-button }
!!! tip "Prüfen ob LibDriveIO aktiv ist"
In der MakeMKV-Ausgabe erscheint beim Laufwerkszugriff `LibDriveIO` statt `LibMMMBD`, wenn der direkte Modus aktiv ist.
---
## MakeMKV-Lizenz
MakeMKV ist **Beta-Software** und kostenlos für den persönlichen Gebrauch während der Beta-Phase. Eine Beta-Lizenz ist regelmäßig im [MakeMKV-Forum](https://www.makemkv.com/forum/viewtopic.php?t=1053) verfügbar.
Ohne gültige Lizenz können Blu-rays nicht entschlüsselt werden.
### Lizenz eintragen
Die Lizenz wird in den MakeMKV-Einstellungen eingetragen (GUI) oder direkt in:
```
~/.MakeMKV/settings.conf
```
```
app_Key = "XXXX-XXXX-XXXX-XXXX-XXXX"
makemkvcon backup <source> <rawDir> --decrypt
```
---
## Konfiguration in Ripster
## Registrierungsschlüssel (optional)
| Einstellung | Beschreibung |
|------------|-------------|
| `makemkv_command` | Pfad/Befehl für `makemkvcon` |
| `makemkv_min_length_minutes` | Mindest-Titellänge (Standard: 15 Min) |
| `makemkv_backup_mode` | Backup-Modus statt MKV |
---
## Troubleshooting
### MakeMKV erkennt Disc nicht
Wenn `makemkv_registration_key` gesetzt ist, führt Ripster vor Analyse/Rip aus:
```bash
# Laufwerk-Berechtigungen prüfen
ls -la /dev/sr0
sudo chmod a+rw /dev/sr0
# Oder Benutzer zur Gruppe cdrom hinzufügen
sudo usermod -a -G cdrom $USER
makemkvcon reg <key>
```
### Langer Analyseprozess
---
Blu-ray-Analyse kann bei Discs mit vielen Playlists 5+ Minuten dauern. Dies ist normal.
## Relevante Settings
### Fehlermeldung: "LibMMBD"
| Key | Bedeutung |
|-----|-----------|
| `makemkv_command` | CLI-Binary |
| `makemkv_source_index` | Source-Index im Auto-Modus |
| `makemkv_min_length_minutes` | Mindestlaufzeitfilter |
| `makemkv_rip_mode_bluray` / `makemkv_rip_mode_dvd` | `mkv` oder `backup` |
| `makemkv_analyze_extra_args_bluray` / `_dvd` | Zusatzargs Analyse |
| `makemkv_rip_extra_args_bluray` / `_dvd` | Zusatzargs Rip |
LibMMBD ist MakeMKVs interne Verschlüsselungsbibliothek. Bei Fehlern die MakeMKV-Version aktualisieren.
---
## Hinweise
- Blu-ray-Backups werden oft für robuste Playlist-Analyse genutzt.
- MakeMKV-Ausgaben werden geparst und als `makemkvInfo` im Job gespeichert.
+15 -86
View File
@@ -1,108 +1,37 @@
# MediaInfo
MediaInfo analysiert die Track-Struktur von Mediendateien. Ripster nutzt es nach dem Ripping um Audio- und Untertitelspuren zu identifizieren.
Ripster nutzt `mediainfo` zur JSON-Analyse von Medien-Dateien.
---
## Verwendeter Befehl
## Aufruf
```bash
mediainfo --Output=JSON /path/to/raw/film.mkv
mediainfo --Output=JSON <input>
```
Gibt vollständige Track-Informationen als JSON zurück.
Der Input ist typischerweise eine RAW-Datei oder ein vom Workflow gewählter Inputpfad.
---
## Ausgabe-Struktur
## Verwendung in Ripster
```json
{
"media": {
"track": [
{
"@type": "General",
"Duration": "8885.042",
"Format": "Matroska"
},
{
"@type": "Video",
"Format": "HEVC",
"Width": "1920",
"Height": "1080",
"FrameRate": "23.976"
},
{
"@type": "Audio",
"StreamOrder": "1",
"Format": "TrueHD",
"Channels": "8",
"Language": "en"
},
{
"@type": "Audio",
"StreamOrder": "2",
"Format": "AC-3",
"Channels": "6",
"Language": "de"
},
{
"@type": "Text",
"StreamOrder": "1",
"Format": "UTF-8",
"Language": "de"
}
]
}
}
```
- Track-/Codec-Metadaten für Review-Plan
- Fallback-Informationen in bestimmten Analysepfaden
- Persistenz als `mediainfoInfo` im Job
---
## Verarbeitung in Ripster
## Relevante Settings
`encodePlan.js` verarbeitet die MediaInfo-Ausgabe:
1. **Track-Extraktion**: Alle Audio- und Untertitel-Tracks werden extrahiert
2. **Sprach-Normalisierung**: Sprachcodes werden auf ISO 639-3 normalisiert
3. **Codec-Klassifizierung**: Bestimmt ob Codec kopiert oder transcodiert werden kann
4. **Track-Labels**: Benutzerfreundliche Bezeichnungen (z.B. "Deutsch (AC-3, 5.1)")
### Track-Label-Format
```
{Sprache} ({Format}, {Kanäle})
```
Beispiele:
- `Deutsch (AC-3, 5.1)`
- `English (TrueHD, 7.1)`
- `Français (AC-3, 2.0)`
---
## Konfiguration in Ripster
| Einstellung | Beschreibung |
|------------|-------------|
| `mediainfo_command` | Pfad/Befehl für `mediainfo` |
| Key | Bedeutung |
|-----|-----------|
| `mediainfo_command` | CLI-Binary |
| `mediainfo_extra_args_bluray` / `_dvd` | profilspezifische Zusatzargumente |
---
## Troubleshooting
### MediaInfo gibt kein JSON aus
```bash
# Version prüfen
mediainfo --Version
# JSON-Ausgabe testen
mediainfo --Output=JSON /path/to/test.mkv
```
MediaInfo >= 17.10 wird empfohlen.
### Sprache als "und" angezeigt
`und` steht für "undetermined" – die Sprache ist in der MKV-Datei nicht getaggt. Dies ist bei manchen Rips normal. Der Track wird trotzdem angezeigt und kann manuell ausgewählt werden.
- JSON-Test: `mediainfo --Output=JSON <datei>`
- unbekannte Sprache erscheint oft als `und` (undetermined)
@@ -65,6 +65,23 @@ function isBurnedSubtitleTrack(track) {
);
}
function isForcedOnlySubtitleTrack(track) {
const summary = `${track?.title || ''} ${track?.description || ''} ${track?.languageLabel || ''}`.toLowerCase();
return Boolean(
track?.forcedTrack
|| /forced only/.test(summary)
|| /nur erzwungen/.test(summary)
|| /\berzwungen\b/.test(summary)
);
}
function hasForcedSubtitleAvailable(track) {
const sourceTrackIds = normalizeTrackIdList(
Array.isArray(track?.forcedSourceTrackIds) ? track.forcedSourceTrackIds : []
);
return Boolean(track?.forcedAvailable || sourceTrackIds.length > 0);
}
function splitArgs(input) {
if (!input || typeof input !== 'string') {
return [];
@@ -601,6 +618,8 @@ function TrackList({
const displayAudioTitle = audioChannelLabel(track.channels);
const audioVariant = type === 'audio' ? extractAudioVariant(displayHint) : '';
const disabled = !allowSelection || (type === 'subtitle' && burned);
const forcedOnlyTrack = type === 'subtitle' ? isForcedOnlySubtitleTrack(track) : false;
const forcedAvailable = type === 'subtitle' ? hasForcedSubtitleAvailable(track) : false;
let displayText = `#${track.id} | ${displayLanguage} | ${displayCodec}`;
if (type === 'audio') {
@@ -616,6 +635,10 @@ function TrackList({
}
if (type === 'subtitle' && burned) {
displayText += ' | burned';
} else if (type === 'subtitle' && forcedOnlyTrack) {
displayText += ' | forced-only';
} else if (type === 'subtitle' && forcedAvailable) {
displayText += ' | forced verfügbar';
}
return (
@@ -1074,7 +1097,6 @@ export default function MediaInfoReviewPanel({
allowTrackSelection
&& allowTitleSelection
&& titleChecked
&& titleEligible
);
return (
@@ -1090,7 +1112,7 @@ export default function MediaInfoReviewPanel({
onSelectEncodeTitle(normalizeTitleId(title.id));
}}
readOnly={!allowTitleSelection}
disabled={!allowTitleSelection || !titleEligible}
disabled={!allowTitleSelection}
/>
<span>
#{title.id} | {title.fileName} | {formatDuration(title.durationMinutes)} | {formatBytes(title.sizeBytes)}
+25 -4
View File
@@ -24,6 +24,18 @@ function normalizePlaylistId(value) {
return match ? String(match[1]).padStart(5, '0') : null;
}
function formatDurationClock(seconds) {
const total = Number(seconds || 0);
if (!Number.isFinite(total) || total <= 0) {
return null;
}
const rounded = Math.max(0, Math.trunc(total));
const h = Math.floor(rounded / 3600);
const m = Math.floor((rounded % 3600) / 60);
const s = rounded % 60;
return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
}
function normalizeTrackId(value) {
const parsed = Number(value);
if (!Number.isFinite(parsed) || parsed <= 0) {
@@ -376,10 +388,18 @@ export default function PipelineStatusCard({
item?.structuralMetrics?.sequenceCoherence ?? item?.sequenceCoherence
);
const handBrakeTitleId = Number(item?.handBrakeTitleId);
const durationSecondsRaw = Number(item?.durationSeconds ?? item?.duration ?? 0);
const durationSeconds = Number.isFinite(durationSecondsRaw) && durationSecondsRaw > 0
? Math.trunc(durationSecondsRaw)
: 0;
const durationLabelRaw = String(item?.durationLabel || '').trim();
const durationLabel = durationLabelRaw || formatDurationClock(durationSeconds);
return {
playlistId,
playlistFile,
titleId: Number.isFinite(Number(item?.titleId)) ? Number(item.titleId) : null,
durationSeconds,
durationLabel: durationLabel || null,
score: Number.isFinite(score) ? score : null,
evaluationLabel: item?.evaluationLabel || null,
segmentCommand: item?.segmentCommand
@@ -688,6 +708,7 @@ export default function PipelineStatusCard({
<span>
{row.playlistFile}
{row.titleId !== null ? ` | Titel #${row.titleId}` : ''}
{row.durationLabel ? ` | Dauer ${row.durationLabel}` : ''}
{row.score !== null ? ` | Score ${row.score}` : ''}
{row.recommended ? ' | empfohlen' : ''}
</span>
@@ -757,7 +778,7 @@ export default function PipelineStatusCard({
{(state === 'READY_TO_ENCODE' || state === 'MEDIAINFO_CHECK' || mediaInfoReview) ? (
<div className="mediainfo-review-block">
<h3>Titel-/Spurprüfung</h3>
{state === 'READY_TO_ENCODE' && !reviewConfirmed && !queueLocked ? (
{state === 'READY_TO_ENCODE' && !queueLocked ? (
<small>
{isPreRipReview
? 'Spurauswahl kann direkt übernommen werden. Beim Klick auf "Backup + Encoding starten" wird automatisch bestätigt und gestartet.'
@@ -770,9 +791,9 @@ export default function PipelineStatusCard({
presetDisplayValue={presetDisplayValue}
commandOutputPath={commandOutputPath}
selectedEncodeTitleId={normalizeTitleId(selectedEncodeTitleId)}
allowTitleSelection={state === 'READY_TO_ENCODE' && !reviewConfirmed && !queueLocked}
allowTitleSelection={state === 'READY_TO_ENCODE' && !queueLocked}
onSelectEncodeTitle={(titleId) => setSelectedEncodeTitleId(normalizeTitleId(titleId))}
allowTrackSelection={state === 'READY_TO_ENCODE' && !reviewConfirmed && !queueLocked}
allowTrackSelection={state === 'READY_TO_ENCODE' && !queueLocked}
trackSelectionByTitle={trackSelectionByTitle}
onTrackSelectionChange={(titleId, trackType, trackId, checked) => {
const normalizedTitleId = normalizeTitleId(titleId);
@@ -808,7 +829,7 @@ export default function PipelineStatusCard({
userPresets={filteredUserPresets}
selectedUserPresetId={selectedUserPresetId}
onUserPresetChange={(presetId) => setSelectedUserPresetId(presetId)}
allowEncodeItemSelection={state === 'READY_TO_ENCODE' && !reviewConfirmed && !queueLocked}
allowEncodeItemSelection={state === 'READY_TO_ENCODE' && !queueLocked}
onAddPreEncodeItem={(itemType) => {
setPreEncodeItems((prev) => {
const current = Array.isArray(prev) ? prev : [];
+11 -1
View File
@@ -392,7 +392,7 @@ else
ok "Benutzer '$SERVICE_USER' angelegt"
fi
for grp in cdrom optical disk; do
for grp in cdrom optical disk video render; do
if getent group "$grp" &>/dev/null; then
usermod -aG "$grp" "$SERVICE_USER" 2>/dev/null || true
info "Benutzer '$SERVICE_USER' zur Gruppe '$grp' hinzugefügt"
@@ -541,6 +541,16 @@ StandardOutput=journal
StandardError=journal
SyslogIdentifier=ripster-backend
# Device-Zugriff fuer GPU und CD-ROM
DeviceAllow=/dev/sr0 rw
DeviceAllow=/dev/nvidia0 rw
DeviceAllow=/dev/nvidiactl rw
DeviceAllow=/dev/nvidia-uvm rw
DeviceAllow=/dev/nvidia-uvm-tools rw
DeviceAllow=/dev/dri/renderD128 rw
DeviceAllow=/dev/dri/renderD129 rw
SupplementaryGroups=video render cdrom disk
NoNewPrivileges=true
ProtectSystem=full
ProtectHome=read-only
+1 -1
View File
@@ -1 +1 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link rel=icon href=/ripster/assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Ripster</title><link rel=stylesheet href=/ripster/assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=/ripster/assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=/ripster/stylesheets/extra.css><script>__md_scope=new URL("/ripster/",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=/ripster/. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg> </label> <div classLine truncated
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link rel=icon href=/ripster/assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Ripster</title><link rel=stylesheet href=/ripster/assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=/ripster/assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=/ripster/stylesheets/extra.css><script>__md_scope=new URL("/ripster/",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=/ripster/. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg> </label> <div classLine truncated
+56
View File
@@ -0,0 +1,56 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/api/crons/ rel=canonical><link href=../history/ rel=prev><link href=../websocket/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Cron API - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#cron-api class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Cron API </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4Line truncated
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a><span class=w> </span><span class=nt>&quot;jobs&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-0-3><a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-0-4><a id=__codelineno-0-4 name=__codelineno-0-4 href=#__codelineno-0-4></a><span class=w> </span><span class=nt>&quot;id&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-0-5><a id=__codelineno-0-5 name=__codelineno-0-5 href=#__codelineno-0-5></a><span class=w> </span><span class=nt>&quot;name&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Nachtlauf Backup&quot;</span><span class=p>,</span>
</span><span id=__span-0-6><a id=__codelineno-0-6 name=__codelineno-0-6 href=#__codelineno-0-6></a><span class=w> </span><span class=nt>&quot;cronExpression&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;0 2 * * *&quot;</span><span class=p>,</span>
</span><span id=__span-0-7><a id=__codelineno-0-7 name=__codelineno-0-7 href=#__codelineno-0-7></a><span class=w> </span><span class=nt>&quot;sourceType&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;script&quot;</span><span class=p>,</span>
</span><span id=__span-0-8><a id=__codelineno-0-8 name=__codelineno-0-8 href=#__codelineno-0-8></a><span class=w> </span><span class=nt>&quot;sourceId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>3</span><span class=p>,</span>
</span><span id=__span-0-9><a id=__codelineno-0-9 name=__codelineno-0-9 href=#__codelineno-0-9></a><span class=w> </span><span class=nt>&quot;sourceName&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Backup-Skript&quot;</span><span class=p>,</span>
</span><span id=__span-0-10><a id=__codelineno-0-10 name=__codelineno-0-10 href=#__codelineno-0-10></a><span class=w> </span><span class=nt>&quot;enabled&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-0-11><a id=__codelineno-0-11 name=__codelineno-0-11 href=#__codelineno-0-11></a><span class=w> </span><span class=nt>&quot;pushoverEnabled&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-0-12><a id=__codelineno-0-12 name=__codelineno-0-12 href=#__codelineno-0-12></a><span class=w> </span><span class=nt>&quot;lastRunAt&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2026-03-10T02:00:00.000Z&quot;</span><span class=p>,</span>
</span><span id=__span-0-13><a id=__codelineno-0-13 name=__codelineno-0-13 href=#__codelineno-0-13></a><span class=w> </span><span class=nt>&quot;lastRunStatus&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;success&quot;</span><span class=p>,</span>
</span><span id=__span-0-14><a id=__codelineno-0-14 name=__codelineno-0-14 href=#__codelineno-0-14></a><span class=w> </span><span class=nt>&quot;nextRunAt&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2026-03-11T02:00:00.000Z&quot;</span><span class=p>,</span>
</span><span id=__span-0-15><a id=__codelineno-0-15 name=__codelineno-0-15 href=#__codelineno-0-15></a><span class=w> </span><span class=nt>&quot;createdAt&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2026-03-01T10:00:00.000Z&quot;</span><span class=p>,</span>
</span><span id=__span-0-16><a id=__codelineno-0-16 name=__codelineno-0-16 href=#__codelineno-0-16></a><span class=w> </span><span class=nt>&quot;updatedAt&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2026-03-10T02:00:05.000Z&quot;</span>
</span><span id=__span-0-17><a id=__codelineno-0-17 name=__codelineno-0-17 href=#__codelineno-0-17></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-0-18><a id=__codelineno-0-18 name=__codelineno-0-18 href=#__codelineno-0-18></a><span class=w> </span><span class=p>]</span>
</span><span id=__span-0-19><a id=__codelineno-0-19 name=__codelineno-0-19 href=#__codelineno-0-19></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=post-apicrons>POST /api/crons<a class=headerlink href=#post-apicrons title="Permanent link">&para;</a></h2> <p>Erstellt Cron-Job.</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a><span class=p>{</span>
</span><span id=__span-1-2><a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a><span class=w> </span><span class=nt>&quot;name&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Nachtlauf Backup&quot;</span><span class=p>,</span>
</span><span id=__span-1-3><a id=__codelineno-1-3 name=__codelineno-1-3 href=#__codelineno-1-3></a><span class=w> </span><span class=nt>&quot;cronExpression&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;0 2 * * *&quot;</span><span class=p>,</span>
</span><span id=__span-1-4><a id=__codelineno-1-4 name=__codelineno-1-4 href=#__codelineno-1-4></a><span class=w> </span><span class=nt>&quot;sourceType&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;script&quot;</span><span class=p>,</span>
</span><span id=__span-1-5><a id=__codelineno-1-5 name=__codelineno-1-5 href=#__codelineno-1-5></a><span class=w> </span><span class=nt>&quot;sourceId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>3</span><span class=p>,</span>
</span><span id=__span-1-6><a id=__codelineno-1-6 name=__codelineno-1-6 href=#__codelineno-1-6></a><span class=w> </span><span class=nt>&quot;enabled&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-1-7><a id=__codelineno-1-7 name=__codelineno-1-7 href=#__codelineno-1-7></a><span class=w> </span><span class=nt>&quot;pushoverEnabled&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span>
</span><span id=__span-1-8><a id=__codelineno-1-8 name=__codelineno-1-8 href=#__codelineno-1-8></a><span class=p>}</span>
</span></code></pre></div> <p>Response: <code>201</code> mit <code>{ "job": { ... } }</code></p> <hr> <h2 id=get-apicronsid>GET /api/crons/:id<a class=headerlink href=#get-apicronsid title="Permanent link">&para;</a></h2> <p>Response:</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;job&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;id&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;name&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;...&quot;</span><span class=w> </span><span class=p>}</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=put-apicronsid>PUT /api/crons/:id<a class=headerlink href=#put-apicronsid title="Permanent link">&para;</a></h2> <p>Aktualisiert Cron-Job. Felder wie bei <code>POST</code>.</p> <p>Response:</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;job&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=err>...</span><span class=w> </span><span class=p>}</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=delete-apicronsid>DELETE /api/crons/:id<a class=headerlink href=#delete-apicronsid title="Permanent link">&para;</a></h2> <p>Response:</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;removed&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;id&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;name&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Nachtlauf Backup&quot;</span><span class=w> </span><span class=p>}</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=get-apicronsidlogs>GET /api/crons/:id/logs<a class=headerlink href=#get-apicronsidlogs title="Permanent link">&para;</a></h2> <p>Liefert Ausführungs-Logs.</p> <p><strong>Query-Parameter:</strong></p> <table> <thead> <tr> <th>Parameter</th> <th>Typ</th> <th>Default</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>limit</code></td> <td>number</td> <td><code>20</code></td> <td>Anzahl Einträge, max. <code>100</code></td> </tr> </tbody> </table> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a><span class=p>{</span>
</span><span id=__span-5-2><a id=__codelineno-5-2 name=__codelineno-5-2 href=#__codelineno-5-2></a><span class=w> </span><span class=nt>&quot;logs&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-5-3><a id=__codelineno-5-3 name=__codelineno-5-3 href=#__codelineno-5-3></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-5-4><a id=__codelineno-5-4 name=__codelineno-5-4 href=#__codelineno-5-4></a><span class=w> </span><span class=nt>&quot;id&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>42</span><span class=p>,</span>
</span><span id=__span-5-5><a id=__codelineno-5-5 name=__codelineno-5-5 href=#__codelineno-5-5></a><span class=w> </span><span class=nt>&quot;cronJobId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-5-6><a id=__codelineno-5-6 name=__codelineno-5-6 href=#__codelineno-5-6></a><span class=w> </span><span class=nt>&quot;startedAt&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2026-03-10T02:00:01.000Z&quot;</span><span class=p>,</span>
</span><span id=__span-5-7><a id=__codelineno-5-7 name=__codelineno-5-7 href=#__codelineno-5-7></a><span class=w> </span><span class=nt>&quot;finishedAt&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2026-03-10T02:00:05.000Z&quot;</span><span class=p>,</span>
</span><span id=__span-5-8><a id=__codelineno-5-8 name=__codelineno-5-8 href=#__codelineno-5-8></a><span class=w> </span><span class=nt>&quot;status&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;success&quot;</span><span class=p>,</span>
</span><span id=__span-5-9><a id=__codelineno-5-9 name=__codelineno-5-9 href=#__codelineno-5-9></a><span class=w> </span><span class=nt>&quot;output&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Backup abgeschlossen.&quot;</span><span class=p>,</span>
</span><span id=__span-5-10><a id=__codelineno-5-10 name=__codelineno-5-10 href=#__codelineno-5-10></a><span class=w> </span><span class=nt>&quot;errorMessage&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>null</span>
</span><span id=__span-5-11><a id=__codelineno-5-11 name=__codelineno-5-11 href=#__codelineno-5-11></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-5-12><a id=__codelineno-5-12 name=__codelineno-5-12 href=#__codelineno-5-12></a><span class=w> </span><span class=p>]</span>
</span><span id=__span-5-13><a id=__codelineno-5-13 name=__codelineno-5-13 href=#__codelineno-5-13></a><span class=p>}</span>
</span></code></pre></div> <p><code>status</code>: <code>running</code> | <code>success</code> | <code>error</code></p> <hr> <h2 id=post-apicronsidrun>POST /api/crons/:id/run<a class=headerlink href=#post-apicronsidrun title="Permanent link">&para;</a></h2> <p>Triggert Job manuell (asynchron).</p> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-6-1><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;triggered&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;cronJobId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <p>Wenn Job bereits läuft: <code>409</code>.</p> <hr> <h2 id=post-apicronsvalidate-expression>POST /api/crons/validate-expression<a class=headerlink href=#post-apicronsvalidate-expression title="Permanent link">&para;</a></h2> <p>Validiert 5-Felder-Cron-Ausdruck und berechnet nächsten Lauf.</p> <p><strong>Request:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-7-1><a id=__codelineno-7-1 name=__codelineno-7-1 href=#__codelineno-7-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;cronExpression&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;*/15 * * * *&quot;</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <p><strong>Gültige Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-8-1><a id=__codelineno-8-1 name=__codelineno-8-1 href=#__codelineno-8-1></a><span class=p>{</span>
</span><span id=__span-8-2><a id=__codelineno-8-2 name=__codelineno-8-2 href=#__codelineno-8-2></a><span class=w> </span><span class=nt>&quot;valid&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-8-3><a id=__codelineno-8-3 name=__codelineno-8-3 href=#__codelineno-8-3></a><span class=w> </span><span class=nt>&quot;nextRunAt&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2026-03-10T14:15:00.000Z&quot;</span>
</span><span id=__span-8-4><a id=__codelineno-8-4 name=__codelineno-8-4 href=#__codelineno-8-4></a><span class=p>}</span>
</span></code></pre></div> <p><strong>Ungültige Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-9-1><a id=__codelineno-9-1 name=__codelineno-9-1 href=#__codelineno-9-1></a><span class=p>{</span>
</span><span id=__span-9-2><a id=__codelineno-9-2 name=__codelineno-9-2 href=#__codelineno-9-2></a><span class=w> </span><span class=nt>&quot;valid&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>false</span><span class=p>,</span>
</span><span id=__span-9-3><a id=__codelineno-9-3 name=__codelineno-9-3 href=#__codelineno-9-3></a><span class=w> </span><span class=nt>&quot;error&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Cron-Ausdruck muss genau 5 Felder haben (Minute Stunde Tag Monat Wochentag).&quot;</span><span class=p>,</span>
</span><span id=__span-9-4><a id=__codelineno-9-4 name=__codelineno-9-4 href=#__codelineno-9-4></a><span class=w> </span><span class=nt>&quot;nextRunAt&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>null</span>
</span><span id=__span-9-5><a id=__codelineno-9-5 name=__codelineno-9-5 href=#__codelineno-9-5></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=cron-format>Cron-Format<a class=headerlink href=#cron-format title="Permanent link">&para;</a></h2> <p>Ripster unterstützt 5 Felder:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-10-1><a id=__codelineno-10-1 name=__codelineno-10-1 href=#__codelineno-10-1></a>Minute Stunde Tag Monat Wochentag
</span></code></pre></div> <p>Beispiele:</p> <ul> <li><code>0 2 * * *</code> täglich 02:00</li> <li><code>*/15 * * * *</code> alle 15 Minuten</li> <li><code>0 6 * * 1-5</code> Mo-Fr 06:00</li> </ul> <hr> <h2 id=websocket-events-zu-cron>WebSocket-Events zu Cron<a class=headerlink href=#websocket-events-zu-cron title="Permanent link">&para;</a></h2> <ul> <li><code>CRON_JOBS_UPDATED</code> bei Create/Update/Delete</li> <li><code>CRON_JOB_UPDATED</code> bei Laufzeitstatus (<code>running</code> -&gt; <code>success|error</code>)</li> </ul> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
+89 -70
View File
@@ -1,80 +1,99 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/api/history/ rel=canonical><link href=../settings/ rel=prev><link href=../websocket/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>History API - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#history-api class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> History API </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59Line truncated
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/api/history/ rel=canonical><link href=../settings/ rel=prev><link href=../crons/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>History API - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#history-api class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> History API </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.0Line truncated
</span></code></pre></div> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a><span class=p>{</span>
</span><span id=__span-1-2><a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a><span class=w> </span><span class=nt>&quot;jobs&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-1-3><a id=__codelineno-1-3 name=__codelineno-1-3 href=#__codelineno-1-3></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-1-4><a id=__codelineno-1-4 name=__codelineno-1-4 href=#__codelineno-1-4></a><span class=w> </span><span class=nt>&quot;id&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>42</span><span class=p>,</span>
</span><span id=__span-1-5><a id=__codelineno-1-5 name=__codelineno-1-5 href=#__codelineno-1-5></a><span class=w> </span><span class=nt>&quot;status&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;FINISHED&quot;</span><span class=p>,</span>
</span><span id=__span-1-6><a id=__codelineno-1-6 name=__codelineno-1-6 href=#__codelineno-1-6></a><span class=w> </span><span class=nt>&quot;title&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Inception&quot;</span><span class=p>,</span>
</span><span id=__span-1-7><a id=__codelineno-1-7 name=__codelineno-1-7 href=#__codelineno-1-7></a><span class=w> </span><span class=nt>&quot;imdb_id&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;tt1375666&quot;</span><span class=p>,</span>
</span><span id=__span-1-8><a id=__codelineno-1-8 name=__codelineno-1-8 href=#__codelineno-1-8></a><span class=w> </span><span class=nt>&quot;omdb_year&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2010&quot;</span><span class=p>,</span>
</span><span id=__span-1-9><a id=__codelineno-1-9 name=__codelineno-1-9 href=#__codelineno-1-9></a><span class=w> </span><span class=nt>&quot;omdb_type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;movie&quot;</span><span class=p>,</span>
</span><span id=__span-1-10><a id=__codelineno-1-10 name=__codelineno-1-10 href=#__codelineno-1-10></a><span class=w> </span><span class=nt>&quot;omdb_poster&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;https://...&quot;</span><span class=p>,</span>
</span><span id=__span-1-11><a id=__codelineno-1-11 name=__codelineno-1-11 href=#__codelineno-1-11></a><span class=w> </span><span class=nt>&quot;raw_path&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/mnt/nas/raw/Inception_t00.mkv&quot;</span><span class=p>,</span>
</span><span id=__span-1-12><a id=__codelineno-1-12 name=__codelineno-1-12 href=#__codelineno-1-12></a><span class=w> </span><span class=nt>&quot;output_path&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/mnt/nas/movies/Inception (2010).mkv&quot;</span><span class=p>,</span>
</span><span id=__span-1-13><a id=__codelineno-1-13 name=__codelineno-1-13 href=#__codelineno-1-13></a><span class=w> </span><span class=nt>&quot;created_at&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2024-01-15T10:00:00.000Z&quot;</span><span class=p>,</span>
</span><span id=__span-1-14><a id=__codelineno-1-14 name=__codelineno-1-14 href=#__codelineno-1-14></a><span class=w> </span><span class=nt>&quot;updated_at&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2024-01-15T12:30:00.000Z&quot;</span>
</span><span id=__span-1-15><a id=__codelineno-1-15 name=__codelineno-1-15 href=#__codelineno-1-15></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-1-16><a id=__codelineno-1-16 name=__codelineno-1-16 href=#__codelineno-1-16></a><span class=w> </span><span class=p>],</span>
</span><span id=__span-1-17><a id=__codelineno-1-17 name=__codelineno-1-17 href=#__codelineno-1-17></a><span class=w> </span><span class=nt>&quot;total&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span>
</span><span id=__span-1-18><a id=__codelineno-1-18 name=__codelineno-1-18 href=#__codelineno-1-18></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=get-apihistoryid>GET /api/history/:id<a class=headerlink href=#get-apihistoryid title="Permanent link">&para;</a></h2> <p>Gibt Detail-Informationen für einen einzelnen Job zurück.</p> <p><strong>URL-Parameter:</strong> <code>id</code> – Job-ID</p> <p><strong>Query-Parameter:</strong></p> <table> <thead> <tr> <th>Parameter</th> <th>Typ</th> <th>Standard</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>includeLogs</code></td> <td>boolean</td> <td><code>false</code></td> <td>Log-Inhalte einschließen</td> </tr> <tr> <td><code>includeLiveLog</code></td> <td>boolean</td> <td><code>false</code></td> <td>Aktuellen Live-Log einschließen</td> </tr> </tbody> </table> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a><span class=p>{</span>
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a><span class=w> </span><span class=nt>&quot;id&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>42</span><span class=p>,</span>
</span><span id=__span-2-3><a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a><span class=w> </span><span class=nt>&quot;status&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;FINISHED&quot;</span><span class=p>,</span>
</span><span id=__span-2-4><a id=__codelineno-2-4 name=__codelineno-2-4 href=#__codelineno-2-4></a><span class=w> </span><span class=nt>&quot;title&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Inception&quot;</span><span class=p>,</span>
</span><span id=__span-2-5><a id=__codelineno-2-5 name=__codelineno-2-5 href=#__codelineno-2-5></a><span class=w> </span><span class=nt>&quot;imdb_id&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;tt1375666&quot;</span><span class=p>,</span>
</span><span id=__span-2-6><a id=__codelineno-2-6 name=__codelineno-2-6 href=#__codelineno-2-6></a><span class=w> </span><span class=nt>&quot;encode_plan&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=err>...</span><span class=w> </span><span class=p>},</span>
</span><span id=__span-2-7><a id=__codelineno-2-7 name=__codelineno-2-7 href=#__codelineno-2-7></a><span class=w> </span><span class=nt>&quot;makemkv_output&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=err>...</span><span class=w> </span><span class=p>},</span>
</span><span id=__span-2-8><a id=__codelineno-2-8 name=__codelineno-2-8 href=#__codelineno-2-8></a><span class=w> </span><span class=nt>&quot;mediainfo_output&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=err>...</span><span class=w> </span><span class=p>},</span>
</span><span id=__span-2-9><a id=__codelineno-2-9 name=__codelineno-2-9 href=#__codelineno-2-9></a><span class=w> </span><span class=nt>&quot;handbrake_log&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/path/to/log&quot;</span><span class=p>,</span>
</span><span id=__span-2-10><a id=__codelineno-2-10 name=__codelineno-2-10 href=#__codelineno-2-10></a><span class=w> </span><span class=nt>&quot;logs&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-2-11><a id=__codelineno-2-11 name=__codelineno-2-11 href=#__codelineno-2-11></a><span class=w> </span><span class=nt>&quot;handbrake&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Encoding: task 1 of 1, 100.0%\n...&quot;</span>
</span><span id=__span-2-12><a id=__codelineno-2-12 name=__codelineno-2-12 href=#__codelineno-2-12></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-2-13><a id=__codelineno-2-13 name=__codelineno-2-13 href=#__codelineno-2-13></a><span class=w> </span><span class=nt>&quot;created_at&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2024-01-15T10:00:00.000Z&quot;</span><span class=p>,</span>
</span><span id=__span-2-14><a id=__codelineno-2-14 name=__codelineno-2-14 href=#__codelineno-2-14></a><span class=w> </span><span class=nt>&quot;updated_at&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2024-01-15T12:30:00.000Z&quot;</span>
</span><span id=__span-2-15><a id=__codelineno-2-15 name=__codelineno-2-15 href=#__codelineno-2-15></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=get-apihistorydatabase>GET /api/history/database<a class=headerlink href=#get-apihistorydatabase title="Permanent link">&para;</a></h2> <p>Gibt alle rohen Datenbankzeilen zurück (Debug-Ansicht).</p> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a><span class=p>{</span>
</span><span id=__span-3-2><a id=__codelineno-3-2 name=__codelineno-3-2 href=#__codelineno-3-2></a><span class=w> </span><span class=nt>&quot;jobs&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;id&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;status&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;FINISHED&quot;</span><span class=p>,</span><span class=w> </span><span class=err>...</span><span class=w> </span><span class=p>}</span><span class=w> </span><span class=p>],</span>
</span><span id=__span-3-3><a id=__codelineno-3-3 name=__codelineno-3-3 href=#__codelineno-3-3></a><span class=w> </span><span class=nt>&quot;total&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>15</span>
</span><span id=__span-3-4><a id=__codelineno-3-4 name=__codelineno-3-4 href=#__codelineno-3-4></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=get-apihistoryorphan-raw>GET /api/history/orphan-raw<a class=headerlink href=#get-apihistoryorphan-raw title="Permanent link">&para;</a></h2> <p>Findet Raw-Ordner, die nicht als Jobs in der Datenbank registriert sind.</p> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a><span class=p>{</span>
</span><span id=__span-4-2><a id=__codelineno-4-2 name=__codelineno-4-2 href=#__codelineno-4-2></a><span class=w> </span><span class=nt>&quot;orphans&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-4-3><a id=__codelineno-4-3 name=__codelineno-4-3 href=#__codelineno-4-3></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-4-4><a id=__codelineno-4-4 name=__codelineno-4-4 href=#__codelineno-4-4></a><span class=w> </span><span class=nt>&quot;path&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/mnt/nas/raw/UnknownMovie_2023-12-01&quot;</span><span class=p>,</span>
</span><span id=__span-4-5><a id=__codelineno-4-5 name=__codelineno-4-5 href=#__codelineno-4-5></a><span class=w> </span><span class=nt>&quot;size&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;45.2 GB&quot;</span><span class=p>,</span>
</span><span id=__span-4-6><a id=__codelineno-4-6 name=__codelineno-4-6 href=#__codelineno-4-6></a><span class=w> </span><span class=nt>&quot;modifiedAt&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2023-12-01T15:00:00.000Z&quot;</span><span class=p>,</span>
</span><span id=__span-4-7><a id=__codelineno-4-7 name=__codelineno-4-7 href=#__codelineno-4-7></a><span class=w> </span><span class=nt>&quot;files&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=s2>&quot;t00.mkv&quot;</span><span class=p>,</span><span class=w> </span><span class=s2>&quot;t01.mkv&quot;</span><span class=p>]</span>
</span><span id=__span-4-8><a id=__codelineno-4-8 name=__codelineno-4-8 href=#__codelineno-4-8></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-4-9><a id=__codelineno-4-9 name=__codelineno-4-9 href=#__codelineno-4-9></a><span class=w> </span><span class=p>]</span>
</span><span id=__span-4-10><a id=__codelineno-4-10 name=__codelineno-4-10 href=#__codelineno-4-10></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=post-apihistoryorphan-rawimport>POST /api/history/orphan-raw/import<a class=headerlink href=#post-apihistoryorphan-rawimport title="Permanent link">&para;</a></h2> <p>Importiert einen Orphan-Raw-Ordner als Job in die Datenbank.</p> <p><strong>Request:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a><span class=p>{</span>
</span><span id=__span-5-2><a id=__codelineno-5-2 name=__codelineno-5-2 href=#__codelineno-5-2></a><span class=w> </span><span class=nt>&quot;path&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/mnt/nas/raw/UnknownMovie_2023-12-01&quot;</span>
</span><span id=__span-5-3><a id=__codelineno-5-3 name=__codelineno-5-3 href=#__codelineno-5-3></a><span class=p>}</span>
</span><span id=__span-1-7><a id=__codelineno-1-7 name=__codelineno-1-7 href=#__codelineno-1-7></a><span class=w> </span><span class=nt>&quot;raw_path&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/mnt/raw/Inception - RAW - job-42&quot;</span><span class=p>,</span>
</span><span id=__span-1-8><a id=__codelineno-1-8 name=__codelineno-1-8 href=#__codelineno-1-8></a><span class=w> </span><span class=nt>&quot;output_path&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/mnt/movies/Inception (2010)/Inception (2010).mkv&quot;</span><span class=p>,</span>
</span><span id=__span-1-9><a id=__codelineno-1-9 name=__codelineno-1-9 href=#__codelineno-1-9></a><span class=w> </span><span class=nt>&quot;mediaType&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;bluray&quot;</span><span class=p>,</span>
</span><span id=__span-1-10><a id=__codelineno-1-10 name=__codelineno-1-10 href=#__codelineno-1-10></a><span class=w> </span><span class=nt>&quot;ripSuccessful&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-1-11><a id=__codelineno-1-11 name=__codelineno-1-11 href=#__codelineno-1-11></a><span class=w> </span><span class=nt>&quot;encodeSuccess&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-1-12><a id=__codelineno-1-12 name=__codelineno-1-12 href=#__codelineno-1-12></a><span class=w> </span><span class=nt>&quot;created_at&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2026-03-10T08:00:00.000Z&quot;</span><span class=p>,</span>
</span><span id=__span-1-13><a id=__codelineno-1-13 name=__codelineno-1-13 href=#__codelineno-1-13></a><span class=w> </span><span class=nt>&quot;updated_at&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2026-03-10T10:00:00.000Z&quot;</span>
</span><span id=__span-1-14><a id=__codelineno-1-14 name=__codelineno-1-14 href=#__codelineno-1-14></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-1-15><a id=__codelineno-1-15 name=__codelineno-1-15 href=#__codelineno-1-15></a><span class=w> </span><span class=p>]</span>
</span><span id=__span-1-16><a id=__codelineno-1-16 name=__codelineno-1-16 href=#__codelineno-1-16></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=get-apihistoryid>GET /api/history/:id<a class=headerlink href=#get-apihistoryid title="Permanent link">&para;</a></h2> <p>Liefert Job-Detail.</p> <p><strong>Query-Parameter:</strong></p> <table> <thead> <tr> <th>Parameter</th> <th>Typ</th> <th>Standard</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>includeLogs</code></td> <td>bool</td> <td><code>false</code></td> <td>Prozesslog laden</td> </tr> <tr> <td><code>includeLiveLog</code></td> <td>bool</td> <td><code>false</code></td> <td>alias-artig ebenfalls Prozesslog laden</td> </tr> <tr> <td><code>includeAllLogs</code></td> <td>bool</td> <td><code>false</code></td> <td>vollständiges Log statt Tail</td> </tr> <tr> <td><code>logTailLines</code></td> <td>number</td> <td><code>800</code></td> <td>Tail-Länge falls nicht <code>includeAllLogs</code></td> </tr> </tbody> </table> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a><span class=p>{</span>
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a><span class=w> </span><span class=nt>&quot;job&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-2-3><a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a><span class=w> </span><span class=nt>&quot;id&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>42</span><span class=p>,</span>
</span><span id=__span-2-4><a id=__codelineno-2-4 name=__codelineno-2-4 href=#__codelineno-2-4></a><span class=w> </span><span class=nt>&quot;status&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;FINISHED&quot;</span><span class=p>,</span>
</span><span id=__span-2-5><a id=__codelineno-2-5 name=__codelineno-2-5 href=#__codelineno-2-5></a><span class=w> </span><span class=nt>&quot;makemkvInfo&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{},</span>
</span><span id=__span-2-6><a id=__codelineno-2-6 name=__codelineno-2-6 href=#__codelineno-2-6></a><span class=w> </span><span class=nt>&quot;mediainfoInfo&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{},</span>
</span><span id=__span-2-7><a id=__codelineno-2-7 name=__codelineno-2-7 href=#__codelineno-2-7></a><span class=w> </span><span class=nt>&quot;handbrakeInfo&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{},</span>
</span><span id=__span-2-8><a id=__codelineno-2-8 name=__codelineno-2-8 href=#__codelineno-2-8></a><span class=w> </span><span class=nt>&quot;encodePlan&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{},</span>
</span><span id=__span-2-9><a id=__codelineno-2-9 name=__codelineno-2-9 href=#__codelineno-2-9></a><span class=w> </span><span class=nt>&quot;log&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;...&quot;</span><span class=p>,</span>
</span><span id=__span-2-10><a id=__codelineno-2-10 name=__codelineno-2-10 href=#__codelineno-2-10></a><span class=w> </span><span class=nt>&quot;log_count&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-2-11><a id=__codelineno-2-11 name=__codelineno-2-11 href=#__codelineno-2-11></a><span class=w> </span><span class=nt>&quot;logMeta&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-2-12><a id=__codelineno-2-12 name=__codelineno-2-12 href=#__codelineno-2-12></a><span class=w> </span><span class=nt>&quot;loaded&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-2-13><a id=__codelineno-2-13 name=__codelineno-2-13 href=#__codelineno-2-13></a><span class=w> </span><span class=nt>&quot;total&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>800</span><span class=p>,</span>
</span><span id=__span-2-14><a id=__codelineno-2-14 name=__codelineno-2-14 href=#__codelineno-2-14></a><span class=w> </span><span class=nt>&quot;returned&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>800</span><span class=p>,</span>
</span><span id=__span-2-15><a id=__codelineno-2-15 name=__codelineno-2-15 href=#__codelineno-2-15></a><span class=w> </span><span class=nt>&quot;truncated&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span>
</span><span id=__span-2-16><a id=__codelineno-2-16 name=__codelineno-2-16 href=#__codelineno-2-16></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-2-17><a id=__codelineno-2-17 name=__codelineno-2-17 href=#__codelineno-2-17></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-2-18><a id=__codelineno-2-18 name=__codelineno-2-18 href=#__codelineno-2-18></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=get-apihistorydatabase>GET /api/history/database<a class=headerlink href=#get-apihistorydatabase title="Permanent link">&para;</a></h2> <p>Debug-Ansicht der DB-Zeilen (angereichert).</p> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a><span class=p>{</span>
</span><span id=__span-3-2><a id=__codelineno-3-2 name=__codelineno-3-2 href=#__codelineno-3-2></a><span class=w> </span><span class=nt>&quot;rows&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-3-3><a id=__codelineno-3-3 name=__codelineno-3-3 href=#__codelineno-3-3></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-3-4><a id=__codelineno-3-4 name=__codelineno-3-4 href=#__codelineno-3-4></a><span class=w> </span><span class=nt>&quot;id&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>42</span><span class=p>,</span>
</span><span id=__span-3-5><a id=__codelineno-3-5 name=__codelineno-3-5 href=#__codelineno-3-5></a><span class=w> </span><span class=nt>&quot;status&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;FINISHED&quot;</span><span class=p>,</span>
</span><span id=__span-3-6><a id=__codelineno-3-6 name=__codelineno-3-6 href=#__codelineno-3-6></a><span class=w> </span><span class=nt>&quot;rawFolderName&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Inception - RAW - job-42&quot;</span>
</span><span id=__span-3-7><a id=__codelineno-3-7 name=__codelineno-3-7 href=#__codelineno-3-7></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-3-8><a id=__codelineno-3-8 name=__codelineno-3-8 href=#__codelineno-3-8></a><span class=w> </span><span class=p>]</span>
</span><span id=__span-3-9><a id=__codelineno-3-9 name=__codelineno-3-9 href=#__codelineno-3-9></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=get-apihistoryorphan-raw>GET /api/history/orphan-raw<a class=headerlink href=#get-apihistoryorphan-raw title="Permanent link">&para;</a></h2> <p>Sucht RAW-Ordner ohne zugehörigen Job.</p> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a><span class=p>{</span>
</span><span id=__span-4-2><a id=__codelineno-4-2 name=__codelineno-4-2 href=#__codelineno-4-2></a><span class=w> </span><span class=nt>&quot;rawDir&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/mnt/raw&quot;</span><span class=p>,</span>
</span><span id=__span-4-3><a id=__codelineno-4-3 name=__codelineno-4-3 href=#__codelineno-4-3></a><span class=w> </span><span class=nt>&quot;rawDirs&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=s2>&quot;/mnt/raw&quot;</span><span class=p>,</span><span class=w> </span><span class=s2>&quot;/mnt/raw-bluray&quot;</span><span class=p>],</span>
</span><span id=__span-4-4><a id=__codelineno-4-4 name=__codelineno-4-4 href=#__codelineno-4-4></a><span class=w> </span><span class=nt>&quot;rows&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-4-5><a id=__codelineno-4-5 name=__codelineno-4-5 href=#__codelineno-4-5></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-4-6><a id=__codelineno-4-6 name=__codelineno-4-6 href=#__codelineno-4-6></a><span class=w> </span><span class=nt>&quot;rawPath&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/mnt/raw/Inception (2010) [tt1375666] - RAW - job-99&quot;</span><span class=p>,</span>
</span><span id=__span-4-7><a id=__codelineno-4-7 name=__codelineno-4-7 href=#__codelineno-4-7></a><span class=w> </span><span class=nt>&quot;folderName&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Inception (2010) [tt1375666] - RAW - job-99&quot;</span><span class=p>,</span>
</span><span id=__span-4-8><a id=__codelineno-4-8 name=__codelineno-4-8 href=#__codelineno-4-8></a><span class=w> </span><span class=nt>&quot;title&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Inception&quot;</span><span class=p>,</span>
</span><span id=__span-4-9><a id=__codelineno-4-9 name=__codelineno-4-9 href=#__codelineno-4-9></a><span class=w> </span><span class=nt>&quot;year&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>2010</span><span class=p>,</span>
</span><span id=__span-4-10><a id=__codelineno-4-10 name=__codelineno-4-10 href=#__codelineno-4-10></a><span class=w> </span><span class=nt>&quot;imdbId&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;tt1375666&quot;</span><span class=p>,</span>
</span><span id=__span-4-11><a id=__codelineno-4-11 name=__codelineno-4-11 href=#__codelineno-4-11></a><span class=w> </span><span class=nt>&quot;folderJobId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>99</span><span class=p>,</span>
</span><span id=__span-4-12><a id=__codelineno-4-12 name=__codelineno-4-12 href=#__codelineno-4-12></a><span class=w> </span><span class=nt>&quot;entryCount&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>4</span><span class=p>,</span>
</span><span id=__span-4-13><a id=__codelineno-4-13 name=__codelineno-4-13 href=#__codelineno-4-13></a><span class=w> </span><span class=nt>&quot;hasBlurayStructure&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-4-14><a id=__codelineno-4-14 name=__codelineno-4-14 href=#__codelineno-4-14></a><span class=w> </span><span class=nt>&quot;lastModifiedAt&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2026-03-10T09:00:00.000Z&quot;</span>
</span><span id=__span-4-15><a id=__codelineno-4-15 name=__codelineno-4-15 href=#__codelineno-4-15></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-4-16><a id=__codelineno-4-16 name=__codelineno-4-16 href=#__codelineno-4-16></a><span class=w> </span><span class=p>]</span>
</span><span id=__span-4-17><a id=__codelineno-4-17 name=__codelineno-4-17 href=#__codelineno-4-17></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=post-apihistoryorphan-rawimport>POST /api/history/orphan-raw/import<a class=headerlink href=#post-apihistoryorphan-rawimport title="Permanent link">&para;</a></h2> <p>Importiert RAW-Ordner als FINISHED-Job.</p> <p><strong>Request:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;rawPath&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/mnt/raw/Inception (2010) [tt1375666] - RAW - job-99&quot;</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-6-1><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a><span class=p>{</span>
</span><span id=__span-6-2><a id=__codelineno-6-2 name=__codelineno-6-2 href=#__codelineno-6-2></a><span class=w> </span><span class=nt>&quot;ok&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-6-3><a id=__codelineno-6-3 name=__codelineno-6-3 href=#__codelineno-6-3></a><span class=w> </span><span class=nt>&quot;jobId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>99</span><span class=p>,</span>
</span><span id=__span-6-4><a id=__codelineno-6-4 name=__codelineno-6-4 href=#__codelineno-6-4></a><span class=w> </span><span class=nt>&quot;message&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Orphan-Ordner als Job importiert&quot;</span>
</span><span id=__span-6-5><a id=__codelineno-6-5 name=__codelineno-6-5 href=#__codelineno-6-5></a><span class=p>}</span>
</span></code></pre></div> <p>Nach dem Import kann dem Job über <code>/api/history/:id/omdb/assign</code> Metadaten zugewiesen werden.</p> <hr> <h2 id=post-apihistoryidomdbassign>POST /api/history/:id/omdb/assign<a class=headerlink href=#post-apihistoryidomdbassign title="Permanent link">&para;</a></h2> <p>Weist einem bestehenden Job OMDb-Metadaten nachträglich zu.</p> <p><strong>URL-Parameter:</strong> <code>id</code> – Job-ID</p> <p><strong>Request:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-7-1><a id=__codelineno-7-1 name=__codelineno-7-1 href=#__codelineno-7-1></a><span class=p>{</span>
</span><span id=__span-6-2><a id=__codelineno-6-2 name=__codelineno-6-2 href=#__codelineno-6-2></a><span class=w> </span><span class=nt>&quot;job&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;id&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>77</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;status&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;FINISHED&quot;</span><span class=w> </span><span class=p>},</span>
</span><span id=__span-6-3><a id=__codelineno-6-3 name=__codelineno-6-3 href=#__codelineno-6-3></a><span class=w> </span><span class=nt>&quot;uiReset&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;reset&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;state&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;IDLE&quot;</span><span class=w> </span><span class=p>}</span>
</span><span id=__span-6-4><a id=__codelineno-6-4 name=__codelineno-6-4 href=#__codelineno-6-4></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=post-apihistoryidomdbassign>POST /api/history/:id/omdb/assign<a class=headerlink href=#post-apihistoryidomdbassign title="Permanent link">&para;</a></h2> <p>Weist OMDb-/Metadaten nachträglich zu.</p> <p><strong>Request:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-7-1><a id=__codelineno-7-1 name=__codelineno-7-1 href=#__codelineno-7-1></a><span class=p>{</span>
</span><span id=__span-7-2><a id=__codelineno-7-2 name=__codelineno-7-2 href=#__codelineno-7-2></a><span class=w> </span><span class=nt>&quot;imdbId&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;tt1375666&quot;</span><span class=p>,</span>
</span><span id=__span-7-3><a id=__codelineno-7-3 name=__codelineno-7-3 href=#__codelineno-7-3></a><span class=w> </span><span class=nt>&quot;title&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Inception&quot;</span><span class=p>,</span>
</span><span id=__span-7-4><a id=__codelineno-7-4 name=__codelineno-7-4 href=#__codelineno-7-4></a><span class=w> </span><span class=nt>&quot;year&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2010&quot;</span><span class=p>,</span>
</span><span id=__span-7-5><a id=__codelineno-7-5 name=__codelineno-7-5 href=#__codelineno-7-5></a><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;movie&quot;</span><span class=p>,</span>
</span><span id=__span-7-6><a id=__codelineno-7-6 name=__codelineno-7-6 href=#__codelineno-7-6></a><span class=w> </span><span class=nt>&quot;poster&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;https://...&quot;</span>
</span><span id=__span-7-4><a id=__codelineno-7-4 name=__codelineno-7-4 href=#__codelineno-7-4></a><span class=w> </span><span class=nt>&quot;year&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>2010</span><span class=p>,</span>
</span><span id=__span-7-5><a id=__codelineno-7-5 name=__codelineno-7-5 href=#__codelineno-7-5></a><span class=w> </span><span class=nt>&quot;poster&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;https://...&quot;</span><span class=p>,</span>
</span><span id=__span-7-6><a id=__codelineno-7-6 name=__codelineno-7-6 href=#__codelineno-7-6></a><span class=w> </span><span class=nt>&quot;fromOmdb&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span>
</span><span id=__span-7-7><a id=__codelineno-7-7 name=__codelineno-7-7 href=#__codelineno-7-7></a><span class=p>}</span>
</span></code></pre></div> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-8-1><a id=__codelineno-8-1 name=__codelineno-8-1 href=#__codelineno-8-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;ok&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=post-apihistoryiddelete-files>POST /api/history/:id/delete-files<a class=headerlink href=#post-apihistoryiddelete-files title="Permanent link">&para;</a></h2> <p>Löscht die Dateien eines Jobs (Raw und/oder Output), behält den Job-Eintrag.</p> <p><strong>URL-Parameter:</strong> <code>id</code> – Job-ID</p> <p><strong>Request:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-9-1><a id=__codelineno-9-1 name=__codelineno-9-1 href=#__codelineno-9-1></a><span class=p>{</span>
</span><span id=__span-9-2><a id=__codelineno-9-2 name=__codelineno-9-2 href=#__codelineno-9-2></a><span class=w> </span><span class=nt>&quot;deleteRaw&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-9-3><a id=__codelineno-9-3 name=__codelineno-9-3 href=#__codelineno-9-3></a><span class=w> </span><span class=nt>&quot;deleteOutput&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>false</span>
</span><span id=__span-9-4><a id=__codelineno-9-4 name=__codelineno-9-4 href=#__codelineno-9-4></a><span class=p>}</span>
</span></code></pre></div> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-10-1><a id=__codelineno-10-1 name=__codelineno-10-1 href=#__codelineno-10-1></a><span class=p>{</span>
</span><span id=__span-10-2><a id=__codelineno-10-2 name=__codelineno-10-2 href=#__codelineno-10-2></a><span class=w> </span><span class=nt>&quot;ok&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-10-3><a id=__codelineno-10-3 name=__codelineno-10-3 href=#__codelineno-10-3></a><span class=w> </span><span class=nt>&quot;deleted&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-10-4><a id=__codelineno-10-4 name=__codelineno-10-4 href=#__codelineno-10-4></a><span class=w> </span><span class=nt>&quot;raw&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/mnt/nas/raw/Inception_t00.mkv&quot;</span><span class=p>,</span>
</span><span id=__span-10-5><a id=__codelineno-10-5 name=__codelineno-10-5 href=#__codelineno-10-5></a><span class=w> </span><span class=nt>&quot;output&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>null</span>
</span><span id=__span-10-6><a id=__codelineno-10-6 name=__codelineno-10-6 href=#__codelineno-10-6></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-10-7><a id=__codelineno-10-7 name=__codelineno-10-7 href=#__codelineno-10-7></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=post-apihistoryiddelete>POST /api/history/:id/delete<a class=headerlink href=#post-apihistoryiddelete title="Permanent link">&para;</a></h2> <p>Löscht den Job-Eintrag aus der Datenbank, optional auch die Dateien.</p> <p><strong>URL-Parameter:</strong> <code>id</code> – Job-ID</p> <p><strong>Request:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-11-1><a id=__codelineno-11-1 name=__codelineno-11-1 href=#__codelineno-11-1></a><span class=p>{</span>
</span><span id=__span-11-2><a id=__codelineno-11-2 name=__codelineno-11-2 href=#__codelineno-11-2></a><span class=w> </span><span class=nt>&quot;deleteFiles&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span>
</span><span id=__span-11-3><a id=__codelineno-11-3 name=__codelineno-11-3 href=#__codelineno-11-3></a><span class=p>}</span>
</span></code></pre></div> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-12-1><a id=__codelineno-12-1 name=__codelineno-12-1 href=#__codelineno-12-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;ok&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;message&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Job gelöscht&quot;</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <div class="admonition warning"> <p class=admonition-title>Unwiderruflich</p> <p>Das Löschen von Jobs und Dateien ist nicht rückgängig zu machen.</p> </div> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
</span></code></pre></div> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-8-1><a id=__codelineno-8-1 name=__codelineno-8-1 href=#__codelineno-8-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;job&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;id&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>42</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;imdb_id&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;tt1375666&quot;</span><span class=w> </span><span class=p>}</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=post-apihistoryiddelete-files>POST /api/history/:id/delete-files<a class=headerlink href=#post-apihistoryiddelete-files title="Permanent link">&para;</a></h2> <p>Löscht Dateien eines Jobs, behält DB-Eintrag.</p> <p><strong>Request:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-9-1><a id=__codelineno-9-1 name=__codelineno-9-1 href=#__codelineno-9-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;target&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;both&quot;</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <p><code>target</code>: <code>raw</code> | <code>movie</code> | <code>both</code></p> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-10-1><a id=__codelineno-10-1 name=__codelineno-10-1 href=#__codelineno-10-1></a><span class=p>{</span>
</span><span id=__span-10-2><a id=__codelineno-10-2 name=__codelineno-10-2 href=#__codelineno-10-2></a><span class=w> </span><span class=nt>&quot;summary&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-10-3><a id=__codelineno-10-3 name=__codelineno-10-3 href=#__codelineno-10-3></a><span class=w> </span><span class=nt>&quot;target&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;both&quot;</span><span class=p>,</span>
</span><span id=__span-10-4><a id=__codelineno-10-4 name=__codelineno-10-4 href=#__codelineno-10-4></a><span class=w> </span><span class=nt>&quot;raw&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;attempted&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;deleted&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;filesDeleted&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>12</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;dirsRemoved&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>3</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;reason&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>null</span><span class=w> </span><span class=p>},</span>
</span><span id=__span-10-5><a id=__codelineno-10-5 name=__codelineno-10-5 href=#__codelineno-10-5></a><span class=w> </span><span class=nt>&quot;movie&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;attempted&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;deleted&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>false</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;filesDeleted&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>0</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;dirsRemoved&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>0</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;reason&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Movie-Datei/Pfad existiert nicht.&quot;</span><span class=w> </span><span class=p>}</span>
</span><span id=__span-10-6><a id=__codelineno-10-6 name=__codelineno-10-6 href=#__codelineno-10-6></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-10-7><a id=__codelineno-10-7 name=__codelineno-10-7 href=#__codelineno-10-7></a><span class=w> </span><span class=nt>&quot;job&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;id&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>42</span><span class=w> </span><span class=p>}</span>
</span><span id=__span-10-8><a id=__codelineno-10-8 name=__codelineno-10-8 href=#__codelineno-10-8></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=post-apihistoryiddelete>POST /api/history/:id/delete<a class=headerlink href=#post-apihistoryiddelete title="Permanent link">&para;</a></h2> <p>Löscht Job aus DB; optional auch Dateien.</p> <p><strong>Request:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-11-1><a id=__codelineno-11-1 name=__codelineno-11-1 href=#__codelineno-11-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;target&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;none&quot;</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <p><code>target</code>: <code>none</code> | <code>raw</code> | <code>movie</code> | <code>both</code></p> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-12-1><a id=__codelineno-12-1 name=__codelineno-12-1 href=#__codelineno-12-1></a><span class=p>{</span>
</span><span id=__span-12-2><a id=__codelineno-12-2 name=__codelineno-12-2 href=#__codelineno-12-2></a><span class=w> </span><span class=nt>&quot;deleted&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-12-3><a id=__codelineno-12-3 name=__codelineno-12-3 href=#__codelineno-12-3></a><span class=w> </span><span class=nt>&quot;jobId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>42</span><span class=p>,</span>
</span><span id=__span-12-4><a id=__codelineno-12-4 name=__codelineno-12-4 href=#__codelineno-12-4></a><span class=w> </span><span class=nt>&quot;fileTarget&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;both&quot;</span><span class=p>,</span>
</span><span id=__span-12-5><a id=__codelineno-12-5 name=__codelineno-12-5 href=#__codelineno-12-5></a><span class=w> </span><span class=nt>&quot;fileSummary&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-12-6><a id=__codelineno-12-6 name=__codelineno-12-6 href=#__codelineno-12-6></a><span class=w> </span><span class=nt>&quot;target&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;both&quot;</span><span class=p>,</span>
</span><span id=__span-12-7><a id=__codelineno-12-7 name=__codelineno-12-7 href=#__codelineno-12-7></a><span class=w> </span><span class=nt>&quot;raw&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;filesDeleted&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>10</span><span class=w> </span><span class=p>},</span>
</span><span id=__span-12-8><a id=__codelineno-12-8 name=__codelineno-12-8 href=#__codelineno-12-8></a><span class=w> </span><span class=nt>&quot;movie&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;filesDeleted&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=w> </span><span class=p>}</span>
</span><span id=__span-12-9><a id=__codelineno-12-9 name=__codelineno-12-9 href=#__codelineno-12-9></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-12-10><a id=__codelineno-12-10 name=__codelineno-12-10 href=#__codelineno-12-10></a><span class=w> </span><span class=nt>&quot;uiReset&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-12-11><a id=__codelineno-12-11 name=__codelineno-12-11 href=#__codelineno-12-11></a><span class=w> </span><span class=nt>&quot;reset&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-12-12><a id=__codelineno-12-12 name=__codelineno-12-12 href=#__codelineno-12-12></a><span class=w> </span><span class=nt>&quot;state&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;IDLE&quot;</span>
</span><span id=__span-12-13><a id=__codelineno-12-13 name=__codelineno-12-13 href=#__codelineno-12-13></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-12-14><a id=__codelineno-12-14 name=__codelineno-12-14 href=#__codelineno-12-14></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=hinweise>Hinweise<a class=headerlink href=#hinweise title="Permanent link">&para;</a></h2> <ul> <li>Ein aktiver Pipeline-Job kann nicht gelöscht werden (<code>409</code>).</li> <li>Alle Löschoperationen sind irreversibel.</li> </ul> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
+15 -6
View File
@@ -1,6 +1,15 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/api/ rel=canonical><link href=../pipeline/post-encode-scripts/ rel=prev><link href=pipeline/ rel=next><link rel=icon href=../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>API-Referenz - Ripster</title><link rel=stylesheet href=../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../stylesheets/extra.css><script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#api-referenz class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> API-Referenz </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1Line truncated
</span></code></pre></div> <p>Konfigurierbar über die Umgebungsvariable <code>PORT</code>.</p> <hr> <h2 id=api-gruppen>API-Gruppen<a class=headerlink href=#api-gruppen title="Permanent link">&para;</a></h2> <div class="grid cards"> <ul> <li> <p><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M22 14h-2v2h-6v-3h2v-2h-2V6a2 2 0 0 0-2-2H4V2H2v8h2V8h6v3H8v2h2v5a2 2 0 0 0 2 2h8v2h2"/></svg></span> <strong>Pipeline API</strong></p> <hr> <p>Pipeline-Steuerung: Analyse starten, Metadaten setzen, Ripping und Encoding steuern.</p> <p><a href=pipeline/ ><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13.22 19.03a.75.75 0 0 1 0-1.06L18.19 13H3.75a.75.75 0 0 1 0-1.5h14.44l-4.97-4.97a.749.749 0 0 1 .326-1.275.75.75 0 0 1 .734.215l6.25 6.25a.75.75 0 0 1 0 1.06l-6.25 6.25a.75.75 0 0 1-1.06 0"/></svg></span> Pipeline API</a></p> </li> <li> <p><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 15.5A3.5 3.5 0 0 1 8.5 12 3.5 3.5 0 0 1 12 8.5a3.5 3.5 0 0 1 3.5 3.5 3.5 3.5 0 0 1-3.5 3.5m7.43-2.53c.04-.32.07-.64.07-.97s-.03-.66-.07-1l2.11-1.63c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.31-.61-.22l-2.49 1c-.52-.39-1.06-.73-1.69-.98l-.37-2.65A.506.506 0 0 0 14 2h-4c-.25 0-.46.18-.5.42l-.37 2.65c-.63.25-1.17.59-1.69.98l-2.49-1c-.22-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64L4.57 11c-.04.34-.07.67-.07 1s.03.65.07.97l-2.11 1.66c-.19.15-.25.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1.01c.52.4 1.06.74 1.69.99l.37 2.65c.04.24.25.42.5.42h4c.25 0 .46-.18.5-.42l.37-2.65c.63-.26 1.17-.59 1.69-.99l2.49 1.01c.22.08.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64z"/></svg></span> <strong>Settings API</strong></p> <hr> <p>Einstellungen lesen und schreiben.</p> <p><a href=settings/ ><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13.22 19.03a.75.75 0 0 1 0-1.06L18.19 13H3.75a.75.75 0 0 1 0-1.5h14.44l-4.97-4.97a.749.749 0 0 1 .326-1.275.75.75 0 0 1 .734.215l6.25 6.25a.75.75 0 0 1 0 1.06l-6.25 6.25a.75.75 0 0 1-1.06 0"/></svg></span> Settings API</a></p> </li> <li> <p><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13.5 8H12v5l4.28 2.54.72-1.21-3.5-2.08zM13 3a9 9 0 0 0-9 9H1l3.96 4.03L9 12H6a7 7 0 0 1 7-7 7 7 0 0 1 7 7 7 7 0 0 1-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42A8.9 8.9 0 0 0 13 21a9 9 0 0 0 9-9 9 9 0 0 0-9-9"/></svg></span> <strong>History API</strong></p> <hr> <p>Job-Geschichte abfragen, Jobs löschen, Orphan-Ordner importieren.</p> <p><a href=history/ ><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13.22 19.03a.75.75 0 0 1 0-1.06L18.19 13H3.75a.75.75 0 0 1 0-1.5h14.44l-4.97-4.97a.749.749 0 0 1 .326-1.275.75.75 0 0 1 .734.215l6.25 6.25a.75.75 0 0 1 0 1.06l-6.25 6.25a.75.75 0 0 1-1.06 0"/></svg></span> History API</a></p> </li> <li> <p><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M11 15H6l7-14v8h5l-7 14z"/></svg></span> <strong>WebSocket Events</strong></p> <hr> <p>Echtzeit-Events für Pipeline-Status, Fortschritt und Disc-Erkennung.</p> <p><a href=websocket/ ><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13.22 19.03a.75.75 0 0 1 0-1.06L18.19 13H3.75a.75.75 0 0 1 0-1.5h14.44l-4.97-4.97a.749.749 0 0 1 .326-1.275.75.75 0 0 1 .734.215l6.25 6.25a.75.75 0 0 1 0 1.06l-6.25 6.25a.75.75 0 0 1-1.06 0"/></svg></span> WebSocket</a></p> </li> </ul> </div> <hr> <h2 id=authentifizierung>Authentifizierung<a class=headerlink href=#authentifizierung title="Permanent link">&para;</a></h2> <p>Die API hat <strong>keine Authentifizierung</strong>. Sie ist für den Einsatz im lokalen Netzwerk konzipiert.</p> <div class="admonition warning"> <p class=admonition-title>Produktionsbetrieb</p> <p>Falls Ripster öffentlich erreichbar sein soll, schütze die API mit einem Reverse-Proxy (z. B. nginx mit Basic Auth oder OAuth).</p> </div> <hr> <h2 id=fehlerformat>Fehlerformat<a class=headerlink href=#fehlerformat title="Permanent link">&para;</a></h2> <p>Alle API-Fehler werden im folgenden Format zurückgegeben:</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a><span class=p>{</span>
</span><span id=__span-1-2><a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a><span class=w> </span><span class=nt>&quot;error&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Job nicht gefunden&quot;</span><span class=p>,</span>
</span><span id=__span-1-3><a id=__codelineno-1-3 name=__codelineno-1-3 href=#__codelineno-1-3></a><span class=w> </span><span class=nt>&quot;details&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Kein Job mit ID 999 vorhanden&quot;</span>
</span><span id=__span-1-4><a id=__codelineno-1-4 name=__codelineno-1-4 href=#__codelineno-1-4></a><span class=p>}</span>
</span></code></pre></div> <p>HTTP-Statuscodes:</p> <table> <thead> <tr> <th>Code</th> <th>Bedeutung</th> </tr> </thead> <tbody> <tr> <td><code>200</code></td> <td>Erfolg</td> </tr> <tr> <td><code>400</code></td> <td>Ungültige Anfrage</td> </tr> <tr> <td><code>404</code></td> <td>Ressource nicht gefunden</td> </tr> <tr> <td><code>409</code></td> <td>Konflikt (z.B. Pipeline bereits aktiv)</td> </tr> <tr> <td><code>500</code></td> <td>Interner Serverfehler</td> </tr> </tbody> </table> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/api/ rel=canonical><link href=../pipeline/post-encode-scripts/ rel=prev><link href=pipeline/ rel=next><link rel=icon href=../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>API-Referenz - Ripster</title><link rel=stylesheet href=../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../stylesheets/extra.css><script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#api-referenz class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> API-Referenz </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1Line truncated
</span></code></pre></div> <p>API-Prefix: <code>/api</code></p> <p>Beispiele:</p> <ul> <li><code>GET /api/health</code></li> <li><code>GET /api/pipeline/state</code></li> </ul> <hr> <h2 id=api-gruppen>API-Gruppen<a class=headerlink href=#api-gruppen title="Permanent link">&para;</a></h2> <div class="grid cards"> <ul> <li> <p><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M7.5 4A5.5 5.5 0 0 0 2 9.5c0 .5.09 1 .22 1.5H6.3l1.27-3.37c.3-.8 1.48-.88 1.86 0L11.5 13l.59-1.42c.13-.33.48-.58.91-.58h8.78c.13-.5.22-1 .22-1.5A5.5 5.5 0 0 0 16.5 4c-1.86 0-3.5.93-4.5 2.34C11 4.93 9.36 4 7.5 4M3 12.5a1 1 0 0 0-1 1 1 1 0 0 0 1 1h2.44L11 20c1 .9 1 .9 2 0l5.56-5.5H21a1 1 0 0 0 1-1 1 1 0 0 0-1-1h-7.6l-.93 2.3c-.4 1.01-1.55.87-1.92.03L8.5 9.5l-.96 2.33c-.15.38-.49.67-.94.67z"/></svg></span> <strong>Health</strong></p> <hr> <p>Service-Liveness.</p> <p><code>GET /api/health</code></p> </li> <li> <p><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M22 14h-2v2h-6v-3h2v-2h-2V6a2 2 0 0 0-2-2H4V2H2v8h2V8h6v3H8v2h2v5a2 2 0 0 0 2 2h8v2h2"/></svg></span> <strong>Pipeline API</strong></p> <hr> <p>Analyse, Start/Retry/Cancel, Queue, Re-Encode.</p> <p><a href=pipeline/ ><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13.22 19.03a.75.75 0 0 1 0-1.06L18.19 13H3.75a.75.75 0 0 1 0-1.5h14.44l-4.97-4.97a.749.749 0 0 1 .326-1.275.75.75 0 0 1 .734.215l6.25 6.25a.75.75 0 0 1 0 1.06l-6.25 6.25a.75.75 0 0 1-1.06 0"/></svg></span> Pipeline API</a></p> </li> <li> <p><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 15.5A3.5 3.5 0 0 1 8.5 12 3.5 3.5 0 0 1 12 8.5a3.5 3.5 0 0 1 3.5 3.5 3.5 3.5 0 0 1-3.5 3.5m7.43-2.53c.04-.32.07-.64.07-.97s-.03-.66-.07-1l2.11-1.63c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.31-.61-.22l-2.49 1c-.52-.39-1.06-.73-1.69-.98l-.37-2.65A.506.506 0 0 0 14 2h-4c-.25 0-.46.18-.5.42l-.37 2.65c-.63.25-1.17.59-1.69.98l-2.49-1c-.22-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64L4.57 11c-.04.34-.07.67-.07 1s.03.65.07.97l-2.11 1.66c-.19.15-.25.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1.01c.52.4 1.06.74 1.69.99l.37 2.65c.04.24.25.42.5.42h4c.25 0 .46-.18.5-.42l.37-2.65c.63-.26 1.17-.59 1.69-.99l2.49 1.01c.22.08.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64z"/></svg></span> <strong>Settings API</strong></p> <hr> <p>Einstellungen, Skripte/Ketten, User-Presets.</p> <p><a href=settings/ ><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13.22 19.03a.75.75 0 0 1 0-1.06L18.19 13H3.75a.75.75 0 0 1 0-1.5h14.44l-4.97-4.97a.749.749 0 0 1 .326-1.275.75.75 0 0 1 .734.215l6.25 6.25a.75.75 0 0 1 0 1.06l-6.25 6.25a.75.75 0 0 1-1.06 0"/></svg></span> Settings API</a></p> </li> <li> <p><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13.5 8H12v5l4.28 2.54.72-1.21-3.5-2.08zM13 3a9 9 0 0 0-9 9H1l3.96 4.03L9 12H6a7 7 0 0 1 7-7 7 7 0 0 1 7 7 7 7 0 0 1-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42A8.9 8.9 0 0 0 13 21a9 9 0 0 0 9-9 9 9 0 0 0-9-9"/></svg></span> <strong>History API</strong></p> <hr> <p>Job-Historie, Orphan-Import, Löschoperationen.</p> <p><a href=history/ ><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13.22 19.03a.75.75 0 0 1 0-1.06L18.19 13H3.75a.75.75 0 0 1 0-1.5h14.44l-4.97-4.97a.749.749 0 0 1 .326-1.275.75.75 0 0 1 .734.215l6.25 6.25a.75.75 0 0 1 0 1.06l-6.25 6.25a.75.75 0 0 1-1.06 0"/></svg></span> History API</a></p> </li> <li> <p><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 20a8 8 0 0 0 8-8 8 8 0 0 0-8-8 8 8 0 0 0-8 8 8 8 0 0 0 8 8m0-18a10 10 0 0 1 10 10 10 10 0 0 1-10 10C6.47 22 2 17.5 2 12A10 10 0 0 1 12 2m.5 5v5.25l4.5 2.67-.75 1.23L11 13V7z"/></svg></span> <strong>Cron API</strong></p> <hr> <p>Zeitgesteuerte Skript-/Kettenausführung.</p> <p><a href=crons/ ><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13.22 19.03a.75.75 0 0 1 0-1.06L18.19 13H3.75a.75.75 0 0 1 0-1.5h14.44l-4.97-4.97a.749.749 0 0 1 .326-1.275.75.75 0 0 1 .734.215l6.25 6.25a.75.75 0 0 1 0 1.06l-6.25 6.25a.75.75 0 0 1-1.06 0"/></svg></span> Cron API</a></p> </li> <li> <p><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M11 15H6l7-14v8h5l-7 14z"/></svg></span> <strong>WebSocket Events</strong></p> <hr> <p>Pipeline-, Queue-, Disk-, Settings-, Cron- und Monitoring-Events.</p> <p><a href=websocket/ ><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13.22 19.03a.75.75 0 0 1 0-1.06L18.19 13H3.75a.75.75 0 0 1 0-1.5h14.44l-4.97-4.97a.749.749 0 0 1 .326-1.275.75.75 0 0 1 .734.215l6.25 6.25a.75.75 0 0 1 0 1.06l-6.25 6.25a.75.75 0 0 1-1.06 0"/></svg></span> WebSocket</a></p> </li> </ul> </div> <hr> <h2 id=authentifizierung>Authentifizierung<a class=headerlink href=#authentifizierung title="Permanent link">&para;</a></h2> <p>Es gibt keine eingebaute AuthentifiLine truncated
</span><span id=__span-1-2><a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a><span class=w> </span><span class=nt>&quot;error&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-1-3><a id=__codelineno-1-3 name=__codelineno-1-3 href=#__codelineno-1-3></a><span class=w> </span><span class=nt>&quot;message&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Job nicht gefunden.&quot;</span><span class=p>,</span>
</span><span id=__span-1-4><a id=__codelineno-1-4 name=__codelineno-1-4 href=#__codelineno-1-4></a><span class=w> </span><span class=nt>&quot;statusCode&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>404</span><span class=p>,</span>
</span><span id=__span-1-5><a id=__codelineno-1-5 name=__codelineno-1-5 href=#__codelineno-1-5></a><span class=w> </span><span class=nt>&quot;reqId&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;req_...&quot;</span><span class=p>,</span>
</span><span id=__span-1-6><a id=__codelineno-1-6 name=__codelineno-1-6 href=#__codelineno-1-6></a><span class=w> </span><span class=nt>&quot;details&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-1-7><a id=__codelineno-1-7 name=__codelineno-1-7 href=#__codelineno-1-7></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-1-8><a id=__codelineno-1-8 name=__codelineno-1-8 href=#__codelineno-1-8></a><span class=w> </span><span class=nt>&quot;field&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;name&quot;</span><span class=p>,</span>
</span><span id=__span-1-9><a id=__codelineno-1-9 name=__codelineno-1-9 href=#__codelineno-1-9></a><span class=w> </span><span class=nt>&quot;message&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Name darf nicht leer sein.&quot;</span>
</span><span id=__span-1-10><a id=__codelineno-1-10 name=__codelineno-1-10 href=#__codelineno-1-10></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-1-11><a id=__codelineno-1-11 name=__codelineno-1-11 href=#__codelineno-1-11></a><span class=w> </span><span class=p>]</span>
</span><span id=__span-1-12><a id=__codelineno-1-12 name=__codelineno-1-12 href=#__codelineno-1-12></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-1-13><a id=__codelineno-1-13 name=__codelineno-1-13 href=#__codelineno-1-13></a><span class=p>}</span>
</span></code></pre></div> <p><code>details</code> ist optional (z. B. bei Validierungsfehlern).</p> <hr> <h2 id=haufige-statuscodes>Häufige Statuscodes<a class=headerlink href=#haufige-statuscodes title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>Code</th> <th>Bedeutung</th> </tr> </thead> <tbody> <tr> <td><code>200</code></td> <td>Erfolg</td> </tr> <tr> <td><code>201</code></td> <td>Ressource erstellt</td> </tr> <tr> <td><code>400</code></td> <td>Ungültige Anfrage / Validierungsfehler</td> </tr> <tr> <td><code>404</code></td> <td>Ressource nicht gefunden</td> </tr> <tr> <td><code>409</code></td> <td>Konflikt (z. B. falscher Pipeline-Zustand, Job läuft bereits)</td> </tr> <tr> <td><code>500</code></td> <td>Interner Fehler</td> </tr> </tbody> </table> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
+124 -49
View File
@@ -1,35 +1,62 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/api/pipeline/ rel=canonical><link href=../ rel=prev><link href=../settings/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Pipeline API - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#pipeline-api class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Pipeline API </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-Line truncated
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/api/pipeline/ rel=canonical><link href=../ rel=prev><link href=../settings/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Pipeline API - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#pipeline-api class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Pipeline API </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-Line truncated
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a><span class=w> </span><span class=nt>&quot;pipeline&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-0-3><a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a><span class=w> </span><span class=nt>&quot;state&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;READY_TO_ENCODE&quot;</span><span class=p>,</span>
</span><span id=__span-0-4><a id=__codelineno-0-4 name=__codelineno-0-4 href=#__codelineno-0-4></a><span class=w> </span><span class=nt>&quot;activeJobId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>42</span><span class=p>,</span>
</span><span id=__span-0-5><a id=__codelineno-0-5 name=__codelineno-0-5 href=#__codelineno-0-5></a><span class=w> </span><span class=nt>&quot;progress&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>0</span><span class=p>,</span>
</span><span id=__span-0-6><a id=__codelineno-0-6 name=__codelineno-0-6 href=#__codelineno-0-6></a><span class=w> </span><span class=nt>&quot;eta&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>null</span><span class=p>,</span>
</span><span id=__span-0-7><a id=__codelineno-0-7 name=__codelineno-0-7 href=#__codelineno-0-7></a><span class=w> </span><span class=nt>&quot;statusText&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Mediainfo geladen - bitte bestätigen&quot;</span><span class=p>,</span>
</span><span id=__span-0-7><a id=__codelineno-0-7 name=__codelineno-0-7 href=#__codelineno-0-7></a><span class=w> </span><span class=nt>&quot;statusText&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Mediainfo bestätigt - Encode manuell starten&quot;</span><span class=p>,</span>
</span><span id=__span-0-8><a id=__codelineno-0-8 name=__codelineno-0-8 href=#__codelineno-0-8></a><span class=w> </span><span class=nt>&quot;context&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-0-9><a id=__codelineno-0-9 name=__codelineno-0-9 href=#__codelineno-0-9></a><span class=w> </span><span class=nt>&quot;jobId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>42</span>
</span><span id=__span-0-10><a id=__codelineno-0-10 name=__codelineno-0-10 href=#__codelineno-0-10></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-0-11><a id=__codelineno-0-11 name=__codelineno-0-11 href=#__codelineno-0-11></a><span class=w> </span><span class=nt>&quot;queue&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-0-12><a id=__codelineno-0-12 name=__codelineno-0-12 href=#__codelineno-0-12></a><span class=w> </span><span class=nt>&quot;maxParallelJobs&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-0-13><a id=__codelineno-0-13 name=__codelineno-0-13 href=#__codelineno-0-13></a><span class=w> </span><span class=nt>&quot;runningCount&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>0</span><span class=p>,</span>
</span><span id=__span-0-14><a id=__codelineno-0-14 name=__codelineno-0-14 href=#__codelineno-0-14></a><span class=w> </span><span class=nt>&quot;queuedCount&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>0</span><span class=p>,</span>
</span><span id=__span-0-15><a id=__codelineno-0-15 name=__codelineno-0-15 href=#__codelineno-0-15></a><span class=w> </span><span class=nt>&quot;runningJobs&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[],</span>
</span><span id=__span-0-16><a id=__codelineno-0-16 name=__codelineno-0-16 href=#__codelineno-0-16></a><span class=w> </span><span class=nt>&quot;queuedJobs&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[]</span>
</span><span id=__span-0-17><a id=__codelineno-0-17 name=__codelineno-0-17 href=#__codelineno-0-17></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-0-18><a id=__codelineno-0-18 name=__codelineno-0-18 href=#__codelineno-0-18></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-0-19><a id=__codelineno-0-19 name=__codelineno-0-19 href=#__codelineno-0-19></a><span class=p>}</span>
</span></code></pre></div> <p><strong>Pipeline-Zustände:</strong></p> <table> <thead> <tr> <th>Wert</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>IDLE</code></td> <td>Wartet auf Medium</td> </tr> <tr> <td><code>DISC_DETECTED</code></td> <td>Medium erkannt, wartet auf Analyse-Start</td> </tr> <tr> <td><code>METADATA_SELECTION</code></td> <td>Metadaten-Dialog aktiv</td> </tr> <tr> <td><code>WAITING_FOR_USER_DECISION</code></td> <td>Manuelle Playlist-Auswahl erforderlich</td> </tr> <tr> <td><code>READY_TO_START</code></td> <td>Übergang/Fallback vor Start</td> </tr> <tr> <td><code>RIPPING</code></td> <td>MakeMKV läuft</td> </tr> <tr> <td><code>MEDIAINFO_CHECK</code></td> <td>HandBrake-Scan + Plan-Erstellung</td> </tr> <tr> <td><code>READY_TO_ENCODE</code></td> <td>Review bereit</td> </tr> <tr> <td><code>ENCODING</code></td> <td>HandBrake-Encoding läuft (inkl. Post-Skripte)</td> </tr> <tr> <td><code>FINISHED</code></td> <td>Abgeschlossen</td> </tr> <tr> <td><code>CANCELLED</code></td> <td>Vom Benutzer abgebrochen</td> </tr> <tr> <td><code>ERROR</code></td> <td>Fehler</td> </tr> </tbody> </table> <hr> <h2 id=post-apipipelineanalyze>POST /api/pipeline/analyze<a class=headerlink href=#post-apipipelineanalyze title="Permanent link">&para;</a></h2> <p>Startet die Analyse für die aktuell erkannte Disc.</p> <p><strong>Request:</strong> kein Body</p> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a><span class=p>{</span>
</span><span id=__span-0-11><a id=__codelineno-0-11 name=__codelineno-0-11 href=#__codelineno-0-11></a><span class=w> </span><span class=nt>&quot;jobProgress&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-0-12><a id=__codelineno-0-12 name=__codelineno-0-12 href=#__codelineno-0-12></a><span class=w> </span><span class=nt>&quot;42&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-0-13><a id=__codelineno-0-13 name=__codelineno-0-13 href=#__codelineno-0-13></a><span class=w> </span><span class=nt>&quot;state&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;MEDIAINFO_CHECK&quot;</span><span class=p>,</span>
</span><span id=__span-0-14><a id=__codelineno-0-14 name=__codelineno-0-14 href=#__codelineno-0-14></a><span class=w> </span><span class=nt>&quot;progress&quot;</span><span class=p>:</span><span class=w> </span><span class=mf>68.5</span><span class=p>,</span>
</span><span id=__span-0-15><a id=__codelineno-0-15 name=__codelineno-0-15 href=#__codelineno-0-15></a><span class=w> </span><span class=nt>&quot;eta&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>null</span><span class=p>,</span>
</span><span id=__span-0-16><a id=__codelineno-0-16 name=__codelineno-0-16 href=#__codelineno-0-16></a><span class=w> </span><span class=nt>&quot;statusText&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;MEDIAINFO_CHECK 68.50%&quot;</span>
</span><span id=__span-0-17><a id=__codelineno-0-17 name=__codelineno-0-17 href=#__codelineno-0-17></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-0-18><a id=__codelineno-0-18 name=__codelineno-0-18 href=#__codelineno-0-18></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-0-19><a id=__codelineno-0-19 name=__codelineno-0-19 href=#__codelineno-0-19></a><span class=w> </span><span class=nt>&quot;queue&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-0-20><a id=__codelineno-0-20 name=__codelineno-0-20 href=#__codelineno-0-20></a><span class=w> </span><span class=nt>&quot;maxParallelJobs&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-0-21><a id=__codelineno-0-21 name=__codelineno-0-21 href=#__codelineno-0-21></a><span class=w> </span><span class=nt>&quot;runningCount&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-0-22><a id=__codelineno-0-22 name=__codelineno-0-22 href=#__codelineno-0-22></a><span class=w> </span><span class=nt>&quot;queuedCount&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>2</span><span class=p>,</span>
</span><span id=__span-0-23><a id=__codelineno-0-23 name=__codelineno-0-23 href=#__codelineno-0-23></a><span class=w> </span><span class=nt>&quot;runningJobs&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[],</span>
</span><span id=__span-0-24><a id=__codelineno-0-24 name=__codelineno-0-24 href=#__codelineno-0-24></a><span class=w> </span><span class=nt>&quot;queuedJobs&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[]</span>
</span><span id=__span-0-25><a id=__codelineno-0-25 name=__codelineno-0-25 href=#__codelineno-0-25></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-0-26><a id=__codelineno-0-26 name=__codelineno-0-26 href=#__codelineno-0-26></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-0-27><a id=__codelineno-0-27 name=__codelineno-0-27 href=#__codelineno-0-27></a><span class=w> </span><span class=nt>&quot;hardwareMonitoring&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-0-28><a id=__codelineno-0-28 name=__codelineno-0-28 href=#__codelineno-0-28></a><span class=w> </span><span class=nt>&quot;enabled&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-0-29><a id=__codelineno-0-29 name=__codelineno-0-29 href=#__codelineno-0-29></a><span class=w> </span><span class=nt>&quot;intervalMs&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>5000</span><span class=p>,</span>
</span><span id=__span-0-30><a id=__codelineno-0-30 name=__codelineno-0-30 href=#__codelineno-0-30></a><span class=w> </span><span class=nt>&quot;updatedAt&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2026-03-10T09:00:00.000Z&quot;</span><span class=p>,</span>
</span><span id=__span-0-31><a id=__codelineno-0-31 name=__codelineno-0-31 href=#__codelineno-0-31></a><span class=w> </span><span class=nt>&quot;sample&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-0-32><a id=__codelineno-0-32 name=__codelineno-0-32 href=#__codelineno-0-32></a><span class=w> </span><span class=nt>&quot;cpu&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{},</span>
</span><span id=__span-0-33><a id=__codelineno-0-33 name=__codelineno-0-33 href=#__codelineno-0-33></a><span class=w> </span><span class=nt>&quot;memory&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{},</span>
</span><span id=__span-0-34><a id=__codelineno-0-34 name=__codelineno-0-34 href=#__codelineno-0-34></a><span class=w> </span><span class=nt>&quot;gpu&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{},</span>
</span><span id=__span-0-35><a id=__codelineno-0-35 name=__codelineno-0-35 href=#__codelineno-0-35></a><span class=w> </span><span class=nt>&quot;storage&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{}</span>
</span><span id=__span-0-36><a id=__codelineno-0-36 name=__codelineno-0-36 href=#__codelineno-0-36></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-0-37><a id=__codelineno-0-37 name=__codelineno-0-37 href=#__codelineno-0-37></a><span class=w> </span><span class=nt>&quot;error&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>null</span>
</span><span id=__span-0-38><a id=__codelineno-0-38 name=__codelineno-0-38 href=#__codelineno-0-38></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-0-39><a id=__codelineno-0-39 name=__codelineno-0-39 href=#__codelineno-0-39></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=post-apipipelineanalyze>POST /api/pipeline/analyze<a class=headerlink href=#post-apipipelineanalyze title="Permanent link">&para;</a></h2> <p>Startet Disc-Analyse und legt Job an.</p> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a><span class=p>{</span>
</span><span id=__span-1-2><a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a><span class=w> </span><span class=nt>&quot;result&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-1-3><a id=__codelineno-1-3 name=__codelineno-1-3 href=#__codelineno-1-3></a><span class=w> </span><span class=nt>&quot;jobId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>42</span><span class=p>,</span>
</span><span id=__span-1-4><a id=__codelineno-1-4 name=__codelineno-1-4 href=#__codelineno-1-4></a><span class=w> </span><span class=nt>&quot;detectedTitle&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;INCEPTION&quot;</span><span class=p>,</span>
</span><span id=__span-1-5><a id=__codelineno-1-5 name=__codelineno-1-5 href=#__codelineno-1-5></a><span class=w> </span><span class=nt>&quot;omdbCandidates&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[]</span>
</span><span id=__span-1-6><a id=__codelineno-1-6 name=__codelineno-1-6 href=#__codelineno-1-6></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-1-7><a id=__codelineno-1-7 name=__codelineno-1-7 href=#__codelineno-1-7></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=post-apipipelinerescan-disc>POST /api/pipeline/rescan-disc<a class=headerlink href=#post-apipipelinerescan-disc title="Permanent link">&para;</a></h2> <p>Erzwingt eine erneute Laufwerksprüfung.</p> <p><strong>Response (Beispiel):</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a><span class=p>{</span>
</span></code></pre></div> <hr> <h2 id=post-apipipelinerescan-disc>POST /api/pipeline/rescan-disc<a class=headerlink href=#post-apipipelinerescan-disc title="Permanent link">&para;</a></h2> <p>Erzwingt erneute Laufwerksprüfung.</p> <p><strong>Response (Beispiel):</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a><span class=p>{</span>
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a><span class=w> </span><span class=nt>&quot;result&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-2-3><a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a><span class=w> </span><span class=nt>&quot;emitted&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;discInserted&quot;</span>
</span><span id=__span-2-4><a id=__codelineno-2-4 name=__codelineno-2-4 href=#__codelineno-2-4></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-2-5><a id=__codelineno-2-5 name=__codelineno-2-5 href=#__codelineno-2-5></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=get-apipipelineomdbsearchq>GET /api/pipeline/omdb/search?q=<query><a class=headerlink href=#get-apipipelineomdbsearchq title="Permanent link">&para;</a></h2> <p>Sucht OMDb-Titel.</p> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a><span class=p>{</span>
</span><span id=__span-2-3><a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a><span class=w> </span><span class=nt>&quot;present&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-2-4><a id=__codelineno-2-4 name=__codelineno-2-4 href=#__codelineno-2-4></a><span class=w> </span><span class=nt>&quot;changed&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-2-5><a id=__codelineno-2-5 name=__codelineno-2-5 href=#__codelineno-2-5></a><span class=w> </span><span class=nt>&quot;emitted&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;discInserted&quot;</span><span class=p>,</span>
</span><span id=__span-2-6><a id=__codelineno-2-6 name=__codelineno-2-6 href=#__codelineno-2-6></a><span class=w> </span><span class=nt>&quot;device&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-2-7><a id=__codelineno-2-7 name=__codelineno-2-7 href=#__codelineno-2-7></a><span class=w> </span><span class=nt>&quot;path&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/dev/sr0&quot;</span><span class=p>,</span>
</span><span id=__span-2-8><a id=__codelineno-2-8 name=__codelineno-2-8 href=#__codelineno-2-8></a><span class=w> </span><span class=nt>&quot;discLabel&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;INCEPTION&quot;</span><span class=p>,</span>
</span><span id=__span-2-9><a id=__codelineno-2-9 name=__codelineno-2-9 href=#__codelineno-2-9></a><span class=w> </span><span class=nt>&quot;mediaProfile&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;bluray&quot;</span>
</span><span id=__span-2-10><a id=__codelineno-2-10 name=__codelineno-2-10 href=#__codelineno-2-10></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-2-11><a id=__codelineno-2-11 name=__codelineno-2-11 href=#__codelineno-2-11></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-2-12><a id=__codelineno-2-12 name=__codelineno-2-12 href=#__codelineno-2-12></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=get-apipipelineomdbsearchq>GET /api/pipeline/omdb/search?q=<query><a class=headerlink href=#get-apipipelineomdbsearchq title="Permanent link">&para;</a></h2> <p>OMDb-Titelsuche.</p> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a><span class=p>{</span>
</span><span id=__span-3-2><a id=__codelineno-3-2 name=__codelineno-3-2 href=#__codelineno-3-2></a><span class=w> </span><span class=nt>&quot;results&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-3-3><a id=__codelineno-3-3 name=__codelineno-3-3 href=#__codelineno-3-3></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-3-4><a id=__codelineno-3-4 name=__codelineno-3-4 href=#__codelineno-3-4></a><span class=w> </span><span class=nt>&quot;imdbId&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;tt1375666&quot;</span><span class=p>,</span>
@@ -40,7 +67,7 @@
</span><span id=__span-3-9><a id=__codelineno-3-9 name=__codelineno-3-9 href=#__codelineno-3-9></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-3-10><a id=__codelineno-3-10 name=__codelineno-3-10 href=#__codelineno-3-10></a><span class=w> </span><span class=p>]</span>
</span><span id=__span-3-11><a id=__codelineno-3-11 name=__codelineno-3-11 href=#__codelineno-3-11></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=post-apipipelineselect-metadata>POST /api/pipeline/select-metadata<a class=headerlink href=#post-apipipelineselect-metadata title="Permanent link">&para;</a></h2> <p>Setzt Metadaten (und optional Playlist-Entscheidung).</p> <p><strong>Request:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a><span class=p>{</span>
</span></code></pre></div> <hr> <h2 id=post-apipipelineselect-metadata>POST /api/pipeline/select-metadata<a class=headerlink href=#post-apipipelineselect-metadata title="Permanent link">&para;</a></h2> <p>Setzt Metadaten (und optional Playlist) für einen Job.</p> <p><strong>Request:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a><span class=p>{</span>
</span><span id=__span-4-2><a id=__codelineno-4-2 name=__codelineno-4-2 href=#__codelineno-4-2></a><span class=w> </span><span class=nt>&quot;jobId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>42</span><span class=p>,</span>
</span><span id=__span-4-3><a id=__codelineno-4-3 name=__codelineno-4-3 href=#__codelineno-4-3></a><span class=w> </span><span class=nt>&quot;title&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Inception&quot;</span><span class=p>,</span>
</span><span id=__span-4-4><a id=__codelineno-4-4 name=__codelineno-4-4 href=#__codelineno-4-4></a><span class=w> </span><span class=nt>&quot;year&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>2010</span><span class=p>,</span>
@@ -49,34 +76,82 @@
</span><span id=__span-4-7><a id=__codelineno-4-7 name=__codelineno-4-7 href=#__codelineno-4-7></a><span class=w> </span><span class=nt>&quot;fromOmdb&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-4-8><a id=__codelineno-4-8 name=__codelineno-4-8 href=#__codelineno-4-8></a><span class=w> </span><span class=nt>&quot;selectedPlaylist&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;00800&quot;</span>
</span><span id=__span-4-9><a id=__codelineno-4-9 name=__codelineno-4-9 href=#__codelineno-4-9></a><span class=p>}</span>
</span></code></pre></div> <p><strong>Response:</strong> <code>{ "job": { ... } }</code></p> <div class="admonition note"> <p class=admonition-title>Startlogik</p> <p>Nach Metadaten-Bestätigung wird der nächste Schritt automatisch ausgelöst (<code>startPreparedJob</code>). Der Job startet direkt oder wird in die Queue eingereiht.</p> </div> <hr> <h2 id=post-apipipelinestartjobid>POST /api/pipeline/start/:jobId<a class=headerlink href=#post-apipipelinestartjobid title="Permanent link">&para;</a></h2> <p>Startet einen vorbereiteten Job manuell (z. B. Fallback/Queue-Szenario).</p> <p><strong>Response (Beispiel):</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a><span class=p>{</span>
</span><span id=__span-5-2><a id=__codelineno-5-2 name=__codelineno-5-2 href=#__codelineno-5-2></a><span class=w> </span><span class=nt>&quot;result&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-5-3><a id=__codelineno-5-3 name=__codelineno-5-3 href=#__codelineno-5-3></a><span class=w> </span><span class=nt>&quot;started&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-5-4><a id=__codelineno-5-4 name=__codelineno-5-4 href=#__codelineno-5-4></a><span class=w> </span><span class=nt>&quot;stage&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;RIPPING&quot;</span>
</span><span id=__span-5-5><a id=__codelineno-5-5 name=__codelineno-5-5 href=#__codelineno-5-5></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-5-6><a id=__codelineno-5-6 name=__codelineno-5-6 href=#__codelineno-5-6></a><span class=p>}</span>
</span></code></pre></div> <p>Mögliche <code>stage</code>-Werte sind u. a. <code>RIPPING</code>, <code>MEDIAINFO_CHECK</code>, <code>ENCODING</code>.</p> <hr> <h2 id=post-apipipelineconfirm-encodejobid>POST /api/pipeline/confirm-encode/:jobId<a class=headerlink href=#post-apipipelineconfirm-encodejobid title="Permanent link">&para;</a></h2> <p>Bestätigt Review-Auswahl (Titel/Tracks/Post-Skripte).</p> <p><strong>Request:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-6-1><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a><span class=p>{</span>
</span><span id=__span-6-2><a id=__codelineno-6-2 name=__codelineno-6-2 href=#__codelineno-6-2></a><span class=w> </span><span class=nt>&quot;selectedEncodeTitleId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-6-3><a id=__codelineno-6-3 name=__codelineno-6-3 href=#__codelineno-6-3></a><span class=w> </span><span class=nt>&quot;selectedTrackSelection&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-6-4><a id=__codelineno-6-4 name=__codelineno-6-4 href=#__codelineno-6-4></a><span class=w> </span><span class=nt>&quot;1&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-6-5><a id=__codelineno-6-5 name=__codelineno-6-5 href=#__codelineno-6-5></a><span class=w> </span><span class=nt>&quot;audioTrackIds&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=mi>1</span><span class=p>,</span><span class=w> </span><span class=mi>2</span><span class=p>],</span>
</span><span id=__span-6-6><a id=__codelineno-6-6 name=__codelineno-6-6 href=#__codelineno-6-6></a><span class=w> </span><span class=nt>&quot;subtitleTrackIds&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=mi>3</span><span class=p>]</span>
</span><span id=__span-6-7><a id=__codelineno-6-7 name=__codelineno-6-7 href=#__codelineno-6-7></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-6-8><a id=__codelineno-6-8 name=__codelineno-6-8 href=#__codelineno-6-8></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-6-9><a id=__codelineno-6-9 name=__codelineno-6-9 href=#__codelineno-6-9></a><span class=w> </span><span class=nt>&quot;selectedPostEncodeScriptIds&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=mi>2</span><span class=p>,</span><span class=w> </span><span class=mi>7</span><span class=p>],</span>
</span><span id=__span-6-10><a id=__codelineno-6-10 name=__codelineno-6-10 href=#__codelineno-6-10></a><span class=w> </span><span class=nt>&quot;skipPipelineStateUpdate&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>false</span>
</span><span id=__span-6-11><a id=__codelineno-6-11 name=__codelineno-6-11 href=#__codelineno-6-11></a><span class=p>}</span>
</span></code></pre></div> <p><strong>Response:</strong> <code>{ "job": { ... } }</code></p> <hr> <h2 id=post-apipipelinecancel>POST /api/pipeline/cancel<a class=headerlink href=#post-apipipelinecancel title="Permanent link">&para;</a></h2> <p>Bricht laufenden Job ab oder entfernt einen Queue-Eintrag.</p> <p><strong>Request (optional):</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-7-1><a id=__codelineno-7-1 name=__codelineno-7-1 href=#__codelineno-7-1></a><span class=p>{</span>
</span><span id=__span-7-2><a id=__codelineno-7-2 name=__codelineno-7-2 href=#__codelineno-7-2></a><span class=w> </span><span class=nt>&quot;jobId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>42</span>
</span><span id=__span-7-3><a id=__codelineno-7-3 name=__codelineno-7-3 href=#__codelineno-7-3></a><span class=p>}</span>
</span></code></pre></div> <p><strong>Response (Beispiel):</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-8-1><a id=__codelineno-8-1 name=__codelineno-8-1 href=#__codelineno-8-1></a><span class=p>{</span>
</span><span id=__span-8-2><a id=__codelineno-8-2 name=__codelineno-8-2 href=#__codelineno-8-2></a><span class=w> </span><span class=nt>&quot;result&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-8-3><a id=__codelineno-8-3 name=__codelineno-8-3 href=#__codelineno-8-3></a><span class=w> </span><span class=nt>&quot;cancelled&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-8-4><a id=__codelineno-8-4 name=__codelineno-8-4 href=#__codelineno-8-4></a><span class=w> </span><span class=nt>&quot;queuedOnly&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>false</span><span class=p>,</span>
</span><span id=__span-8-5><a id=__codelineno-8-5 name=__codelineno-8-5 href=#__codelineno-8-5></a><span class=w> </span><span class=nt>&quot;jobId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>42</span>
</span><span id=__span-8-6><a id=__codelineno-8-6 name=__codelineno-8-6 href=#__codelineno-8-6></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-8-7><a id=__codelineno-8-7 name=__codelineno-8-7 href=#__codelineno-8-7></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=post-apipipelineretryjobid>POST /api/pipeline/retry/:jobId<a class=headerlink href=#post-apipipelineretryjobid title="Permanent link">&para;</a></h2> <p>Startet einen Job aus <code>ERROR</code>/<code>CANCELLED</code> erneut (oder reiht ihn in die Queue ein).</p> <p><strong>Response:</strong> <code>{ "result": { ... } }</code></p> <hr> <h2 id=post-apipipelineresume-readyjobid>POST /api/pipeline/resume-ready/:jobId<a class=headerlink href=#post-apipipelineresume-readyjobid title="Permanent link">&para;</a></h2> <p>Lädt einen <code>READY_TO_ENCODE</code>-Job nach Neustart wieder in die aktive Session.</p> <p><strong>Response:</strong> <code>{ "job": { ... } }</code></p> <hr> <h2 id=post-apipipelinereencodejobid>POST /api/pipeline/reencode/:jobId<a class=headerlink href=#post-apipipelinereencodejobid title="Permanent link">&para;</a></h2> <p>Startet Re-Encode aus bestehendem RAW.</p> <p><strong>Response:</strong> <code>{ "result": { ... } }</code></p> <hr> <h2 id=post-apipipelinerestart-reviewjobid>POST /api/pipeline/restart-review/:jobId<a class=headerlink href=#post-apipipelinerestart-reviewjobid title="Permanent link">&para;</a></h2> <p>Berechnet die Review aus vorhandenem RAW neu.</p> <p><strong>Response:</strong> <code>{ "result": { ... } }</code></p> <hr> <h2 id=post-apipipelinerestart-encodejobid>POST /api/pipeline/restart-encode/:jobId<a class=headerlink href=#post-apipipelinerestart-encodejobid title="Permanent link">&para;</a></h2> <p>Startet Encoding mit der zuletzt bestätigten Auswahl neu.</p> <p><strong>Response:</strong> <code>{ "result": { ... } }</code></p> <hr> <h2 id=queue-endpunkte>Queue-Endpunkte<a class=headerlink href=#queue-endpunkte title="Permanent link">&para;</a></h2> <h3 id=get-apipipelinequeue>GET /api/pipeline/queue<a class=headerlink href=#get-apipipelinequeue title="Permanent link">&para;</a></h3> <p>Liefert den aktuellen Queue-Status.</p> <p><strong>Response:</strong> <code>{ "queue": { ... } }</code></p> <h3 id=post-apipipelinequeuereorder>POST /api/pipeline/queue/reorder<a class=headerlink href=#post-apipipelinequeuereorder title="Permanent link">&para;</a></h3> <p>Sortiert Queue-Einträge neu.</p> <p><strong>Request:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-9-1><a id=__codelineno-9-1 name=__codelineno-9-1 href=#__codelineno-9-1></a><span class=p>{</span>
</span><span id=__span-9-2><a id=__codelineno-9-2 name=__codelineno-9-2 href=#__codelineno-9-2></a><span class=w> </span><span class=nt>&quot;orderedJobIds&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=mi>42</span><span class=p>,</span><span class=w> </span><span class=mi>43</span><span class=p>,</span><span class=w> </span><span class=mi>41</span><span class=p>]</span>
</span><span id=__span-9-3><a id=__codelineno-9-3 name=__codelineno-9-3 href=#__codelineno-9-3></a><span class=p>}</span>
</span></code></pre></div> <p><strong>Response:</strong> <code>{ "queue": { ... } }</code></p> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
</span></code></pre></div> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;job&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;id&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>42</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;status&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;READY_TO_START&quot;</span><span class=w> </span><span class=p>}</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=post-apipipelinestartjobid>POST /api/pipeline/start/:jobId<a class=headerlink href=#post-apipipelinestartjobid title="Permanent link">&para;</a></h2> <p>Startet vorbereiteten Job oder queued ihn (je nach Parallel-Limit).</p> <p><strong>Mögliche Responses:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-6-1><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;result&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;started&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;stage&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;RIPPING&quot;</span><span class=w> </span><span class=p>}</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <div class="language-json highlight"><pre><span></span><code><span id=__span-7-1><a id=__codelineno-7-1 name=__codelineno-7-1 href=#__codelineno-7-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;result&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;queued&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;started&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>false</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;queuePosition&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>2</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;action&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;START_PREPARED&quot;</span><span class=w> </span><span class=p>}</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=post-apipipelineconfirm-encodejobid>POST /api/pipeline/confirm-encode/:jobId<a class=headerlink href=#post-apipipelineconfirm-encodejobid title="Permanent link">&para;</a></h2> <p>Bestätigt Review-Auswahl (Tracks, Pre/Post-Skripte/Ketten, User-Preset).</p> <p><strong>Request (typisch):</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-8-1><a id=__codelineno-8-1 name=__codelineno-8-1 href=#__codelineno-8-1></a><span class=p>{</span>
</span><span id=__span-8-2><a id=__codelineno-8-2 name=__codelineno-8-2 href=#__codelineno-8-2></a><span class=w> </span><span class=nt>&quot;selectedEncodeTitleId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-8-3><a id=__codelineno-8-3 name=__codelineno-8-3 href=#__codelineno-8-3></a><span class=w> </span><span class=nt>&quot;selectedTrackSelection&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-8-4><a id=__codelineno-8-4 name=__codelineno-8-4 href=#__codelineno-8-4></a><span class=w> </span><span class=nt>&quot;1&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-8-5><a id=__codelineno-8-5 name=__codelineno-8-5 href=#__codelineno-8-5></a><span class=w> </span><span class=nt>&quot;audioTrackIds&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=mi>1</span><span class=p>,</span><span class=w> </span><span class=mi>2</span><span class=p>],</span>
</span><span id=__span-8-6><a id=__codelineno-8-6 name=__codelineno-8-6 href=#__codelineno-8-6></a><span class=w> </span><span class=nt>&quot;subtitleTrackIds&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=mi>3</span><span class=p>]</span>
</span><span id=__span-8-7><a id=__codelineno-8-7 name=__codelineno-8-7 href=#__codelineno-8-7></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-8-8><a id=__codelineno-8-8 name=__codelineno-8-8 href=#__codelineno-8-8></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-8-9><a id=__codelineno-8-9 name=__codelineno-8-9 href=#__codelineno-8-9></a><span class=w> </span><span class=nt>&quot;selectedPreEncodeScriptIds&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=mi>1</span><span class=p>],</span>
</span><span id=__span-8-10><a id=__codelineno-8-10 name=__codelineno-8-10 href=#__codelineno-8-10></a><span class=w> </span><span class=nt>&quot;selectedPostEncodeScriptIds&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=mi>2</span><span class=p>,</span><span class=w> </span><span class=mi>7</span><span class=p>],</span>
</span><span id=__span-8-11><a id=__codelineno-8-11 name=__codelineno-8-11 href=#__codelineno-8-11></a><span class=w> </span><span class=nt>&quot;selectedPreEncodeChainIds&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=mi>3</span><span class=p>],</span>
</span><span id=__span-8-12><a id=__codelineno-8-12 name=__codelineno-8-12 href=#__codelineno-8-12></a><span class=w> </span><span class=nt>&quot;selectedPostEncodeChainIds&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=mi>4</span><span class=p>],</span>
</span><span id=__span-8-13><a id=__codelineno-8-13 name=__codelineno-8-13 href=#__codelineno-8-13></a><span class=w> </span><span class=nt>&quot;selectedUserPresetId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>5</span><span class=p>,</span>
</span><span id=__span-8-14><a id=__codelineno-8-14 name=__codelineno-8-14 href=#__codelineno-8-14></a><span class=w> </span><span class=nt>&quot;skipPipelineStateUpdate&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>false</span>
</span><span id=__span-8-15><a id=__codelineno-8-15 name=__codelineno-8-15 href=#__codelineno-8-15></a><span class=p>}</span>
</span></code></pre></div> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-9-1><a id=__codelineno-9-1 name=__codelineno-9-1 href=#__codelineno-9-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;job&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;id&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>42</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;encode_review_confirmed&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=w> </span><span class=p>}</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=post-apipipelinecancel>POST /api/pipeline/cancel<a class=headerlink href=#post-apipipelinecancel title="Permanent link">&para;</a></h2> <p>Bricht laufenden Job ab oder entfernt Queue-Eintrag.</p> <p><strong>Request (optional):</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-10-1><a id=__codelineno-10-1 name=__codelineno-10-1 href=#__codelineno-10-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;jobId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>42</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <p><strong>Mögliche Responses:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-11-1><a id=__codelineno-11-1 name=__codelineno-11-1 href=#__codelineno-11-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;result&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;cancelled&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;queuedOnly&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;jobId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>42</span><span class=w> </span><span class=p>}</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <div class="language-json highlight"><pre><span></span><code><span id=__span-12-1><a id=__codelineno-12-1 name=__codelineno-12-1 href=#__codelineno-12-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;result&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;cancelled&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;queuedOnly&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>false</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;jobId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>42</span><span class=w> </span><span class=p>}</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <div class="language-json highlight"><pre><span></span><code><span id=__span-13-1><a id=__codelineno-13-1 name=__codelineno-13-1 href=#__codelineno-13-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;result&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;cancelled&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;queuedOnly&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>false</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;pending&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;jobId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>42</span><span class=w> </span><span class=p>}</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=post-apipipelineretryjobid>POST /api/pipeline/retry/:jobId<a class=headerlink href=#post-apipipelineretryjobid title="Permanent link">&para;</a></h2> <p>Retry für <code>ERROR</code>/<code>CANCELLED</code>-Jobs (oder Queue-Einreihung).</p> <h2 id=post-apipipelinereencodejobid>POST /api/pipeline/reencode/:jobId<a class=headerlink href=#post-apipipelinereencodejobid title="Permanent link">&para;</a></h2> <p>Startet Re-Encode aus bestehendem RAW.</p> <h2 id=post-apipipelinerestart-reviewjobid>POST /api/pipeline/restart-review/:jobId<a class=headerlink href=#post-apipipelinerestart-reviewjobid title="Permanent link">&para;</a></h2> <p>Berechnet Review aus RAW neu.</p> <h2 id=post-apipipelinerestart-encodejobid>POST /api/pipeline/restart-encode/:jobId<a class=headerlink href=#post-apipipelinerestart-encodejobid title="Permanent link">&para;</a></h2> <p>Startet Encoding mit letzter bestätigter Review neu.</p> <h2 id=post-apipipelineresume-readyjobid>POST /api/pipeline/resume-ready/:jobId<a class=headerlink href=#post-apipipelineresume-readyjobid title="Permanent link">&para;</a></h2> <p>Lädt <code>READY_TO_ENCODE</code>-Job nach Neustart wieder in aktive Session.</p> <p>Alle Endpunkte liefern <code>{ result: ... }</code> bzw. <code>{ job: ... }</code>.</p> <hr> <h2 id=queue-endpunkte>Queue-Endpunkte<a class=headerlink href=#queue-endpunkte title="Permanent link">&para;</a></h2> <h3 id=get-apipipelinequeue>GET /api/pipeline/queue<a class=headerlink href=#get-apipipelinequeue title="Permanent link">&para;</a></h3> <p>Liefert Queue-Snapshot.</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-14-1><a id=__codelineno-14-1 name=__codelineno-14-1 href=#__codelineno-14-1></a><span class=p>{</span>
</span><span id=__span-14-2><a id=__codelineno-14-2 name=__codelineno-14-2 href=#__codelineno-14-2></a><span class=w> </span><span class=nt>&quot;queue&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-14-3><a id=__codelineno-14-3 name=__codelineno-14-3 href=#__codelineno-14-3></a><span class=w> </span><span class=nt>&quot;maxParallelJobs&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-14-4><a id=__codelineno-14-4 name=__codelineno-14-4 href=#__codelineno-14-4></a><span class=w> </span><span class=nt>&quot;runningCount&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-14-5><a id=__codelineno-14-5 name=__codelineno-14-5 href=#__codelineno-14-5></a><span class=w> </span><span class=nt>&quot;queuedCount&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>3</span><span class=p>,</span>
</span><span id=__span-14-6><a id=__codelineno-14-6 name=__codelineno-14-6 href=#__codelineno-14-6></a><span class=w> </span><span class=nt>&quot;runningJobs&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-14-7><a id=__codelineno-14-7 name=__codelineno-14-7 href=#__codelineno-14-7></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-14-8><a id=__codelineno-14-8 name=__codelineno-14-8 href=#__codelineno-14-8></a><span class=w> </span><span class=nt>&quot;jobId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>41</span><span class=p>,</span>
</span><span id=__span-14-9><a id=__codelineno-14-9 name=__codelineno-14-9 href=#__codelineno-14-9></a><span class=w> </span><span class=nt>&quot;title&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Inception&quot;</span><span class=p>,</span>
</span><span id=__span-14-10><a id=__codelineno-14-10 name=__codelineno-14-10 href=#__codelineno-14-10></a><span class=w> </span><span class=nt>&quot;status&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;ENCODING&quot;</span><span class=p>,</span>
</span><span id=__span-14-11><a id=__codelineno-14-11 name=__codelineno-14-11 href=#__codelineno-14-11></a><span class=w> </span><span class=nt>&quot;lastState&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;ENCODING&quot;</span>
</span><span id=__span-14-12><a id=__codelineno-14-12 name=__codelineno-14-12 href=#__codelineno-14-12></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-14-13><a id=__codelineno-14-13 name=__codelineno-14-13 href=#__codelineno-14-13></a><span class=w> </span><span class=p>],</span>
</span><span id=__span-14-14><a id=__codelineno-14-14 name=__codelineno-14-14 href=#__codelineno-14-14></a><span class=w> </span><span class=nt>&quot;queuedJobs&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-14-15><a id=__codelineno-14-15 name=__codelineno-14-15 href=#__codelineno-14-15></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-14-16><a id=__codelineno-14-16 name=__codelineno-14-16 href=#__codelineno-14-16></a><span class=w> </span><span class=nt>&quot;entryId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>11</span><span class=p>,</span>
</span><span id=__span-14-17><a id=__codelineno-14-17 name=__codelineno-14-17 href=#__codelineno-14-17></a><span class=w> </span><span class=nt>&quot;position&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-14-18><a id=__codelineno-14-18 name=__codelineno-14-18 href=#__codelineno-14-18></a><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;job&quot;</span><span class=p>,</span>
</span><span id=__span-14-19><a id=__codelineno-14-19 name=__codelineno-14-19 href=#__codelineno-14-19></a><span class=w> </span><span class=nt>&quot;jobId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>42</span><span class=p>,</span>
</span><span id=__span-14-20><a id=__codelineno-14-20 name=__codelineno-14-20 href=#__codelineno-14-20></a><span class=w> </span><span class=nt>&quot;action&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;START_PREPARED&quot;</span><span class=p>,</span>
</span><span id=__span-14-21><a id=__codelineno-14-21 name=__codelineno-14-21 href=#__codelineno-14-21></a><span class=w> </span><span class=nt>&quot;actionLabel&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Start&quot;</span><span class=p>,</span>
</span><span id=__span-14-22><a id=__codelineno-14-22 name=__codelineno-14-22 href=#__codelineno-14-22></a><span class=w> </span><span class=nt>&quot;title&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Matrix&quot;</span><span class=p>,</span>
</span><span id=__span-14-23><a id=__codelineno-14-23 name=__codelineno-14-23 href=#__codelineno-14-23></a><span class=w> </span><span class=nt>&quot;status&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;READY_TO_ENCODE&quot;</span><span class=p>,</span>
</span><span id=__span-14-24><a id=__codelineno-14-24 name=__codelineno-14-24 href=#__codelineno-14-24></a><span class=w> </span><span class=nt>&quot;lastState&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;READY_TO_ENCODE&quot;</span><span class=p>,</span>
</span><span id=__span-14-25><a id=__codelineno-14-25 name=__codelineno-14-25 href=#__codelineno-14-25></a><span class=w> </span><span class=nt>&quot;hasScripts&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-14-26><a id=__codelineno-14-26 name=__codelineno-14-26 href=#__codelineno-14-26></a><span class=w> </span><span class=nt>&quot;hasChains&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>false</span><span class=p>,</span>
</span><span id=__span-14-27><a id=__codelineno-14-27 name=__codelineno-14-27 href=#__codelineno-14-27></a><span class=w> </span><span class=nt>&quot;enqueuedAt&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2026-03-10T09:00:00.000Z&quot;</span>
</span><span id=__span-14-28><a id=__codelineno-14-28 name=__codelineno-14-28 href=#__codelineno-14-28></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-14-29><a id=__codelineno-14-29 name=__codelineno-14-29 href=#__codelineno-14-29></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-14-30><a id=__codelineno-14-30 name=__codelineno-14-30 href=#__codelineno-14-30></a><span class=w> </span><span class=nt>&quot;entryId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>12</span><span class=p>,</span>
</span><span id=__span-14-31><a id=__codelineno-14-31 name=__codelineno-14-31 href=#__codelineno-14-31></a><span class=w> </span><span class=nt>&quot;position&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>2</span><span class=p>,</span>
</span><span id=__span-14-32><a id=__codelineno-14-32 name=__codelineno-14-32 href=#__codelineno-14-32></a><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;wait&quot;</span><span class=p>,</span>
</span><span id=__span-14-33><a id=__codelineno-14-33 name=__codelineno-14-33 href=#__codelineno-14-33></a><span class=w> </span><span class=nt>&quot;waitSeconds&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>30</span><span class=p>,</span>
</span><span id=__span-14-34><a id=__codelineno-14-34 name=__codelineno-14-34 href=#__codelineno-14-34></a><span class=w> </span><span class=nt>&quot;title&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Warten 30s&quot;</span><span class=p>,</span>
</span><span id=__span-14-35><a id=__codelineno-14-35 name=__codelineno-14-35 href=#__codelineno-14-35></a><span class=w> </span><span class=nt>&quot;status&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;QUEUED&quot;</span><span class=p>,</span>
</span><span id=__span-14-36><a id=__codelineno-14-36 name=__codelineno-14-36 href=#__codelineno-14-36></a><span class=w> </span><span class=nt>&quot;enqueuedAt&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2026-03-10T09:01:00.000Z&quot;</span>
</span><span id=__span-14-37><a id=__codelineno-14-37 name=__codelineno-14-37 href=#__codelineno-14-37></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-14-38><a id=__codelineno-14-38 name=__codelineno-14-38 href=#__codelineno-14-38></a><span class=w> </span><span class=p>],</span>
</span><span id=__span-14-39><a id=__codelineno-14-39 name=__codelineno-14-39 href=#__codelineno-14-39></a><span class=w> </span><span class=nt>&quot;updatedAt&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2026-03-10T09:01:02.000Z&quot;</span>
</span><span id=__span-14-40><a id=__codelineno-14-40 name=__codelineno-14-40 href=#__codelineno-14-40></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-14-41><a id=__codelineno-14-41 name=__codelineno-14-41 href=#__codelineno-14-41></a><span class=p>}</span>
</span></code></pre></div> <h3 id=post-apipipelinequeuereorder>POST /api/pipeline/queue/reorder<a class=headerlink href=#post-apipipelinequeuereorder title="Permanent link">&para;</a></h3> <p>Sortiert Queue-Einträge neu.</p> <p><strong>Request:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-15-1><a id=__codelineno-15-1 name=__codelineno-15-1 href=#__codelineno-15-1></a><span class=p>{</span>
</span><span id=__span-15-2><a id=__codelineno-15-2 name=__codelineno-15-2 href=#__codelineno-15-2></a><span class=w> </span><span class=nt>&quot;orderedEntryIds&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=mi>12</span><span class=p>,</span><span class=w> </span><span class=mi>11</span><span class=p>]</span>
</span><span id=__span-15-3><a id=__codelineno-15-3 name=__codelineno-15-3 href=#__codelineno-15-3></a><span class=p>}</span>
</span></code></pre></div> <p>Legacy fallback wird akzeptiert:</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-16-1><a id=__codelineno-16-1 name=__codelineno-16-1 href=#__codelineno-16-1></a><span class=p>{</span>
</span><span id=__span-16-2><a id=__codelineno-16-2 name=__codelineno-16-2 href=#__codelineno-16-2></a><span class=w> </span><span class=nt>&quot;orderedJobIds&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=mi>42</span><span class=p>,</span><span class=w> </span><span class=mi>43</span><span class=p>]</span>
</span><span id=__span-16-3><a id=__codelineno-16-3 name=__codelineno-16-3 href=#__codelineno-16-3></a><span class=p>}</span>
</span></code></pre></div> <h3 id=post-apipipelinequeueentry>POST /api/pipeline/queue/entry<a class=headerlink href=#post-apipipelinequeueentry title="Permanent link">&para;</a></h3> <p>Fügt Nicht-Job-Queue-Eintrag hinzu (<code>script</code>, <code>chain</code>, <code>wait</code>).</p> <p><strong>Request-Beispiele:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-17-1><a id=__codelineno-17-1 name=__codelineno-17-1 href=#__codelineno-17-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;script&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;scriptId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>3</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <div class="language-json highlight"><pre><span></span><code><span id=__span-18-1><a id=__codelineno-18-1 name=__codelineno-18-1 href=#__codelineno-18-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;chain&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;chainId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>2</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;insertAfterEntryId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>11</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <div class="language-json highlight"><pre><span></span><code><span id=__span-19-1><a id=__codelineno-19-1 name=__codelineno-19-1 href=#__codelineno-19-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;wait&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;waitSeconds&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>45</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-20-1><a id=__codelineno-20-1 name=__codelineno-20-1 href=#__codelineno-20-1></a><span class=p>{</span>
</span><span id=__span-20-2><a id=__codelineno-20-2 name=__codelineno-20-2 href=#__codelineno-20-2></a><span class=w> </span><span class=nt>&quot;result&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;entryId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>12</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;wait&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;position&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>2</span><span class=w> </span><span class=p>},</span>
</span><span id=__span-20-3><a id=__codelineno-20-3 name=__codelineno-20-3 href=#__codelineno-20-3></a><span class=w> </span><span class=nt>&quot;queue&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;...&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;...&quot;</span><span class=w> </span><span class=p>}</span>
</span><span id=__span-20-4><a id=__codelineno-20-4 name=__codelineno-20-4 href=#__codelineno-20-4></a><span class=p>}</span>
</span></code></pre></div> <h3 id=delete-apipipelinequeueentryentryid>DELETE /api/pipeline/queue/entry/:entryId<a class=headerlink href=#delete-apipipelinequeueentryentryid title="Permanent link">&para;</a></h3> <p>Entfernt Queue-Eintrag.</p> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-21-1><a id=__codelineno-21-1 name=__codelineno-21-1 href=#__codelineno-21-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;queue&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;...&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;...&quot;</span><span class=w> </span><span class=p>}</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=pipeline-zustande>Pipeline-Zustände<a class=headerlink href=#pipeline-zustande title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>State</th> <th>Bedeutung</th> </tr> </thead> <tbody> <tr> <td><code>IDLE</code></td> <td>Wartet auf Medium</td> </tr> <tr> <td><code>DISC_DETECTED</code></td> <td>Medium erkannt</td> </tr> <tr> <td><code>ANALYZING</code></td> <td>MakeMKV-Analyse läuft</td> </tr> <tr> <td><code>METADATA_SELECTION</code></td> <td>Metadaten-Auswahl</td> </tr> <tr> <td><code>WAITING_FOR_USER_DECISION</code></td> <td>Playlist-Entscheidung erforderlich</td> </tr> <tr> <td><code>READY_TO_START</code></td> <td>Übergang vor Start</td> </tr> <tr> <td><code>RIPPING</code></td> <td>MakeMKV-Rip läuft</td> </tr> <tr> <td><code>MEDIAINFO_CHECK</code></td> <td>Titel-/Track-Auswertung</td> </tr> <tr> <td><code>READY_TO_ENCODE</code></td> <td>Review bereit</td> </tr> <tr> <td><code>ENCODING</code></td> <td>HandBrake-Encoding läuft</td> </tr> <tr> <td><code>FINISHED</code></td> <td>Abgeschlossen</td> </tr> <tr> <td><code>CANCELLED</code></td> <td>Abgebrochen</td> </tr> <tr> <td><code>ERROR</code></td> <td>Fehler</td> </tr> </tbody> </table> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.resLine truncated
+128 -78
View File
@@ -1,79 +1,129 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/api/settings/ rel=canonical><link href=../pipeline/ rel=prev><link href=../history/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Settings API - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#settings-api class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Settings API </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.Line truncated
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a><span class=w> </span><span class=nt>&quot;paths&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-0-3><a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a><span class=w> </span><span class=nt>&quot;raw_dir&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-0-4><a id=__codelineno-0-4 name=__codelineno-0-4 href=#__codelineno-0-4></a><span class=w> </span><span class=nt>&quot;value&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/mnt/nas/raw&quot;</span><span class=p>,</span>
</span><span id=__span-0-5><a id=__codelineno-0-5 name=__codelineno-0-5 href=#__codelineno-0-5></a><span class=w> </span><span class=nt>&quot;schema&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-0-6><a id=__codelineno-0-6 name=__codelineno-0-6 href=#__codelineno-0-6></a><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;string&quot;</span><span class=p>,</span>
</span><span id=__span-0-7><a id=__codelineno-0-7 name=__codelineno-0-7 href=#__codelineno-0-7></a><span class=w> </span><span class=nt>&quot;label&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Raw-Verzeichnis&quot;</span><span class=p>,</span>
</span><span id=__span-0-8><a id=__codelineno-0-8 name=__codelineno-0-8 href=#__codelineno-0-8></a><span class=w> </span><span class=nt>&quot;description&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Speicherort für rohe MKV-Dateien&quot;</span><span class=p>,</span>
</span><span id=__span-0-9><a id=__codelineno-0-9 name=__codelineno-0-9 href=#__codelineno-0-9></a><span class=w> </span><span class=nt>&quot;required&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span>
</span><span id=__span-0-10><a id=__codelineno-0-10 name=__codelineno-0-10 href=#__codelineno-0-10></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-0-11><a id=__codelineno-0-11 name=__codelineno-0-11 href=#__codelineno-0-11></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-0-12><a id=__codelineno-0-12 name=__codelineno-0-12 href=#__codelineno-0-12></a><span class=w> </span><span class=nt>&quot;movie_dir&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-0-13><a id=__codelineno-0-13 name=__codelineno-0-13 href=#__codelineno-0-13></a><span class=w> </span><span class=nt>&quot;value&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/mnt/nas/movies&quot;</span><span class=p>,</span>
</span><span id=__span-0-14><a id=__codelineno-0-14 name=__codelineno-0-14 href=#__codelineno-0-14></a><span class=w> </span><span class=nt>&quot;schema&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=err>...</span><span class=w> </span><span class=p>}</span>
</span><span id=__span-0-15><a id=__codelineno-0-15 name=__codelineno-0-15 href=#__codelineno-0-15></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-0-16><a id=__codelineno-0-16 name=__codelineno-0-16 href=#__codelineno-0-16></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-0-17><a id=__codelineno-0-17 name=__codelineno-0-17 href=#__codelineno-0-17></a><span class=w> </span><span class=nt>&quot;tools&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=err>...</span><span class=w> </span><span class=p>},</span>
</span><span id=__span-0-18><a id=__codelineno-0-18 name=__codelineno-0-18 href=#__codelineno-0-18></a><span class=w> </span><span class=nt>&quot;encoding&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=err>...</span><span class=w> </span><span class=p>},</span>
</span><span id=__span-0-19><a id=__codelineno-0-19 name=__codelineno-0-19 href=#__codelineno-0-19></a><span class=w> </span><span class=nt>&quot;drive&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=err>...</span><span class=w> </span><span class=p>},</span>
</span><span id=__span-0-20><a id=__codelineno-0-20 name=__codelineno-0-20 href=#__codelineno-0-20></a><span class=w> </span><span class=nt>&quot;makemkv&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=err>...</span><span class=w> </span><span class=p>},</span>
</span><span id=__span-0-21><a id=__codelineno-0-21 name=__codelineno-0-21 href=#__codelineno-0-21></a><span class=w> </span><span class=nt>&quot;omdb&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=err>...</span><span class=w> </span><span class=p>},</span>
</span><span id=__span-0-22><a id=__codelineno-0-22 name=__codelineno-0-22 href=#__codelineno-0-22></a><span class=w> </span><span class=nt>&quot;notifications&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=err>...</span><span class=w> </span><span class=p>}</span>
</span><span id=__span-0-23><a id=__codelineno-0-23 name=__codelineno-0-23 href=#__codelineno-0-23></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=put-apisettingskey>PUT /api/settings/:key<a class=headerlink href=#put-apisettingskey title="Permanent link">&para;</a></h2> <p>Aktualisiert eine einzelne Einstellung.</p> <p><strong>URL-Parameter:</strong> <code>key</code> – Einstellungs-Schlüssel</p> <p><strong>Request:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a><span class=p>{</span>
</span><span id=__span-1-2><a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a><span class=w> </span><span class=nt>&quot;value&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/mnt/storage/raw&quot;</span>
</span><span id=__span-1-3><a id=__codelineno-1-3 name=__codelineno-1-3 href=#__codelineno-1-3></a><span class=p>}</span>
</span></code></pre></div> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;ok&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;key&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;raw_dir&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;value&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/mnt/storage/raw&quot;</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <p><strong>Fehlerfälle:</strong> - <code>400</code> – Ungültiger Wert (Validierungsfehler) - <code>404</code> – Einstellung nicht gefunden</p> <div class="admonition note"> <p class=admonition-title>Encode-Review-Refresh</p> <p>Wenn eine encoding-relevante Einstellung geändert wird (z.B. <code>handbrake_preset</code>), wird der Encode-Plan für den aktuell wartenden Job automatisch neu berechnet.</p> </div> <hr> <h2 id=put-apisettings>PUT /api/settings<a class=headerlink href=#put-apisettings title="Permanent link">&para;</a></h2> <p>Aktualisiert mehrere Einstellungen auf einmal.</p> <p><strong>Request:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a><span class=p>{</span>
</span><span id=__span-3-2><a id=__codelineno-3-2 name=__codelineno-3-2 href=#__codelineno-3-2></a><span class=w> </span><span class=nt>&quot;raw_dir&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/mnt/storage/raw&quot;</span><span class=p>,</span>
</span><span id=__span-3-3><a id=__codelineno-3-3 name=__codelineno-3-3 href=#__codelineno-3-3></a><span class=w> </span><span class=nt>&quot;movie_dir&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/mnt/storage/movies&quot;</span><span class=p>,</span>
</span><span id=__span-3-4><a id=__codelineno-3-4 name=__codelineno-3-4 href=#__codelineno-3-4></a><span class=w> </span><span class=nt>&quot;handbrake_preset&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;H.265 MKV 720p30&quot;</span>
</span><span id=__span-3-5><a id=__codelineno-3-5 name=__codelineno-3-5 href=#__codelineno-3-5></a><span class=p>}</span>
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/api/settings/ rel=canonical><link href=../pipeline/ rel=prev><link href=../history/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Settings API - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#settings-api class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Settings API </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.Line truncated
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a><span class=w> </span><span class=nt>&quot;categories&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-0-3><a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-0-4><a id=__codelineno-0-4 name=__codelineno-0-4 href=#__codelineno-0-4></a><span class=w> </span><span class=nt>&quot;category&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Pfade&quot;</span><span class=p>,</span>
</span><span id=__span-0-5><a id=__codelineno-0-5 name=__codelineno-0-5 href=#__codelineno-0-5></a><span class=w> </span><span class=nt>&quot;settings&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-0-6><a id=__codelineno-0-6 name=__codelineno-0-6 href=#__codelineno-0-6></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-0-7><a id=__codelineno-0-7 name=__codelineno-0-7 href=#__codelineno-0-7></a><span class=w> </span><span class=nt>&quot;key&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;raw_dir&quot;</span><span class=p>,</span>
</span><span id=__span-0-8><a id=__codelineno-0-8 name=__codelineno-0-8 href=#__codelineno-0-8></a><span class=w> </span><span class=nt>&quot;label&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Raw Ausgabeordner&quot;</span><span class=p>,</span>
</span><span id=__span-0-9><a id=__codelineno-0-9 name=__codelineno-0-9 href=#__codelineno-0-9></a><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;path&quot;</span><span class=p>,</span>
</span><span id=__span-0-10><a id=__codelineno-0-10 name=__codelineno-0-10 href=#__codelineno-0-10></a><span class=w> </span><span class=nt>&quot;required&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-0-11><a id=__codelineno-0-11 name=__codelineno-0-11 href=#__codelineno-0-11></a><span class=w> </span><span class=nt>&quot;description&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;...&quot;</span><span class=p>,</span>
</span><span id=__span-0-12><a id=__codelineno-0-12 name=__codelineno-0-12 href=#__codelineno-0-12></a><span class=w> </span><span class=nt>&quot;defaultValue&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;data/output/raw&quot;</span><span class=p>,</span>
</span><span id=__span-0-13><a id=__codelineno-0-13 name=__codelineno-0-13 href=#__codelineno-0-13></a><span class=w> </span><span class=nt>&quot;options&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[],</span>
</span><span id=__span-0-14><a id=__codelineno-0-14 name=__codelineno-0-14 href=#__codelineno-0-14></a><span class=w> </span><span class=nt>&quot;validation&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;minLength&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=w> </span><span class=p>},</span>
</span><span id=__span-0-15><a id=__codelineno-0-15 name=__codelineno-0-15 href=#__codelineno-0-15></a><span class=w> </span><span class=nt>&quot;value&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;data/output/raw&quot;</span><span class=p>,</span>
</span><span id=__span-0-16><a id=__codelineno-0-16 name=__codelineno-0-16 href=#__codelineno-0-16></a><span class=w> </span><span class=nt>&quot;orderIndex&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>100</span>
</span><span id=__span-0-17><a id=__codelineno-0-17 name=__codelineno-0-17 href=#__codelineno-0-17></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-0-18><a id=__codelineno-0-18 name=__codelineno-0-18 href=#__codelineno-0-18></a><span class=w> </span><span class=p>]</span>
</span><span id=__span-0-19><a id=__codelineno-0-19 name=__codelineno-0-19 href=#__codelineno-0-19></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-0-20><a id=__codelineno-0-20 name=__codelineno-0-20 href=#__codelineno-0-20></a><span class=w> </span><span class=p>]</span>
</span><span id=__span-0-21><a id=__codelineno-0-21 name=__codelineno-0-21 href=#__codelineno-0-21></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=put-apisettingskey>PUT /api/settings/:key<a class=headerlink href=#put-apisettingskey title="Permanent link">&para;</a></h2> <p>Aktualisiert eine einzelne Einstellung.</p> <p><strong>Request:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;value&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/mnt/storage/raw&quot;</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a><span class=p>{</span>
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a><span class=w> </span><span class=nt>&quot;setting&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-2-3><a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a><span class=w> </span><span class=nt>&quot;key&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;raw_dir&quot;</span><span class=p>,</span>
</span><span id=__span-2-4><a id=__codelineno-2-4 name=__codelineno-2-4 href=#__codelineno-2-4></a><span class=w> </span><span class=nt>&quot;value&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/mnt/storage/raw&quot;</span>
</span><span id=__span-2-5><a id=__codelineno-2-5 name=__codelineno-2-5 href=#__codelineno-2-5></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-2-6><a id=__codelineno-2-6 name=__codelineno-2-6 href=#__codelineno-2-6></a><span class=w> </span><span class=nt>&quot;reviewRefresh&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-2-7><a id=__codelineno-2-7 name=__codelineno-2-7 href=#__codelineno-2-7></a><span class=w> </span><span class=nt>&quot;triggered&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>false</span><span class=p>,</span>
</span><span id=__span-2-8><a id=__codelineno-2-8 name=__codelineno-2-8 href=#__codelineno-2-8></a><span class=w> </span><span class=nt>&quot;reason&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;not_ready&quot;</span>
</span><span id=__span-2-9><a id=__codelineno-2-9 name=__codelineno-2-9 href=#__codelineno-2-9></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-2-10><a id=__codelineno-2-10 name=__codelineno-2-10 href=#__codelineno-2-10></a><span class=p>}</span>
</span></code></pre></div> <p><code>reviewRefresh</code> ist <code>null</code> oder ein Objekt mit Status der optionalen Review-Neuberechnung.</p> <hr> <h2 id=put-apisettings>PUT /api/settings<a class=headerlink href=#put-apisettings title="Permanent link">&para;</a></h2> <p>Aktualisiert mehrere Einstellungen atomar.</p> <p><strong>Request:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a><span class=p>{</span>
</span><span id=__span-3-2><a id=__codelineno-3-2 name=__codelineno-3-2 href=#__codelineno-3-2></a><span class=w> </span><span class=nt>&quot;settings&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-3-3><a id=__codelineno-3-3 name=__codelineno-3-3 href=#__codelineno-3-3></a><span class=w> </span><span class=nt>&quot;raw_dir&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/mnt/storage/raw&quot;</span><span class=p>,</span>
</span><span id=__span-3-4><a id=__codelineno-3-4 name=__codelineno-3-4 href=#__codelineno-3-4></a><span class=w> </span><span class=nt>&quot;movie_dir&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/mnt/storage/movies&quot;</span><span class=p>,</span>
</span><span id=__span-3-5><a id=__codelineno-3-5 name=__codelineno-3-5 href=#__codelineno-3-5></a><span class=w> </span><span class=nt>&quot;handbrake_preset_bluray&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;H.264 MKV 1080p30&quot;</span>
</span><span id=__span-3-6><a id=__codelineno-3-6 name=__codelineno-3-6 href=#__codelineno-3-6></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-3-7><a id=__codelineno-3-7 name=__codelineno-3-7 href=#__codelineno-3-7></a><span class=p>}</span>
</span></code></pre></div> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a><span class=p>{</span>
</span><span id=__span-4-2><a id=__codelineno-4-2 name=__codelineno-4-2 href=#__codelineno-4-2></a><span class=w> </span><span class=nt>&quot;ok&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-4-3><a id=__codelineno-4-3 name=__codelineno-4-3 href=#__codelineno-4-3></a><span class=w> </span><span class=nt>&quot;updated&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=s2>&quot;raw_dir&quot;</span><span class=p>,</span><span class=w> </span><span class=s2>&quot;movie_dir&quot;</span><span class=p>,</span><span class=w> </span><span class=s2>&quot;handbrake_preset&quot;</span><span class=p>],</span>
</span><span id=__span-4-4><a id=__codelineno-4-4 name=__codelineno-4-4 href=#__codelineno-4-4></a><span class=w> </span><span class=nt>&quot;errors&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[]</span>
</span><span id=__span-4-5><a id=__codelineno-4-5 name=__codelineno-4-5 href=#__codelineno-4-5></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=post-apisettingspushovertest>POST /api/settings/pushover/test<a class=headerlink href=#post-apisettingspushovertest title="Permanent link">&para;</a></h2> <p>Sendet eine Test-Benachrichtigung über PushOver.</p> <p><strong>Request:</strong> Kein Body erforderlich (verwendet gespeicherte Zugangsdaten)</p> <p><strong>Response (Erfolg):</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;ok&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;message&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Test-Benachrichtigung gesendet&quot;</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <p><strong>Response (Fehler):</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-6-1><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;ok&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>false</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;error&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Ungültiger API-Token&quot;</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=skript-verwaltung>Skript-Verwaltung<a class=headerlink href=#skript-verwaltung title="Permanent link">&para;</a></h2> <p>Post-Encode-Skripte werden über eigene Endpunkte unter <code>/api/settings/scripts</code> verwaltet.</p> <h3 id=get-apisettingsscripts>GET /api/settings/scripts<a class=headerlink href=#get-apisettingsscripts title="Permanent link">&para;</a></h3> <p>Gibt alle konfigurierten Skripte zurück.</p> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-7-1><a id=__codelineno-7-1 name=__codelineno-7-1 href=#__codelineno-7-1></a><span class=p>{</span>
</span><span id=__span-7-2><a id=__codelineno-7-2 name=__codelineno-7-2 href=#__codelineno-7-2></a><span class=w> </span><span class=nt>&quot;scripts&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-7-3><a id=__codelineno-7-3 name=__codelineno-7-3 href=#__codelineno-7-3></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-7-4><a id=__codelineno-7-4 name=__codelineno-7-4 href=#__codelineno-7-4></a><span class=w> </span><span class=nt>&quot;id&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;script-abc123&quot;</span><span class=p>,</span>
</span><span id=__span-7-5><a id=__codelineno-7-5 name=__codelineno-7-5 href=#__codelineno-7-5></a><span class=w> </span><span class=nt>&quot;name&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Zu Plex verschieben&quot;</span><span class=p>,</span>
</span><span id=__span-7-6><a id=__codelineno-7-6 name=__codelineno-7-6 href=#__codelineno-7-6></a><span class=w> </span><span class=nt>&quot;command&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/home/michael/scripts/move-to-plex.sh&quot;</span><span class=p>,</span>
</span><span id=__span-7-7><a id=__codelineno-7-7 name=__codelineno-7-7 href=#__codelineno-7-7></a><span class=w> </span><span class=nt>&quot;description&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Verschiebt die fertige Datei ins Plex-Verzeichnis&quot;</span><span class=p>,</span>
</span><span id=__span-7-8><a id=__codelineno-7-8 name=__codelineno-7-8 href=#__codelineno-7-8></a><span class=w> </span><span class=nt>&quot;createdAt&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2024-01-15T10:00:00.000Z&quot;</span>
</span><span id=__span-7-9><a id=__codelineno-7-9 name=__codelineno-7-9 href=#__codelineno-7-9></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-7-10><a id=__codelineno-7-10 name=__codelineno-7-10 href=#__codelineno-7-10></a><span class=w> </span><span class=p>]</span>
</span><span id=__span-7-11><a id=__codelineno-7-11 name=__codelineno-7-11 href=#__codelineno-7-11></a><span class=p>}</span>
</span></code></pre></div> <hr> <h3 id=post-apisettingsscripts>POST /api/settings/scripts<a class=headerlink href=#post-apisettingsscripts title="Permanent link">&para;</a></h3> <p>Legt ein neues Post-Encode-Skript an.</p> <p><strong>Request:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-8-1><a id=__codelineno-8-1 name=__codelineno-8-1 href=#__codelineno-8-1></a><span class=p>{</span>
</span><span id=__span-8-2><a id=__codelineno-8-2 name=__codelineno-8-2 href=#__codelineno-8-2></a><span class=w> </span><span class=nt>&quot;name&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Zu Plex verschieben&quot;</span><span class=p>,</span>
</span><span id=__span-8-3><a id=__codelineno-8-3 name=__codelineno-8-3 href=#__codelineno-8-3></a><span class=w> </span><span class=nt>&quot;command&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/home/michael/scripts/move-to-plex.sh&quot;</span><span class=p>,</span>
</span><span id=__span-8-4><a id=__codelineno-8-4 name=__codelineno-8-4 href=#__codelineno-8-4></a><span class=w> </span><span class=nt>&quot;description&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Verschiebt die fertige Datei ins Plex-Verzeichnis&quot;</span>
</span><span id=__span-8-5><a id=__codelineno-8-5 name=__codelineno-8-5 href=#__codelineno-8-5></a><span class=p>}</span>
</span></code></pre></div> <table> <thead> <tr> <th>Feld</th> <th>Typ</th> <th>Pflicht</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>name</code></td> <td>string</td> <td>✅</td> <td>Anzeigename</td> </tr> <tr> <td><code>command</code></td> <td>string</td> <td>✅</td> <td>Shell-Befehl oder absoluter Skriptpfad</td> </tr> <tr> <td><code>description</code></td> <td>string</td> <td>—</td> <td>Optionale Beschreibung</td> </tr> </tbody> </table> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-9-1><a id=__codelineno-9-1 name=__codelineno-9-1 href=#__codelineno-9-1></a><span class=p>{</span>
</span><span id=__span-9-2><a id=__codelineno-9-2 name=__codelineno-9-2 href=#__codelineno-9-2></a><span class=w> </span><span class=nt>&quot;ok&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-9-3><a id=__codelineno-9-3 name=__codelineno-9-3 href=#__codelineno-9-3></a><span class=w> </span><span class=nt>&quot;script&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-9-4><a id=__codelineno-9-4 name=__codelineno-9-4 href=#__codelineno-9-4></a><span class=w> </span><span class=nt>&quot;id&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;script-abc123&quot;</span><span class=p>,</span>
</span><span id=__span-9-5><a id=__codelineno-9-5 name=__codelineno-9-5 href=#__codelineno-9-5></a><span class=w> </span><span class=nt>&quot;name&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Zu Plex verschieben&quot;</span><span class=p>,</span>
</span><span id=__span-9-6><a id=__codelineno-9-6 name=__codelineno-9-6 href=#__codelineno-9-6></a><span class=w> </span><span class=nt>&quot;command&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/home/michael/scripts/move-to-plex.sh&quot;</span>
</span><span id=__span-9-7><a id=__codelineno-9-7 name=__codelineno-9-7 href=#__codelineno-9-7></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-9-8><a id=__codelineno-9-8 name=__codelineno-9-8 href=#__codelineno-9-8></a><span class=p>}</span>
</span></code></pre></div> <hr> <h3 id=put-apisettingsscriptsscriptid>PUT /api/settings/scripts/:scriptId<a class=headerlink href=#put-apisettingsscriptsscriptid title="Permanent link">&para;</a></h3> <p>Aktualisiert ein vorhandenes Skript.</p> <p><strong>URL-Parameter:</strong> <code>scriptId</code></p> <p><strong>Request:</strong> Gleiche Felder wie beim Anlegen (alle optional).</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-10-1><a id=__codelineno-10-1 name=__codelineno-10-1 href=#__codelineno-10-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;name&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Zu Jellyfin verschieben&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;command&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/home/michael/scripts/move-to-jellyfin.sh&quot;</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <p><strong>Response:</strong> <code>{ "ok": true }</code></p> <hr> <h3 id=delete-apisettingsscriptsscriptid>DELETE /api/settings/scripts/:scriptId<a class=headerlink href=#delete-apisettingsscriptsscriptid title="Permanent link">&para;</a></h3> <p>Löscht ein Skript.</p> <p><strong>URL-Parameter:</strong> <code>scriptId</code></p> <p><strong>Response:</strong> <code>{ "ok": true }</code></p> <div class="admonition warning"> <p class=admonition-title>Referenzen in Jobs</p> <p>Wenn das Skript in laufenden oder abgeschlossenen Jobs referenziert wird, wird es trotzdem gelöscht. In zukünftigen Encode-Reviews erscheint es nicht mehr.</p> </div> <hr> <h3 id=post-apisettingsscriptsscriptidtest>POST /api/settings/scripts/:scriptId/test<a class=headerlink href=#post-apisettingsscriptsscriptidtest title="Permanent link">&para;</a></h3> <p>Führt ein Skript mit Platzhalter-Umgebungsvariablen aus (Testlauf).</p> <p><strong>URL-Parameter:</strong> <code>scriptId</code></p> <p><strong>Response (Erfolg):</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-11-1><a id=__codelineno-11-1 name=__codelineno-11-1 href=#__codelineno-11-1></a><span class=p>{</span>
</span><span id=__span-11-2><a id=__codelineno-11-2 name=__codelineno-11-2 href=#__codelineno-11-2></a><span class=w> </span><span class=nt>&quot;ok&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-11-3><a id=__codelineno-11-3 name=__codelineno-11-3 href=#__codelineno-11-3></a><span class=w> </span><span class=nt>&quot;exitCode&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>0</span><span class=p>,</span>
</span><span id=__span-11-4><a id=__codelineno-11-4 name=__codelineno-11-4 href=#__codelineno-11-4></a><span class=w> </span><span class=nt>&quot;stdout&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Testausgabe des Skripts&quot;</span><span class=p>,</span>
</span><span id=__span-11-5><a id=__codelineno-11-5 name=__codelineno-11-5 href=#__codelineno-11-5></a><span class=w> </span><span class=nt>&quot;stderr&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;&quot;</span><span class=p>,</span>
</span><span id=__span-11-6><a id=__codelineno-11-6 name=__codelineno-11-6 href=#__codelineno-11-6></a><span class=w> </span><span class=nt>&quot;durationMs&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>245</span>
</span><span id=__span-11-7><a id=__codelineno-11-7 name=__codelineno-11-7 href=#__codelineno-11-7></a><span class=p>}</span>
</span></code></pre></div> <p><strong>Response (Fehler):</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-12-1><a id=__codelineno-12-1 name=__codelineno-12-1 href=#__codelineno-12-1></a><span class=p>{</span>
</span><span id=__span-12-2><a id=__codelineno-12-2 name=__codelineno-12-2 href=#__codelineno-12-2></a><span class=w> </span><span class=nt>&quot;ok&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>false</span><span class=p>,</span>
</span><span id=__span-12-3><a id=__codelineno-12-3 name=__codelineno-12-3 href=#__codelineno-12-3></a><span class=w> </span><span class=nt>&quot;exitCode&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-12-4><a id=__codelineno-12-4 name=__codelineno-12-4 href=#__codelineno-12-4></a><span class=w> </span><span class=nt>&quot;stdout&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;&quot;</span><span class=p>,</span>
</span><span id=__span-12-5><a id=__codelineno-12-5 name=__codelineno-12-5 href=#__codelineno-12-5></a><span class=w> </span><span class=nt>&quot;stderr&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Datei nicht gefunden: /home/michael/scripts/move-to-plex.sh&quot;</span><span class=p>,</span>
</span><span id=__span-12-6><a id=__codelineno-12-6 name=__codelineno-12-6 href=#__codelineno-12-6></a><span class=w> </span><span class=nt>&quot;durationMs&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>12</span>
</span><span id=__span-12-7><a id=__codelineno-12-7 name=__codelineno-12-7 href=#__codelineno-12-7></a><span class=p>}</span>
</span></code></pre></div> <p><strong>Platzhalter-Werte beim Testlauf:</strong></p> <table> <thead> <tr> <th>Variable</th> <th>Testwert</th> </tr> </thead> <tbody> <tr> <td><code>RIPSTER_OUTPUT_PATH</code></td> <td><code>/tmp/ripster-test-output.mkv</code></td> </tr> <tr> <td><code>RIPSTER_JOB_ID</code></td> <td><code>0</code></td> </tr> <tr> <td><code>RIPSTER_TITLE</code></td> <td><code>Test Film</code></td> </tr> <tr> <td><code>RIPSTER_YEAR</code></td> <td><code>2024</code></td> </tr> <tr> <td><code>RIPSTER_IMDB_ID</code></td> <td><code>tt0000000</code></td> </tr> <tr> <td><code>RIPSTER_RAW_PATH</code></td> <td><code>/tmp/ripster-test-raw.mkv</code></td> </tr> </tbody> </table> <hr> <h2 id=einstellungs-schlussel-referenz>Einstellungs-Schlüssel Referenz<a class=headerlink href=#einstellungs-schlussel-referenz title="Permanent link">&para;</a></h2> <p>Eine vollständige Liste aller Einstellungs-Schlüssel:</p> <table> <thead> <tr> <th>Schlüssel</th> <th>Kategorie</th> <th>Typ</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>raw_dir</code></td> <td>paths</td> <td>string</td> <td>Raw-MKV Verzeichnis</td> </tr> <tr> <td><code>movie_dir</code></td> <td>paths</td> <td>string</td> <td>Ausgabe-Verzeichnis</td> </tr> <tr> <td><code>log_dir</code></td> <td>paths</td> <td>string</td> <td>Log-Verzeichnis</td> </tr> <tr> <td><code>makemkv_command</code></td> <td>tools</td> <td>string</td> <td>MakeMKV-Befehl</td> </tr> <tr> <td><code>handbrake_command</code></td> <td>tools</td> <td>string</td> <td>HandBrake-Befehl</td> </tr> <tr> <td><code>mediainfo_command</code></td> <td>tools</td> <td>string</td> <td>MediaInfo-Befehl</td> </tr> <tr> <td><code>handbrake_preset</code></td> <td>encoding</td> <td>string</td> <td>HandBrake-Preset-Name</td> </tr> <tr> <td><code>handbrake_extra_args</code></td> <td>encoding</td> <td>string</td> <td>Zusatz-Argumente</td> </tr> <tr> <td><code>output_extension</code></td> <td>encoding</td> <td>string</td> <td>Dateiendung (z.B. <code>mkv</code>)</td> </tr> <tr> <td><code>filename_template</code></td> <td>encoding</td> <td>string</td> <td>Dateiname-Template</td> </tr> <tr> <td><code>drive_mode</code></td> <td>drive</td> <td>select</td> <td><code>auto</code> oder <code>explicit</code></td> </tr> <tr> <td><code>drive_device</code></td> <td>drive</td> <td>string</td> <td>Geräte-Pfad</td> </tr> <tr> <td><code>disc_poll_interval_ms</code></td> <td>drive</td> <td>number</td> <td>Polling-Intervall (ms)</td> </tr> <tr> <td><code>makemkv_min_length_minutes</code></td> <td>makemkv</td> <td>number</td> <td>Min. Titellänge (Minuten)</td> </tr> <tr> <td><code>makemkv_backup_mode</code></td> <td>makemkv</td> <td>boolean</td> <td>Backup-Modus aktivieren</td> </tr> <tr> <td><code>omdb_api_key</code></td> <td>omdb</td> <td>string</td> <td>OMDb API-Key</td> </tr> <tr> <td><code>omdb_default_type</code></td> <td>omdb</td> <td>select</td> <td>Standard-Suchtyp</td> </tr> <tr> <td><code>pushover_user_key</code></td> <td>notifications</td> <td>string</td> <td>PushOver User-Key</td> </tr> <tr> <td><code>pushover_api_token</code></td> <td>notifications</td> <td>string</td> <td>PushOver API-Token</td> </tr> </tbody> </table> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 23Line truncated
</span><span id=__span-4-2><a id=__codelineno-4-2 name=__codelineno-4-2 href=#__codelineno-4-2></a><span class=w> </span><span class=nt>&quot;changes&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-4-3><a id=__codelineno-4-3 name=__codelineno-4-3 href=#__codelineno-4-3></a><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;key&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;raw_dir&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;value&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/mnt/storage/raw&quot;</span><span class=w> </span><span class=p>},</span>
</span><span id=__span-4-4><a id=__codelineno-4-4 name=__codelineno-4-4 href=#__codelineno-4-4></a><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;key&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;movie_dir&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;value&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/mnt/storage/movies&quot;</span><span class=w> </span><span class=p>}</span>
</span><span id=__span-4-5><a id=__codelineno-4-5 name=__codelineno-4-5 href=#__codelineno-4-5></a><span class=w> </span><span class=p>],</span>
</span><span id=__span-4-6><a id=__codelineno-4-6 name=__codelineno-4-6 href=#__codelineno-4-6></a><span class=w> </span><span class=nt>&quot;reviewRefresh&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-4-7><a id=__codelineno-4-7 name=__codelineno-4-7 href=#__codelineno-4-7></a><span class=w> </span><span class=nt>&quot;triggered&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-4-8><a id=__codelineno-4-8 name=__codelineno-4-8 href=#__codelineno-4-8></a><span class=w> </span><span class=nt>&quot;jobId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>42</span><span class=p>,</span>
</span><span id=__span-4-9><a id=__codelineno-4-9 name=__codelineno-4-9 href=#__codelineno-4-9></a><span class=w> </span><span class=nt>&quot;relevantKeys&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=s2>&quot;handbrake_preset_bluray&quot;</span><span class=p>]</span>
</span><span id=__span-4-10><a id=__codelineno-4-10 name=__codelineno-4-10 href=#__codelineno-4-10></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-4-11><a id=__codelineno-4-11 name=__codelineno-4-11 href=#__codelineno-4-11></a><span class=p>}</span>
</span></code></pre></div> <p>Bei Validierungsfehlern kommt <code>400</code> mit <code>error.details[]</code>.</p> <hr> <h2 id=get-apisettingshandbrake-presets>GET /api/settings/handbrake-presets<a class=headerlink href=#get-apisettingshandbrake-presets title="Permanent link">&para;</a></h2> <p>Liest Preset-Liste via <code>HandBrakeCLI -z</code> (mit Fallback auf konfigurierte Presets).</p> <p><strong>Response (Beispiel):</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a><span class=p>{</span>
</span><span id=__span-5-2><a id=__codelineno-5-2 name=__codelineno-5-2 href=#__codelineno-5-2></a><span class=w> </span><span class=nt>&quot;source&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;handbrake-cli&quot;</span><span class=p>,</span>
</span><span id=__span-5-3><a id=__codelineno-5-3 name=__codelineno-5-3 href=#__codelineno-5-3></a><span class=w> </span><span class=nt>&quot;message&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>null</span><span class=p>,</span>
</span><span id=__span-5-4><a id=__codelineno-5-4 name=__codelineno-5-4 href=#__codelineno-5-4></a><span class=w> </span><span class=nt>&quot;options&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-5-5><a id=__codelineno-5-5 name=__codelineno-5-5 href=#__codelineno-5-5></a><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;label&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;General/&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;value&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;__group__general&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;disabled&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;category&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;General&quot;</span><span class=w> </span><span class=p>},</span>
</span><span id=__span-5-6><a id=__codelineno-5-6 name=__codelineno-5-6 href=#__codelineno-5-6></a><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;label&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot; Fast 1080p30&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;value&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Fast 1080p30&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;category&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;General&quot;</span><span class=w> </span><span class=p>}</span>
</span><span id=__span-5-7><a id=__codelineno-5-7 name=__codelineno-5-7 href=#__codelineno-5-7></a><span class=w> </span><span class=p>]</span>
</span><span id=__span-5-8><a id=__codelineno-5-8 name=__codelineno-5-8 href=#__codelineno-5-8></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=post-apisettingspushovertest>POST /api/settings/pushover/test<a class=headerlink href=#post-apisettingspushovertest title="Permanent link">&para;</a></h2> <p>Sendet Testnachricht über aktuelle PushOver-Settings.</p> <p><strong>Request (optional):</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-6-1><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a><span class=p>{</span>
</span><span id=__span-6-2><a id=__codelineno-6-2 name=__codelineno-6-2 href=#__codelineno-6-2></a><span class=w> </span><span class=nt>&quot;title&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Test&quot;</span><span class=p>,</span>
</span><span id=__span-6-3><a id=__codelineno-6-3 name=__codelineno-6-3 href=#__codelineno-6-3></a><span class=w> </span><span class=nt>&quot;message&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Ripster Test&quot;</span>
</span><span id=__span-6-4><a id=__codelineno-6-4 name=__codelineno-6-4 href=#__codelineno-6-4></a><span class=p>}</span>
</span></code></pre></div> <p><strong>Response:</strong></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-7-1><a id=__codelineno-7-1 name=__codelineno-7-1 href=#__codelineno-7-1></a><span class=p>{</span>
</span><span id=__span-7-2><a id=__codelineno-7-2 name=__codelineno-7-2 href=#__codelineno-7-2></a><span class=w> </span><span class=nt>&quot;result&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-7-3><a id=__codelineno-7-3 name=__codelineno-7-3 href=#__codelineno-7-3></a><span class=w> </span><span class=nt>&quot;sent&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-7-4><a id=__codelineno-7-4 name=__codelineno-7-4 href=#__codelineno-7-4></a><span class=w> </span><span class=nt>&quot;eventKey&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;test&quot;</span><span class=p>,</span>
</span><span id=__span-7-5><a id=__codelineno-7-5 name=__codelineno-7-5 href=#__codelineno-7-5></a><span class=w> </span><span class=nt>&quot;requestId&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;...&quot;</span>
</span><span id=__span-7-6><a id=__codelineno-7-6 name=__codelineno-7-6 href=#__codelineno-7-6></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-7-7><a id=__codelineno-7-7 name=__codelineno-7-7 href=#__codelineno-7-7></a><span class=p>}</span>
</span></code></pre></div> <p>Wenn PushOver deaktiviert ist oder Credentials fehlen, kommt i. d. R. ebenfalls <code>200</code> mit <code>sent: false</code> + <code>reason</code>.</p> <hr> <h2 id=skripte>Skripte<a class=headerlink href=#skripte title="Permanent link">&para;</a></h2> <p>Basis: <code>/api/settings/scripts</code></p> <h3 id=get-apisettingsscripts>GET /api/settings/scripts<a class=headerlink href=#get-apisettingsscripts title="Permanent link">&para;</a></h3> <div class="language-json highlight"><pre><span></span><code><span id=__span-8-1><a id=__codelineno-8-1 name=__codelineno-8-1 href=#__codelineno-8-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;scripts&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;id&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;name&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;...&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;scriptBody&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;...&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;orderIndex&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;createdAt&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;...&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;updatedAt&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;...&quot;</span><span class=w> </span><span class=p>}</span><span class=w> </span><span class=p>]</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <h3 id=post-apisettingsscripts>POST /api/settings/scripts<a class=headerlink href=#post-apisettingsscripts title="Permanent link">&para;</a></h3> <div class="language-json highlight"><pre><span></span><code><span id=__span-9-1><a id=__codelineno-9-1 name=__codelineno-9-1 href=#__codelineno-9-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;name&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Move&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;scriptBody&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;mv \&quot;$RIPSTER_OUTPUT_PATH\&quot; /mnt/movies/&quot;</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <p>Response: <code>201</code> mit <code>{ "script": { ... } }</code></p> <h3 id=put-apisettingsscriptsid>PUT /api/settings/scripts/:id<a class=headerlink href=#put-apisettingsscriptsid title="Permanent link">&para;</a></h3> <p>Body wie <code>POST</code>, Response <code>{ "script": { ... } }</code>.</p> <h3 id=delete-apisettingsscriptsid>DELETE /api/settings/scripts/:id<a class=headerlink href=#delete-apisettingsscriptsid title="Permanent link">&para;</a></h3> <p>Response <code>{ "removed": { ... } }</code>.</p> <h3 id=post-apisettingsscriptsreorder>POST /api/settings/scripts/reorder<a class=headerlink href=#post-apisettingsscriptsreorder title="Permanent link">&para;</a></h3> <div class="language-json highlight"><pre><span></span><code><span id=__span-10-1><a id=__codelineno-10-1 name=__codelineno-10-1 href=#__codelineno-10-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;orderedScriptIds&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=mi>3</span><span class=p>,</span><span class=w> </span><span class=mi>1</span><span class=p>,</span><span class=w> </span><span class=mi>2</span><span class=p>]</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <p>Response <code>{ "scripts": [ ... ] }</code>.</p> <h3 id=post-apisettingsscriptsidtest>POST /api/settings/scripts/:id/test<a class=headerlink href=#post-apisettingsscriptsidtest title="Permanent link">&para;</a></h3> <p>Führt Skript als Testlauf aus.</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-11-1><a id=__codelineno-11-1 name=__codelineno-11-1 href=#__codelineno-11-1></a><span class=p>{</span>
</span><span id=__span-11-2><a id=__codelineno-11-2 name=__codelineno-11-2 href=#__codelineno-11-2></a><span class=w> </span><span class=nt>&quot;result&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-11-3><a id=__codelineno-11-3 name=__codelineno-11-3 href=#__codelineno-11-3></a><span class=w> </span><span class=nt>&quot;scriptId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-11-4><a id=__codelineno-11-4 name=__codelineno-11-4 href=#__codelineno-11-4></a><span class=w> </span><span class=nt>&quot;scriptName&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Move&quot;</span><span class=p>,</span>
</span><span id=__span-11-5><a id=__codelineno-11-5 name=__codelineno-11-5 href=#__codelineno-11-5></a><span class=w> </span><span class=nt>&quot;success&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-11-6><a id=__codelineno-11-6 name=__codelineno-11-6 href=#__codelineno-11-6></a><span class=w> </span><span class=nt>&quot;exitCode&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>0</span><span class=p>,</span>
</span><span id=__span-11-7><a id=__codelineno-11-7 name=__codelineno-11-7 href=#__codelineno-11-7></a><span class=w> </span><span class=nt>&quot;signal&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>null</span><span class=p>,</span>
</span><span id=__span-11-8><a id=__codelineno-11-8 name=__codelineno-11-8 href=#__codelineno-11-8></a><span class=w> </span><span class=nt>&quot;timedOut&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>false</span><span class=p>,</span>
</span><span id=__span-11-9><a id=__codelineno-11-9 name=__codelineno-11-9 href=#__codelineno-11-9></a><span class=w> </span><span class=nt>&quot;durationMs&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>120</span><span class=p>,</span>
</span><span id=__span-11-10><a id=__codelineno-11-10 name=__codelineno-11-10 href=#__codelineno-11-10></a><span class=w> </span><span class=nt>&quot;stdout&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;...&quot;</span><span class=p>,</span>
</span><span id=__span-11-11><a id=__codelineno-11-11 name=__codelineno-11-11 href=#__codelineno-11-11></a><span class=w> </span><span class=nt>&quot;stderr&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;...&quot;</span><span class=p>,</span>
</span><span id=__span-11-12><a id=__codelineno-11-12 name=__codelineno-11-12 href=#__codelineno-11-12></a><span class=w> </span><span class=nt>&quot;stdoutTruncated&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>false</span><span class=p>,</span>
</span><span id=__span-11-13><a id=__codelineno-11-13 name=__codelineno-11-13 href=#__codelineno-11-13></a><span class=w> </span><span class=nt>&quot;stderrTruncated&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>false</span>
</span><span id=__span-11-14><a id=__codelineno-11-14 name=__codelineno-11-14 href=#__codelineno-11-14></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-11-15><a id=__codelineno-11-15 name=__codelineno-11-15 href=#__codelineno-11-15></a><span class=p>}</span>
</span></code></pre></div> <h3 id=umgebungsvariablen-fur-skripte>Umgebungsvariablen für Skripte<a class=headerlink href=#umgebungsvariablen-fur-skripte title="Permanent link">&para;</a></h3> <p>Diese Variablen werden beim Ausführen gesetzt:</p> <ul> <li><code>RIPSTER_SCRIPT_RUN_AT</code></li> <li><code>RIPSTER_JOB_ID</code></li> <li><code>RIPSTER_JOB_TITLE</code></li> <li><code>RIPSTER_MODE</code></li> <li><code>RIPSTER_INPUT_PATH</code></li> <li><code>RIPSTER_OUTPUT_PATH</code></li> <li><code>RIPSTER_RAW_PATH</code></li> <li><code>RIPSTER_SCRIPT_ID</code></li> <li><code>RIPSTER_SCRIPT_NAME</code></li> <li><code>RIPSTER_SCRIPT_SOURCE</code></li> </ul> <hr> <h2 id=skript-ketten>Skript-Ketten<a class=headerlink href=#skript-ketten title="Permanent link">&para;</a></h2> <p>Basis: <code>/api/settings/script-chains</code></p> <p>Eine Kette hat Schritte vom Typ:</p> <ul> <li><code>script</code> (<code>scriptId</code> erforderlich)</li> <li><code>wait</code> (<code>waitSeconds</code> 1..3600)</li> </ul> <h3 id=get-apisettingsscript-chains>GET /api/settings/script-chains<a class=headerlink href=#get-apisettingsscript-chains title="Permanent link">&para;</a></h3> <p>Response <code>{ "chains": [ ... ] }</code> (inkl. <code>steps[]</code>).</p> <h3 id=get-apisettingsscript-chainsid>GET /api/settings/script-chains/:id<a class=headerlink href=#get-apisettingsscript-chainsid title="Permanent link">&para;</a></h3> <p>Response <code>{ "chain": { ... } }</code>.</p> <h3 id=post-apisettingsscript-chains>POST /api/settings/script-chains<a class=headerlink href=#post-apisettingsscript-chains title="Permanent link">&para;</a></h3> <div class="language-json highlight"><pre><span></span><code><span id=__span-12-1><a id=__codelineno-12-1 name=__codelineno-12-1 href=#__codelineno-12-1></a><span class=p>{</span>
</span><span id=__span-12-2><a id=__codelineno-12-2 name=__codelineno-12-2 href=#__codelineno-12-2></a><span class=w> </span><span class=nt>&quot;name&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;After Encode&quot;</span><span class=p>,</span>
</span><span id=__span-12-3><a id=__codelineno-12-3 name=__codelineno-12-3 href=#__codelineno-12-3></a><span class=w> </span><span class=nt>&quot;steps&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-12-4><a id=__codelineno-12-4 name=__codelineno-12-4 href=#__codelineno-12-4></a><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;stepType&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;script&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;scriptId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=w> </span><span class=p>},</span>
</span><span id=__span-12-5><a id=__codelineno-12-5 name=__codelineno-12-5 href=#__codelineno-12-5></a><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;stepType&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;wait&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;waitSeconds&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>15</span><span class=w> </span><span class=p>},</span>
</span><span id=__span-12-6><a id=__codelineno-12-6 name=__codelineno-12-6 href=#__codelineno-12-6></a><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;stepType&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;script&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;scriptId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>2</span><span class=w> </span><span class=p>}</span>
</span><span id=__span-12-7><a id=__codelineno-12-7 name=__codelineno-12-7 href=#__codelineno-12-7></a><span class=w> </span><span class=p>]</span>
</span><span id=__span-12-8><a id=__codelineno-12-8 name=__codelineno-12-8 href=#__codelineno-12-8></a><span class=p>}</span>
</span></code></pre></div> <p>Response: <code>201</code> mit <code>{ "chain": { ... } }</code></p> <h3 id=put-apisettingsscript-chainsid>PUT /api/settings/script-chains/:id<a class=headerlink href=#put-apisettingsscript-chainsid title="Permanent link">&para;</a></h3> <p>Body wie <code>POST</code>, Response <code>{ "chain": { ... } }</code>.</p> <h3 id=delete-apisettingsscript-chainsid>DELETE /api/settings/script-chains/:id<a class=headerlink href=#delete-apisettingsscript-chainsid title="Permanent link">&para;</a></h3> <p>Response <code>{ "removed": { ... } }</code>.</p> <h3 id=post-apisettingsscript-chainsreorder>POST /api/settings/script-chains/reorder<a class=headerlink href=#post-apisettingsscript-chainsreorder title="Permanent link">&para;</a></h3> <div class="language-json highlight"><pre><span></span><code><span id=__span-13-1><a id=__codelineno-13-1 name=__codelineno-13-1 href=#__codelineno-13-1></a><span class=p>{</span><span class=w> </span><span class=nt>&quot;orderedChainIds&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=mi>2</span><span class=p>,</span><span class=w> </span><span class=mi>1</span><span class=p>,</span><span class=w> </span><span class=mi>3</span><span class=p>]</span><span class=w> </span><span class=p>}</span>
</span></code></pre></div> <p>Response <code>{ "chains": [ ... ] }</code>.</p> <h3 id=post-apisettingsscript-chainsidtest>POST /api/settings/script-chains/:id/test<a class=headerlink href=#post-apisettingsscript-chainsidtest title="Permanent link">&para;</a></h3> <p>Response:</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-14-1><a id=__codelineno-14-1 name=__codelineno-14-1 href=#__codelineno-14-1></a><span class=p>{</span>
</span><span id=__span-14-2><a id=__codelineno-14-2 name=__codelineno-14-2 href=#__codelineno-14-2></a><span class=w> </span><span class=nt>&quot;result&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-14-3><a id=__codelineno-14-3 name=__codelineno-14-3 href=#__codelineno-14-3></a><span class=w> </span><span class=nt>&quot;chainId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>2</span><span class=p>,</span>
</span><span id=__span-14-4><a id=__codelineno-14-4 name=__codelineno-14-4 href=#__codelineno-14-4></a><span class=w> </span><span class=nt>&quot;chainName&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;After Encode&quot;</span><span class=p>,</span>
</span><span id=__span-14-5><a id=__codelineno-14-5 name=__codelineno-14-5 href=#__codelineno-14-5></a><span class=w> </span><span class=nt>&quot;steps&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>3</span><span class=p>,</span>
</span><span id=__span-14-6><a id=__codelineno-14-6 name=__codelineno-14-6 href=#__codelineno-14-6></a><span class=w> </span><span class=nt>&quot;succeeded&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>3</span><span class=p>,</span>
</span><span id=__span-14-7><a id=__codelineno-14-7 name=__codelineno-14-7 href=#__codelineno-14-7></a><span class=w> </span><span class=nt>&quot;failed&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>0</span><span class=p>,</span>
</span><span id=__span-14-8><a id=__codelineno-14-8 name=__codelineno-14-8 href=#__codelineno-14-8></a><span class=w> </span><span class=nt>&quot;aborted&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>false</span><span class=p>,</span>
</span><span id=__span-14-9><a id=__codelineno-14-9 name=__codelineno-14-9 href=#__codelineno-14-9></a><span class=w> </span><span class=nt>&quot;results&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[]</span>
</span><span id=__span-14-10><a id=__codelineno-14-10 name=__codelineno-14-10 href=#__codelineno-14-10></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-14-11><a id=__codelineno-14-11 name=__codelineno-14-11 href=#__codelineno-14-11></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=user-presets>User-Presets<a class=headerlink href=#user-presets title="Permanent link">&para;</a></h2> <p>Basis: <code>/api/settings/user-presets</code></p> <h3 id=get-apisettingsuser-presets>GET /api/settings/user-presets<a class=headerlink href=#get-apisettingsuser-presets title="Permanent link">&para;</a></h3> <p>Optionaler Query-Parameter: <code>media_type=bluray|dvd|other|all</code></p> <div class="language-json highlight"><pre><span></span><code><span id=__span-15-1><a id=__codelineno-15-1 name=__codelineno-15-1 href=#__codelineno-15-1></a><span class=p>{</span>
</span><span id=__span-15-2><a id=__codelineno-15-2 name=__codelineno-15-2 href=#__codelineno-15-2></a><span class=w> </span><span class=nt>&quot;presets&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-15-3><a id=__codelineno-15-3 name=__codelineno-15-3 href=#__codelineno-15-3></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-15-4><a id=__codelineno-15-4 name=__codelineno-15-4 href=#__codelineno-15-4></a><span class=w> </span><span class=nt>&quot;id&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-15-5><a id=__codelineno-15-5 name=__codelineno-15-5 href=#__codelineno-15-5></a><span class=w> </span><span class=nt>&quot;name&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Blu-ray HQ&quot;</span><span class=p>,</span>
</span><span id=__span-15-6><a id=__codelineno-15-6 name=__codelineno-15-6 href=#__codelineno-15-6></a><span class=w> </span><span class=nt>&quot;mediaType&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;bluray&quot;</span><span class=p>,</span>
</span><span id=__span-15-7><a id=__codelineno-15-7 name=__codelineno-15-7 href=#__codelineno-15-7></a><span class=w> </span><span class=nt>&quot;handbrakePreset&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;H.264 MKV 1080p30&quot;</span><span class=p>,</span>
</span><span id=__span-15-8><a id=__codelineno-15-8 name=__codelineno-15-8 href=#__codelineno-15-8></a><span class=w> </span><span class=nt>&quot;extraArgs&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;--encoder-preset slow&quot;</span><span class=p>,</span>
</span><span id=__span-15-9><a id=__codelineno-15-9 name=__codelineno-15-9 href=#__codelineno-15-9></a><span class=w> </span><span class=nt>&quot;description&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;...&quot;</span><span class=p>,</span>
</span><span id=__span-15-10><a id=__codelineno-15-10 name=__codelineno-15-10 href=#__codelineno-15-10></a><span class=w> </span><span class=nt>&quot;createdAt&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;...&quot;</span><span class=p>,</span>
</span><span id=__span-15-11><a id=__codelineno-15-11 name=__codelineno-15-11 href=#__codelineno-15-11></a><span class=w> </span><span class=nt>&quot;updatedAt&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;...&quot;</span>
</span><span id=__span-15-12><a id=__codelineno-15-12 name=__codelineno-15-12 href=#__codelineno-15-12></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-15-13><a id=__codelineno-15-13 name=__codelineno-15-13 href=#__codelineno-15-13></a><span class=w> </span><span class=p>]</span>
</span><span id=__span-15-14><a id=__codelineno-15-14 name=__codelineno-15-14 href=#__codelineno-15-14></a><span class=p>}</span>
</span></code></pre></div> <h3 id=post-apisettingsuser-presets>POST /api/settings/user-presets<a class=headerlink href=#post-apisettingsuser-presets title="Permanent link">&para;</a></h3> <div class="language-json highlight"><pre><span></span><code><span id=__span-16-1><a id=__codelineno-16-1 name=__codelineno-16-1 href=#__codelineno-16-1></a><span class=p>{</span>
</span><span id=__span-16-2><a id=__codelineno-16-2 name=__codelineno-16-2 href=#__codelineno-16-2></a><span class=w> </span><span class=nt>&quot;name&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Blu-ray HQ&quot;</span><span class=p>,</span>
</span><span id=__span-16-3><a id=__codelineno-16-3 name=__codelineno-16-3 href=#__codelineno-16-3></a><span class=w> </span><span class=nt>&quot;mediaType&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;bluray&quot;</span><span class=p>,</span>
</span><span id=__span-16-4><a id=__codelineno-16-4 name=__codelineno-16-4 href=#__codelineno-16-4></a><span class=w> </span><span class=nt>&quot;handbrakePreset&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;H.264 MKV 1080p30&quot;</span><span class=p>,</span>
</span><span id=__span-16-5><a id=__codelineno-16-5 name=__codelineno-16-5 href=#__codelineno-16-5></a><span class=w> </span><span class=nt>&quot;extraArgs&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;--encoder-preset slow&quot;</span><span class=p>,</span>
</span><span id=__span-16-6><a id=__codelineno-16-6 name=__codelineno-16-6 href=#__codelineno-16-6></a><span class=w> </span><span class=nt>&quot;description&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;optional&quot;</span>
</span><span id=__span-16-7><a id=__codelineno-16-7 name=__codelineno-16-7 href=#__codelineno-16-7></a><span class=p>}</span>
</span></code></pre></div> <p>Response: <code>201</code> mit <code>{ "preset": { ... } }</code></p> <h3 id=put-apisettingsuser-presetsid>PUT /api/settings/user-presets/:id<a class=headerlink href=#put-apisettingsuser-presetsid title="Permanent link">&para;</a></h3> <p>Body mit beliebigen Feldern aus <code>POST</code>, Response <code>{ "preset": { ... } }</code>.</p> <h3 id=delete-apisettingsuser-presetsid>DELETE /api/settings/user-presets/:id<a class=headerlink href=#delete-apisettingsuser-presetsid title="Permanent link">&para;</a></h3> <p>Response <code>{ "removed": { ... } }</code>.</p> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
+72 -66
View File
@@ -1,92 +1,98 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/api/websocket/ rel=canonical><link href=../history/ rel=prev><link href=../../configuration/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>WebSocket Events - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#websocket-events class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> WebSocket Events </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 Line truncated
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/api/websocket/ rel=canonical><link href=../crons/ rel=prev><link href=../../configuration/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>WebSocket Events - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#websocket-events class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> WebSocket Events </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 Line truncated
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a>
</span><span id=__span-0-3><a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a><span class=nx>ws</span><span class=p>.</span><span class=nx>onmessage</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=p>(</span><span class=nx>event</span><span class=p>)</span><span class=w> </span><span class=p>=&gt;</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-0-4><a id=__codelineno-0-4 name=__codelineno-0-4 href=#__codelineno-0-4></a><span class=w> </span><span class=kd>const</span><span class=w> </span><span class=nx>message</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=nb>JSON</span><span class=p>.</span><span class=nx>parse</span><span class=p>(</span><span class=nx>event</span><span class=p>.</span><span class=nx>data</span><span class=p>);</span>
</span><span id=__span-0-5><a id=__codelineno-0-5 name=__codelineno-0-5 href=#__codelineno-0-5></a><span class=w> </span><span class=nx>console</span><span class=p>.</span><span class=nx>log</span><span class=p>(</span><span class=nx>message</span><span class=p>.</span><span class=nx>type</span><span class=p>,</span><span class=w> </span><span class=nx>message</span><span class=p>.</span><span class=nx>payload</span><span class=p>);</span>
</span><span id=__span-0-4><a id=__codelineno-0-4 name=__codelineno-0-4 href=#__codelineno-0-4></a><span class=w> </span><span class=kd>const</span><span class=w> </span><span class=nx>msg</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=nb>JSON</span><span class=p>.</span><span class=nx>parse</span><span class=p>(</span><span class=nx>event</span><span class=p>.</span><span class=nx>data</span><span class=p>);</span>
</span><span id=__span-0-5><a id=__codelineno-0-5 name=__codelineno-0-5 href=#__codelineno-0-5></a><span class=w> </span><span class=nx>console</span><span class=p>.</span><span class=nx>log</span><span class=p>(</span><span class=nx>msg</span><span class=p>.</span><span class=nx>type</span><span class=p>,</span><span class=w> </span><span class=nx>msg</span><span class=p>.</span><span class=nx>payload</span><span class=p>);</span>
</span><span id=__span-0-6><a id=__codelineno-0-6 name=__codelineno-0-6 href=#__codelineno-0-6></a><span class=p>};</span>
</span></code></pre></div> <hr> <h2 id=nachrichtenformat>Nachrichtenformat<a class=headerlink href=#nachrichtenformat title="Permanent link">&para;</a></h2> <p>Alle Broadcasts haben dieses Schema:</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a><span class=p>{</span>
</span></code></pre></div> <hr> <h2 id=nachrichtenformat>Nachrichtenformat<a class=headerlink href=#nachrichtenformat title="Permanent link">&para;</a></h2> <p>Die meisten Broadcasts haben dieses Schema:</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a><span class=p>{</span>
</span><span id=__span-1-2><a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;EVENT_TYPE&quot;</span><span class=p>,</span>
</span><span id=__span-1-3><a id=__codelineno-1-3 name=__codelineno-1-3 href=#__codelineno-1-3></a><span class=w> </span><span class=nt>&quot;payload&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=p>},</span>
</span><span id=__span-1-4><a id=__codelineno-1-4 name=__codelineno-1-4 href=#__codelineno-1-4></a><span class=w> </span><span class=nt>&quot;timestamp&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2026-03-05T10:00:00.000Z&quot;</span>
</span><span id=__span-1-3><a id=__codelineno-1-3 name=__codelineno-1-3 href=#__codelineno-1-3></a><span class=w> </span><span class=nt>&quot;payload&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{},</span>
</span><span id=__span-1-4><a id=__codelineno-1-4 name=__codelineno-1-4 href=#__codelineno-1-4></a><span class=w> </span><span class=nt>&quot;timestamp&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2026-03-10T09:00:00.000Z&quot;</span>
</span><span id=__span-1-5><a id=__codelineno-1-5 name=__codelineno-1-5 href=#__codelineno-1-5></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=event-typen>Event-Typen<a class=headerlink href=#event-typen title="Permanent link">&para;</a></h2> <h3 id=ws_connected>WS_CONNECTED<a class=headerlink href=#ws_connected title="Permanent link">&para;</a></h3> <p>Wird direkt nach Verbindungsaufbau gesendet.</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a><span class=p>{</span>
</span></code></pre></div> <p>Ausnahme: <code>WS_CONNECTED</code> beim Verbindungsaufbau enthält kein <code>timestamp</code>.</p> <hr> <h2 id=event-typen>Event-Typen<a class=headerlink href=#event-typen title="Permanent link">&para;</a></h2> <h3 id=ws_connected>WS_CONNECTED<a class=headerlink href=#ws_connected title="Permanent link">&para;</a></h3> <p>Sofort nach erfolgreicher Verbindung.</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a><span class=p>{</span>
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;WS_CONNECTED&quot;</span><span class=p>,</span>
</span><span id=__span-2-3><a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a><span class=w> </span><span class=nt>&quot;payload&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-2-4><a id=__codelineno-2-4 name=__codelineno-2-4 href=#__codelineno-2-4></a><span class=w> </span><span class=nt>&quot;connectedAt&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2026-03-05T10:00:00.000Z&quot;</span>
</span><span id=__span-2-4><a id=__codelineno-2-4 name=__codelineno-2-4 href=#__codelineno-2-4></a><span class=w> </span><span class=nt>&quot;connectedAt&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2026-03-10T09:00:00.000Z&quot;</span>
</span><span id=__span-2-5><a id=__codelineno-2-5 name=__codelineno-2-5 href=#__codelineno-2-5></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-2-6><a id=__codelineno-2-6 name=__codelineno-2-6 href=#__codelineno-2-6></a><span class=p>}</span>
</span></code></pre></div> <h3 id=pipeline_state_changed>PIPELINE_STATE_CHANGED<a class=headerlink href=#pipeline_state_changed title="Permanent link">&para;</a></h3> <p>Snapshot bei Zustandswechsel.</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a><span class=p>{</span>
</span></code></pre></div> <h3 id=pipeline_state_changed>PIPELINE_STATE_CHANGED<a class=headerlink href=#pipeline_state_changed title="Permanent link">&para;</a></h3> <p>Neuer Pipeline-Snapshot.</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a><span class=p>{</span>
</span><span id=__span-3-2><a id=__codelineno-3-2 name=__codelineno-3-2 href=#__codelineno-3-2></a><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;PIPELINE_STATE_CHANGED&quot;</span><span class=p>,</span>
</span><span id=__span-3-3><a id=__codelineno-3-3 name=__codelineno-3-3 href=#__codelineno-3-3></a><span class=w> </span><span class=nt>&quot;payload&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-3-4><a id=__codelineno-3-4 name=__codelineno-3-4 href=#__codelineno-3-4></a><span class=w> </span><span class=nt>&quot;state&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;ENCODING&quot;</span><span class=p>,</span>
</span><span id=__span-3-5><a id=__codelineno-3-5 name=__codelineno-3-5 href=#__codelineno-3-5></a><span class=w> </span><span class=nt>&quot;activeJobId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>42</span><span class=p>,</span>
</span><span id=__span-3-6><a id=__codelineno-3-6 name=__codelineno-3-6 href=#__codelineno-3-6></a><span class=w> </span><span class=nt>&quot;progress&quot;</span><span class=p>:</span><span class=w> </span><span class=mf>73.5</span><span class=p>,</span>
</span><span id=__span-3-6><a id=__codelineno-3-6 name=__codelineno-3-6 href=#__codelineno-3-6></a><span class=w> </span><span class=nt>&quot;progress&quot;</span><span class=p>:</span><span class=w> </span><span class=mf>62.5</span><span class=p>,</span>
</span><span id=__span-3-7><a id=__codelineno-3-7 name=__codelineno-3-7 href=#__codelineno-3-7></a><span class=w> </span><span class=nt>&quot;eta&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;00:12:34&quot;</span><span class=p>,</span>
</span><span id=__span-3-8><a id=__codelineno-3-8 name=__codelineno-3-8 href=#__codelineno-3-8></a><span class=w> </span><span class=nt>&quot;statusText&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Encoding mit HandBrake&quot;</span><span class=p>,</span>
</span><span id=__span-3-8><a id=__codelineno-3-8 name=__codelineno-3-8 href=#__codelineno-3-8></a><span class=w> </span><span class=nt>&quot;statusText&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;ENCODING 62.50%&quot;</span><span class=p>,</span>
</span><span id=__span-3-9><a id=__codelineno-3-9 name=__codelineno-3-9 href=#__codelineno-3-9></a><span class=w> </span><span class=nt>&quot;context&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{},</span>
</span><span id=__span-3-10><a id=__codelineno-3-10 name=__codelineno-3-10 href=#__codelineno-3-10></a><span class=w> </span><span class=nt>&quot;queue&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-3-11><a id=__codelineno-3-11 name=__codelineno-3-11 href=#__codelineno-3-11></a><span class=w> </span><span class=nt>&quot;maxParallelJobs&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-3-12><a id=__codelineno-3-12 name=__codelineno-3-12 href=#__codelineno-3-12></a><span class=w> </span><span class=nt>&quot;runningCount&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-3-13><a id=__codelineno-3-13 name=__codelineno-3-13 href=#__codelineno-3-13></a><span class=w> </span><span class=nt>&quot;queuedCount&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>0</span>
</span><span id=__span-3-14><a id=__codelineno-3-14 name=__codelineno-3-14 href=#__codelineno-3-14></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-3-15><a id=__codelineno-3-15 name=__codelineno-3-15 href=#__codelineno-3-15></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-3-16><a id=__codelineno-3-16 name=__codelineno-3-16 href=#__codelineno-3-16></a><span class=p>}</span>
</span></code></pre></div> <h3 id=pipeline_progress>PIPELINE_PROGRESS<a class=headerlink href=#pipeline_progress title="Permanent link">&para;</a></h3> <p>Laufende Fortschrittsupdates während aktiver Phasen.</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a><span class=p>{</span>
</span><span id=__span-3-10><a id=__codelineno-3-10 name=__codelineno-3-10 href=#__codelineno-3-10></a><span class=w> </span><span class=nt>&quot;jobProgress&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-3-11><a id=__codelineno-3-11 name=__codelineno-3-11 href=#__codelineno-3-11></a><span class=w> </span><span class=nt>&quot;42&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-3-12><a id=__codelineno-3-12 name=__codelineno-3-12 href=#__codelineno-3-12></a><span class=w> </span><span class=nt>&quot;state&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;ENCODING&quot;</span><span class=p>,</span>
</span><span id=__span-3-13><a id=__codelineno-3-13 name=__codelineno-3-13 href=#__codelineno-3-13></a><span class=w> </span><span class=nt>&quot;progress&quot;</span><span class=p>:</span><span class=w> </span><span class=mf>62.5</span><span class=p>,</span>
</span><span id=__span-3-14><a id=__codelineno-3-14 name=__codelineno-3-14 href=#__codelineno-3-14></a><span class=w> </span><span class=nt>&quot;eta&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;00:12:34&quot;</span><span class=p>,</span>
</span><span id=__span-3-15><a id=__codelineno-3-15 name=__codelineno-3-15 href=#__codelineno-3-15></a><span class=w> </span><span class=nt>&quot;statusText&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;ENCODING 62.50%&quot;</span>
</span><span id=__span-3-16><a id=__codelineno-3-16 name=__codelineno-3-16 href=#__codelineno-3-16></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-3-17><a id=__codelineno-3-17 name=__codelineno-3-17 href=#__codelineno-3-17></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-3-18><a id=__codelineno-3-18 name=__codelineno-3-18 href=#__codelineno-3-18></a><span class=w> </span><span class=nt>&quot;queue&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-3-19><a id=__codelineno-3-19 name=__codelineno-3-19 href=#__codelineno-3-19></a><span class=w> </span><span class=nt>&quot;maxParallelJobs&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-3-20><a id=__codelineno-3-20 name=__codelineno-3-20 href=#__codelineno-3-20></a><span class=w> </span><span class=nt>&quot;runningCount&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-3-21><a id=__codelineno-3-21 name=__codelineno-3-21 href=#__codelineno-3-21></a><span class=w> </span><span class=nt>&quot;queuedCount&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>2</span><span class=p>,</span>
</span><span id=__span-3-22><a id=__codelineno-3-22 name=__codelineno-3-22 href=#__codelineno-3-22></a><span class=w> </span><span class=nt>&quot;runningJobs&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[],</span>
</span><span id=__span-3-23><a id=__codelineno-3-23 name=__codelineno-3-23 href=#__codelineno-3-23></a><span class=w> </span><span class=nt>&quot;queuedJobs&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[]</span>
</span><span id=__span-3-24><a id=__codelineno-3-24 name=__codelineno-3-24 href=#__codelineno-3-24></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-3-25><a id=__codelineno-3-25 name=__codelineno-3-25 href=#__codelineno-3-25></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-3-26><a id=__codelineno-3-26 name=__codelineno-3-26 href=#__codelineno-3-26></a><span class=p>}</span>
</span></code></pre></div> <h3 id=pipeline_progress>PIPELINE_PROGRESS<a class=headerlink href=#pipeline_progress title="Permanent link">&para;</a></h3> <p>Laufende Fortschrittsupdates.</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a><span class=p>{</span>
</span><span id=__span-4-2><a id=__codelineno-4-2 name=__codelineno-4-2 href=#__codelineno-4-2></a><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;PIPELINE_PROGRESS&quot;</span><span class=p>,</span>
</span><span id=__span-4-3><a id=__codelineno-4-3 name=__codelineno-4-3 href=#__codelineno-4-3></a><span class=w> </span><span class=nt>&quot;payload&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-4-4><a id=__codelineno-4-4 name=__codelineno-4-4 href=#__codelineno-4-4></a><span class=w> </span><span class=nt>&quot;state&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;ENCODING&quot;</span><span class=p>,</span>
</span><span id=__span-4-5><a id=__codelineno-4-5 name=__codelineno-4-5 href=#__codelineno-4-5></a><span class=w> </span><span class=nt>&quot;activeJobId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>42</span><span class=p>,</span>
</span><span id=__span-4-6><a id=__codelineno-4-6 name=__codelineno-4-6 href=#__codelineno-4-6></a><span class=w> </span><span class=nt>&quot;progress&quot;</span><span class=p>:</span><span class=w> </span><span class=mf>73.5</span><span class=p>,</span>
</span><span id=__span-4-6><a id=__codelineno-4-6 name=__codelineno-4-6 href=#__codelineno-4-6></a><span class=w> </span><span class=nt>&quot;progress&quot;</span><span class=p>:</span><span class=w> </span><span class=mf>62.5</span><span class=p>,</span>
</span><span id=__span-4-7><a id=__codelineno-4-7 name=__codelineno-4-7 href=#__codelineno-4-7></a><span class=w> </span><span class=nt>&quot;eta&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;00:12:34&quot;</span><span class=p>,</span>
</span><span id=__span-4-8><a id=__codelineno-4-8 name=__codelineno-4-8 href=#__codelineno-4-8></a><span class=w> </span><span class=nt>&quot;statusText&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;ENCODING 73.50% - task 1 of 1&quot;</span>
</span><span id=__span-4-8><a id=__codelineno-4-8 name=__codelineno-4-8 href=#__codelineno-4-8></a><span class=w> </span><span class=nt>&quot;statusText&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;ENCODING 62.50%&quot;</span>
</span><span id=__span-4-9><a id=__codelineno-4-9 name=__codelineno-4-9 href=#__codelineno-4-9></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-4-10><a id=__codelineno-4-10 name=__codelineno-4-10 href=#__codelineno-4-10></a><span class=p>}</span>
</span></code></pre></div> <h3 id=pipeline_queue_changed>PIPELINE_QUEUE_CHANGED<a class=headerlink href=#pipeline_queue_changed title="Permanent link">&para;</a></h3> <p>Aktualisierung der Job-Queue.</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a><span class=p>{</span>
</span><span id=__span-5-2><a id=__codelineno-5-2 name=__codelineno-5-2 href=#__codelineno-5-2></a><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;PIPELINE_QUEUE_CHANGED&quot;</span><span class=p>,</span>
</span></code></pre></div> <h3 id=pipeline_queue_changed>PIPELINE_QUEUE_CHANGED<a class=headerlink href=#pipeline_queue_changed title="Permanent link">&para;</a></h3> <p>Queue-Snapshot aktualisiert.</p> <h3 id=disc_detected-disc_removed>DISC_DETECTED / DISC_REMOVED<a class=headerlink href=#disc_detected-disc_removed title="Permanent link">&para;</a></h3> <p>Disc-Insertion/-Removal.</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a><span class=p>{</span>
</span><span id=__span-5-2><a id=__codelineno-5-2 name=__codelineno-5-2 href=#__codelineno-5-2></a><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;DISC_DETECTED&quot;</span><span class=p>,</span>
</span><span id=__span-5-3><a id=__codelineno-5-3 name=__codelineno-5-3 href=#__codelineno-5-3></a><span class=w> </span><span class=nt>&quot;payload&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-5-4><a id=__codelineno-5-4 name=__codelineno-5-4 href=#__codelineno-5-4></a><span class=w> </span><span class=nt>&quot;maxParallelJobs&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-5-5><a id=__codelineno-5-5 name=__codelineno-5-5 href=#__codelineno-5-5></a><span class=w> </span><span class=nt>&quot;runningCount&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-5-6><a id=__codelineno-5-6 name=__codelineno-5-6 href=#__codelineno-5-6></a><span class=w> </span><span class=nt>&quot;queuedCount&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>2</span><span class=p>,</span>
</span><span id=__span-5-7><a id=__codelineno-5-7 name=__codelineno-5-7 href=#__codelineno-5-7></a><span class=w> </span><span class=nt>&quot;runningJobs&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[],</span>
</span><span id=__span-5-8><a id=__codelineno-5-8 name=__codelineno-5-8 href=#__codelineno-5-8></a><span class=w> </span><span class=nt>&quot;queuedJobs&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[]</span>
</span><span id=__span-5-9><a id=__codelineno-5-9 name=__codelineno-5-9 href=#__codelineno-5-9></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-5-10><a id=__codelineno-5-10 name=__codelineno-5-10 href=#__codelineno-5-10></a><span class=p>}</span>
</span></code></pre></div> <h3 id=disc_detected>DISC_DETECTED<a class=headerlink href=#disc_detected title="Permanent link">&para;</a></h3> <p>Disc erkannt.</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-6-1><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a><span class=p>{</span>
</span><span id=__span-6-2><a id=__codelineno-6-2 name=__codelineno-6-2 href=#__codelineno-6-2></a><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;DISC_DETECTED&quot;</span><span class=p>,</span>
</span><span id=__span-5-4><a id=__codelineno-5-4 name=__codelineno-5-4 href=#__codelineno-5-4></a><span class=w> </span><span class=nt>&quot;device&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-5-5><a id=__codelineno-5-5 name=__codelineno-5-5 href=#__codelineno-5-5></a><span class=w> </span><span class=nt>&quot;path&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/dev/sr0&quot;</span><span class=p>,</span>
</span><span id=__span-5-6><a id=__codelineno-5-6 name=__codelineno-5-6 href=#__codelineno-5-6></a><span class=w> </span><span class=nt>&quot;discLabel&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;INCEPTION&quot;</span><span class=p>,</span>
</span><span id=__span-5-7><a id=__codelineno-5-7 name=__codelineno-5-7 href=#__codelineno-5-7></a><span class=w> </span><span class=nt>&quot;model&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;ASUS BW-16D1HT&quot;</span><span class=p>,</span>
</span><span id=__span-5-8><a id=__codelineno-5-8 name=__codelineno-5-8 href=#__codelineno-5-8></a><span class=w> </span><span class=nt>&quot;fstype&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;udf&quot;</span><span class=p>,</span>
</span><span id=__span-5-9><a id=__codelineno-5-9 name=__codelineno-5-9 href=#__codelineno-5-9></a><span class=w> </span><span class=nt>&quot;mountpoint&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>null</span><span class=p>,</span>
</span><span id=__span-5-10><a id=__codelineno-5-10 name=__codelineno-5-10 href=#__codelineno-5-10></a><span class=w> </span><span class=nt>&quot;mediaProfile&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;bluray&quot;</span>
</span><span id=__span-5-11><a id=__codelineno-5-11 name=__codelineno-5-11 href=#__codelineno-5-11></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-5-12><a id=__codelineno-5-12 name=__codelineno-5-12 href=#__codelineno-5-12></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-5-13><a id=__codelineno-5-13 name=__codelineno-5-13 href=#__codelineno-5-13></a><span class=p>}</span>
</span></code></pre></div> <p><code>mediaProfile</code>: <code>bluray</code> | <code>dvd</code> | <code>other</code> | <code>null</code></p> <h3 id=hardware_monitor_update>HARDWARE_MONITOR_UPDATE<a class=headerlink href=#hardware_monitor_update title="Permanent link">&para;</a></h3> <p>Snapshot aus Hardware-Monitoring.</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-6-1><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a><span class=p>{</span>
</span><span id=__span-6-2><a id=__codelineno-6-2 name=__codelineno-6-2 href=#__codelineno-6-2></a><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;HARDWARE_MONITOR_UPDATE&quot;</span><span class=p>,</span>
</span><span id=__span-6-3><a id=__codelineno-6-3 name=__codelineno-6-3 href=#__codelineno-6-3></a><span class=w> </span><span class=nt>&quot;payload&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-6-4><a id=__codelineno-6-4 name=__codelineno-6-4 href=#__codelineno-6-4></a><span class=w> </span><span class=nt>&quot;device&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-6-5><a id=__codelineno-6-5 name=__codelineno-6-5 href=#__codelineno-6-5></a><span class=w> </span><span class=nt>&quot;path&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/dev/sr0&quot;</span><span class=p>,</span>
</span><span id=__span-6-6><a id=__codelineno-6-6 name=__codelineno-6-6 href=#__codelineno-6-6></a><span class=w> </span><span class=nt>&quot;discLabel&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;INCEPTION&quot;</span>
</span><span id=__span-6-7><a id=__codelineno-6-7 name=__codelineno-6-7 href=#__codelineno-6-7></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-6-8><a id=__codelineno-6-8 name=__codelineno-6-8 href=#__codelineno-6-8></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-6-9><a id=__codelineno-6-9 name=__codelineno-6-9 href=#__codelineno-6-9></a><span class=p>}</span>
</span></code></pre></div> <h3 id=disc_removed>DISC_REMOVED<a class=headerlink href=#disc_removed title="Permanent link">&para;</a></h3> <p>Disc entfernt.</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-7-1><a id=__codelineno-7-1 name=__codelineno-7-1 href=#__codelineno-7-1></a><span class=p>{</span>
</span><span id=__span-7-2><a id=__codelineno-7-2 name=__codelineno-7-2 href=#__codelineno-7-2></a><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;DISC_REMOVED&quot;</span><span class=p>,</span>
</span><span id=__span-6-4><a id=__codelineno-6-4 name=__codelineno-6-4 href=#__codelineno-6-4></a><span class=w> </span><span class=nt>&quot;enabled&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-6-5><a id=__codelineno-6-5 name=__codelineno-6-5 href=#__codelineno-6-5></a><span class=w> </span><span class=nt>&quot;intervalMs&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>5000</span><span class=p>,</span>
</span><span id=__span-6-6><a id=__codelineno-6-6 name=__codelineno-6-6 href=#__codelineno-6-6></a><span class=w> </span><span class=nt>&quot;updatedAt&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2026-03-10T09:00:00.000Z&quot;</span><span class=p>,</span>
</span><span id=__span-6-7><a id=__codelineno-6-7 name=__codelineno-6-7 href=#__codelineno-6-7></a><span class=w> </span><span class=nt>&quot;sample&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-6-8><a id=__codelineno-6-8 name=__codelineno-6-8 href=#__codelineno-6-8></a><span class=w> </span><span class=nt>&quot;cpu&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{},</span>
</span><span id=__span-6-9><a id=__codelineno-6-9 name=__codelineno-6-9 href=#__codelineno-6-9></a><span class=w> </span><span class=nt>&quot;memory&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{},</span>
</span><span id=__span-6-10><a id=__codelineno-6-10 name=__codelineno-6-10 href=#__codelineno-6-10></a><span class=w> </span><span class=nt>&quot;gpu&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{},</span>
</span><span id=__span-6-11><a id=__codelineno-6-11 name=__codelineno-6-11 href=#__codelineno-6-11></a><span class=w> </span><span class=nt>&quot;storage&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{}</span>
</span><span id=__span-6-12><a id=__codelineno-6-12 name=__codelineno-6-12 href=#__codelineno-6-12></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-6-13><a id=__codelineno-6-13 name=__codelineno-6-13 href=#__codelineno-6-13></a><span class=w> </span><span class=nt>&quot;error&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>null</span>
</span><span id=__span-6-14><a id=__codelineno-6-14 name=__codelineno-6-14 href=#__codelineno-6-14></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-6-15><a id=__codelineno-6-15 name=__codelineno-6-15 href=#__codelineno-6-15></a><span class=p>}</span>
</span></code></pre></div> <h3 id=pipeline_error>PIPELINE_ERROR<a class=headerlink href=#pipeline_error title="Permanent link">&para;</a></h3> <p>Fehler bei Disc-Event-Verarbeitung in Pipeline.</p> <h3 id=disk_detection_error>DISK_DETECTION_ERROR<a class=headerlink href=#disk_detection_error title="Permanent link">&para;</a></h3> <p>Fehler in Laufwerkserkennung.</p> <h3 id=settings_updated>SETTINGS_UPDATED<a class=headerlink href=#settings_updated title="Permanent link">&para;</a></h3> <p>Einzelnes Setting wurde gespeichert.</p> <h3 id=settings_bulk_updated>SETTINGS_BULK_UPDATED<a class=headerlink href=#settings_bulk_updated title="Permanent link">&para;</a></h3> <p>Bulk-Settings gespeichert.</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-7-1><a id=__codelineno-7-1 name=__codelineno-7-1 href=#__codelineno-7-1></a><span class=p>{</span>
</span><span id=__span-7-2><a id=__codelineno-7-2 name=__codelineno-7-2 href=#__codelineno-7-2></a><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;SETTINGS_BULK_UPDATED&quot;</span><span class=p>,</span>
</span><span id=__span-7-3><a id=__codelineno-7-3 name=__codelineno-7-3 href=#__codelineno-7-3></a><span class=w> </span><span class=nt>&quot;payload&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-7-4><a id=__codelineno-7-4 name=__codelineno-7-4 href=#__codelineno-7-4></a><span class=w> </span><span class=nt>&quot;device&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-7-5><a id=__codelineno-7-5 name=__codelineno-7-5 href=#__codelineno-7-5></a><span class=w> </span><span class=nt>&quot;path&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;/dev/sr0&quot;</span>
</span><span id=__span-7-6><a id=__codelineno-7-6 name=__codelineno-7-6 href=#__codelineno-7-6></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-7-7><a id=__codelineno-7-7 name=__codelineno-7-7 href=#__codelineno-7-7></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-7-8><a id=__codelineno-7-8 name=__codelineno-7-8 href=#__codelineno-7-8></a><span class=p>}</span>
</span></code></pre></div> <h3 id=pipeline_error>PIPELINE_ERROR<a class=headerlink href=#pipeline_error title="Permanent link">&para;</a></h3> <p>Fehler bei Pipeline-Disc-Events im Backend.</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-8-1><a id=__codelineno-8-1 name=__codelineno-8-1 href=#__codelineno-8-1></a><span class=p>{</span>
</span><span id=__span-8-2><a id=__codelineno-8-2 name=__codelineno-8-2 href=#__codelineno-8-2></a><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;PIPELINE_ERROR&quot;</span><span class=p>,</span>
</span><span id=__span-7-4><a id=__codelineno-7-4 name=__codelineno-7-4 href=#__codelineno-7-4></a><span class=w> </span><span class=nt>&quot;count&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>3</span><span class=p>,</span>
</span><span id=__span-7-5><a id=__codelineno-7-5 name=__codelineno-7-5 href=#__codelineno-7-5></a><span class=w> </span><span class=nt>&quot;keys&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=s2>&quot;raw_dir&quot;</span><span class=p>,</span><span class=w> </span><span class=s2>&quot;movie_dir&quot;</span><span class=p>,</span><span class=w> </span><span class=s2>&quot;handbrake_preset_bluray&quot;</span><span class=p>]</span>
</span><span id=__span-7-6><a id=__codelineno-7-6 name=__codelineno-7-6 href=#__codelineno-7-6></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-7-7><a id=__codelineno-7-7 name=__codelineno-7-7 href=#__codelineno-7-7></a><span class=p>}</span>
</span></code></pre></div> <h3 id=settings_scripts_updated>SETTINGS_SCRIPTS_UPDATED<a class=headerlink href=#settings_scripts_updated title="Permanent link">&para;</a></h3> <p>Skript geändert (<code>created|updated|deleted|reordered</code>).</p> <h3 id=settings_script_chains_updated>SETTINGS_SCRIPT_CHAINS_UPDATED<a class=headerlink href=#settings_script_chains_updated title="Permanent link">&para;</a></h3> <p>Skript-Kette geändert (<code>created|updated|deleted|reordered</code>).</p> <h3 id=user_presets_updated>USER_PRESETS_UPDATED<a class=headerlink href=#user_presets_updated title="Permanent link">&para;</a></h3> <p>User-Preset geändert (<code>created|updated|deleted</code>).</p> <h3 id=cron_jobs_updated>CRON_JOBS_UPDATED<a class=headerlink href=#cron_jobs_updated title="Permanent link">&para;</a></h3> <p>Cron-Config geändert (<code>created|updated|deleted</code>).</p> <h3 id=cron_job_updated>CRON_JOB_UPDATED<a class=headerlink href=#cron_job_updated title="Permanent link">&para;</a></h3> <p>Laufzeitstatus eines Cron-Jobs geändert.</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-8-1><a id=__codelineno-8-1 name=__codelineno-8-1 href=#__codelineno-8-1></a><span class=p>{</span>
</span><span id=__span-8-2><a id=__codelineno-8-2 name=__codelineno-8-2 href=#__codelineno-8-2></a><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;CRON_JOB_UPDATED&quot;</span><span class=p>,</span>
</span><span id=__span-8-3><a id=__codelineno-8-3 name=__codelineno-8-3 href=#__codelineno-8-3></a><span class=w> </span><span class=nt>&quot;payload&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-8-4><a id=__codelineno-8-4 name=__codelineno-8-4 href=#__codelineno-8-4></a><span class=w> </span><span class=nt>&quot;message&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;...&quot;</span>
</span><span id=__span-8-5><a id=__codelineno-8-5 name=__codelineno-8-5 href=#__codelineno-8-5></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-8-6><a id=__codelineno-8-6 name=__codelineno-8-6 href=#__codelineno-8-6></a><span class=p>}</span>
</span></code></pre></div> <h3 id=disk_detection_error>DISK_DETECTION_ERROR<a class=headerlink href=#disk_detection_error title="Permanent link">&para;</a></h3> <p>Fehler im Laufwerkserkennungsdienst.</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-9-1><a id=__codelineno-9-1 name=__codelineno-9-1 href=#__codelineno-9-1></a><span class=p>{</span>
</span><span id=__span-9-2><a id=__codelineno-9-2 name=__codelineno-9-2 href=#__codelineno-9-2></a><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;DISK_DETECTION_ERROR&quot;</span><span class=p>,</span>
</span><span id=__span-9-3><a id=__codelineno-9-3 name=__codelineno-9-3 href=#__codelineno-9-3></a><span class=w> </span><span class=nt>&quot;payload&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-9-4><a id=__codelineno-9-4 name=__codelineno-9-4 href=#__codelineno-9-4></a><span class=w> </span><span class=nt>&quot;message&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;...&quot;</span>
</span><span id=__span-9-5><a id=__codelineno-9-5 name=__codelineno-9-5 href=#__codelineno-9-5></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-9-6><a id=__codelineno-9-6 name=__codelineno-9-6 href=#__codelineno-9-6></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=reconnect-verhalten>Reconnect-Verhalten<a class=headerlink href=#reconnect-verhalten title="Permanent link">&para;</a></h2> <p><code>useWebSocket.js</code> versucht bei Verbindungsabbruch automatisch erneut zu verbinden.</p> <ul> <li>fester Retry-Intervall: <code>1500ms</code></li> <li>erneuter Versuch bis zum Unmount der Komponente</li> </ul> <hr> <h2 id=react-beispiel>React-Beispiel<a class=headerlink href=#react-beispiel title="Permanent link">&para;</a></h2> <div class="language-js highlight"><pre><span></span><code><span id=__span-10-1><a id=__codelineno-10-1 name=__codelineno-10-1 href=#__codelineno-10-1></a><span class=k>import</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nx>useWebSocket</span><span class=w> </span><span class=p>}</span><span class=w> </span><span class=kr>from</span><span class=w> </span><span class=s1>&#39;./hooks/useWebSocket&#39;</span><span class=p>;</span>
</span><span id=__span-10-2><a id=__codelineno-10-2 name=__codelineno-10-2 href=#__codelineno-10-2></a>
</span><span id=__span-10-3><a id=__codelineno-10-3 name=__codelineno-10-3 href=#__codelineno-10-3></a><span class=nx>useWebSocket</span><span class=p>({</span>
</span><span id=__span-10-4><a id=__codelineno-10-4 name=__codelineno-10-4 href=#__codelineno-10-4></a><span class=w> </span><span class=nx>onMessage</span><span class=o>:</span><span class=w> </span><span class=p>(</span><span class=nx>msg</span><span class=p>)</span><span class=w> </span><span class=p>=&gt;</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-10-5><a id=__codelineno-10-5 name=__codelineno-10-5 href=#__codelineno-10-5></a><span class=w> </span><span class=k>if</span><span class=w> </span><span class=p>(</span><span class=nx>msg</span><span class=p>.</span><span class=nx>type</span><span class=w> </span><span class=o>===</span><span class=w> </span><span class=s1>&#39;PIPELINE_STATE_CHANGED&#39;</span><span class=p>)</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-10-6><a id=__codelineno-10-6 name=__codelineno-10-6 href=#__codelineno-10-6></a><span class=w> </span><span class=nx>setPipeline</span><span class=p>(</span><span class=nx>msg</span><span class=p>.</span><span class=nx>payload</span><span class=p>);</span>
</span><span id=__span-10-7><a id=__codelineno-10-7 name=__codelineno-10-7 href=#__codelineno-10-7></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-10-8><a id=__codelineno-10-8 name=__codelineno-10-8 href=#__codelineno-10-8></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-10-9><a id=__codelineno-10-9 name=__codelineno-10-9 href=#__codelineno-10-9></a><span class=p>});</span>
</span></code></pre></div> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
</span><span id=__span-8-4><a id=__codelineno-8-4 name=__codelineno-8-4 href=#__codelineno-8-4></a><span class=w> </span><span class=nt>&quot;id&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-8-5><a id=__codelineno-8-5 name=__codelineno-8-5 href=#__codelineno-8-5></a><span class=w> </span><span class=nt>&quot;lastRunStatus&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;running&quot;</span><span class=p>,</span>
</span><span id=__span-8-6><a id=__codelineno-8-6 name=__codelineno-8-6 href=#__codelineno-8-6></a><span class=w> </span><span class=nt>&quot;lastRunAt&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2026-03-10T10:00:00.000Z&quot;</span><span class=p>,</span>
</span><span id=__span-8-7><a id=__codelineno-8-7 name=__codelineno-8-7 href=#__codelineno-8-7></a><span class=w> </span><span class=nt>&quot;nextRunAt&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>null</span>
</span><span id=__span-8-8><a id=__codelineno-8-8 name=__codelineno-8-8 href=#__codelineno-8-8></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-8-9><a id=__codelineno-8-9 name=__codelineno-8-9 href=#__codelineno-8-9></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=reconnect-verhalten>Reconnect-Verhalten<a class=headerlink href=#reconnect-verhalten title="Permanent link">&para;</a></h2> <p><code>useWebSocket</code> verbindet bei Abbruch automatisch neu:</p> <ul> <li>Retry-Intervall: <code>1500ms</code></li> <li>Wiederverbindung bis Komponente unmounted wird</li> </ul> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
+1 -57
View File
@@ -1,57 +1 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/architecture/backend/ rel=canonical><link href=../overview/ rel=prev><link href=../frontend/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Backend-Services - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#backend-services class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Backend-Services </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 Line truncated
START(( )) --&gt; IDLE
IDLE --&gt;|analyzeDisc()| META[METADATA\nSELECTION]
META --&gt;|selectMetadata()| RTS[READY_TO\nSTART]
RTS --&gt;|Auto-Start/Queue| RIP[RIPPING]
RTS --&gt;|Auto-Start mit RAW| MIC[MEDIAINFO\nCHECK]
RIP --&gt;|MKV erstellt| MIC[MEDIAINFO\nCHECK]
MIC --&gt;|Playlist offen| WUD[WAITING_FOR\nUSER_DECISION]
WUD --&gt;|selectMetadata(selectedPlaylist)| MIC
MIC --&gt;|Tracks analysiert| RTE[READY_TO\nENCODE]
RTE --&gt;|confirmEncodeReview() + startPreparedJob()| ENC[ENCODING]
ENC --&gt;|HandBrake + Post-Skripte fertig| FIN([FINISHED])
ENC --&gt;|Abbruch| CAN([CANCELLED])
ENC --&gt;|Fehler| ERR([ERROR])
RIP --&gt;|Fehler| ERR
RIP --&gt;|Abbruch| CAN
ERR --&gt;|retry() / cancel()| IDLE
CAN --&gt;|retry() / analyzeDisc()| IDLE
FIN --&gt;|cancel / neue Disc| IDLE
style FIN fill:#e8f5e9,stroke:#66bb6a,color:#2e7d32
style CAN fill:#fff3e0,stroke:#fb8c00,color:#e65100
style ERR fill:#ffebee,stroke:#ef5350,color:#c62828
style ENC fill:#f3e5f5,stroke:#ab47bc,color:#6a1b9a
style RIP fill:#e3f2fd,stroke:#42a5f5,color:#1565c0
style MIC fill:#e3f2fd,stroke:#42a5f5,color:#1565c0</code></pre> </div> <hr> <h2 id=diskdetectionservicejs>diskDetectionService.js<a class=headerlink href=#diskdetectionservicejs title="Permanent link">&para;</a></h2> <p>Überwacht das Disc-Laufwerk auf Disc-Einleger- und Auswurf-Ereignisse.</p> <h3 id=modi>Modi<a class=headerlink href=#modi title="Permanent link">&para;</a></h3> <table> <thead> <tr> <th>Modus</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>auto</code></td> <td>Erkennt verfügbare Laufwerke automatisch</td> </tr> <tr> <td><code>explicit</code></td> <td>Überwacht ein bestimmtes Gerät (z.B. <code>/dev/sr0</code>)</td> </tr> </tbody> </table> <h3 id=polling>Polling<a class=headerlink href=#polling title="Permanent link">&para;</a></h3> <p>Der Service pollt das Laufwerk im konfigurierten Intervall (<code>disc_poll_interval_ms</code>, Standard: 4000ms) und emittiert Events:</p> <div class="language-js highlight"><pre><span></span><code><span id=__span-0-1><a id=__codelineno-0-1 name=__codelineno-0-1 href=#__codelineno-0-1></a><span class=c1>// Ereignisse</span>
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a><span class=nx>emit</span><span class=p>(</span><span class=s1>&#39;discInserted&#39;</span><span class=p>,</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nx>path</span><span class=o>:</span><span class=w> </span><span class=s1>&#39;/dev/sr0&#39;</span><span class=w> </span><span class=p>})</span>
</span><span id=__span-0-3><a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a><span class=nx>emit</span><span class=p>(</span><span class=s1>&#39;discRemoved&#39;</span><span class=p>,</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nx>path</span><span class=o>:</span><span class=w> </span><span class=s1>&#39;/dev/sr0&#39;</span><span class=w> </span><span class=p>})</span>
</span></code></pre></div> <hr> <h2 id=processrunnerjs>processRunner.js<a class=headerlink href=#processrunnerjs title="Permanent link">&para;</a></h2> <p>Verwaltet externe CLI-Prozesse.</p> <h3 id=features>Features<a class=headerlink href=#features title="Permanent link">&para;</a></h3> <ul> <li><strong>Streaming</strong>: stdout/stderr werden zeilenweise gelesen</li> <li><strong>Progress-Callbacks</strong>: Ermöglicht Echtzeit-Fortschrittsanzeige</li> <li><strong>Graceful Shutdown</strong>: SIGINT → Warte-Timeout → SIGKILL</li> <li><strong>Prozess-Registry</strong>: Verfolgt aktive Prozesse für sauberes Beenden</li> </ul> <h3 id=nutzung>Nutzung<a class=headerlink href=#nutzung title="Permanent link">&para;</a></h3> <div class="language-js highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a><span class=kd>const</span><span class=w> </span><span class=nx>result</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=k>await</span><span class=w> </span><span class=nx>runProcess</span><span class=p>(</span>
</span><span id=__span-1-2><a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a><span class=w> </span><span class=s1>&#39;HandBrakeCLI&#39;</span><span class=p>,</span>
</span><span id=__span-1-3><a id=__codelineno-1-3 name=__codelineno-1-3 href=#__codelineno-1-3></a><span class=w> </span><span class=p>[</span><span class=s1>&#39;--input&#39;</span><span class=p>,</span><span class=w> </span><span class=nx>rawFile</span><span class=p>,</span><span class=w> </span><span class=s1>&#39;--output&#39;</span><span class=p>,</span><span class=w> </span><span class=nx>outputFile</span><span class=p>,</span><span class=w> </span><span class=s1>&#39;--preset&#39;</span><span class=p>,</span><span class=w> </span><span class=nx>preset</span><span class=p>],</span>
</span><span id=__span-1-4><a id=__codelineno-1-4 name=__codelineno-1-4 href=#__codelineno-1-4></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-1-5><a id=__codelineno-1-5 name=__codelineno-1-5 href=#__codelineno-1-5></a><span class=w> </span><span class=nx>onStderr</span><span class=o>:</span><span class=w> </span><span class=p>(</span><span class=nx>line</span><span class=p>)</span><span class=w> </span><span class=p>=&gt;</span><span class=w> </span><span class=nx>parseHandBrakeProgress</span><span class=p>(</span><span class=nx>line</span><span class=p>),</span>
</span><span id=__span-1-6><a id=__codelineno-1-6 name=__codelineno-1-6 href=#__codelineno-1-6></a><span class=w> </span><span class=nx>onStdout</span><span class=o>:</span><span class=w> </span><span class=p>(</span><span class=nx>line</span><span class=p>)</span><span class=w> </span><span class=p>=&gt;</span><span class=w> </span><span class=nx>logger</span><span class=p>.</span><span class=nx>debug</span><span class=p>(</span><span class=nx>line</span><span class=p>)</span>
</span><span id=__span-1-7><a id=__codelineno-1-7 name=__codelineno-1-7 href=#__codelineno-1-7></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-1-8><a id=__codelineno-1-8 name=__codelineno-1-8 href=#__codelineno-1-8></a><span class=p>);</span>
</span></code></pre></div> <hr> <h2 id=websocketservicejs>websocketService.js<a class=headerlink href=#websocketservicejs title="Permanent link">&para;</a></h2> <p>WebSocket-Server für Echtzeit-Client-Kommunikation.</p> <h3 id=betrieb>Betrieb<a class=headerlink href=#betrieb title="Permanent link">&para;</a></h3> <ul> <li>Läuft auf Pfad <code>/ws</code> des Express-Servers</li> <li>Hält eine Registry aller verbundenen Clients</li> <li>Ermöglicht Broadcast an alle Clients oder gezieltes Senden</li> </ul> <h3 id=api>API<a class=headerlink href=#api title="Permanent link">&para;</a></h3> <div class="language-js highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a><span class=nx>broadcast</span><span class=p>(</span><span class=s1>&#39;PIPELINE_STATE_CHANGED&#39;</span><span class=p>,</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nx>state</span><span class=p>,</span><span class=w> </span><span class=nx>activeJobId</span><span class=w> </span><span class=p>});</span>
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a><span class=nx>broadcast</span><span class=p>(</span><span class=s1>&#39;PIPELINE_PROGRESS&#39;</span><span class=p>,</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nx>state</span><span class=p>,</span><span class=w> </span><span class=nx>progress</span><span class=p>,</span><span class=w> </span><span class=nx>eta</span><span class=p>,</span><span class=w> </span><span class=nx>statusText</span><span class=w> </span><span class=p>});</span>
</span><span id=__span-2-3><a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a><span class=nx>broadcast</span><span class=p>(</span><span class=s1>&#39;PIPELINE_QUEUE_CHANGED&#39;</span><span class=p>,</span><span class=w> </span><span class=nx>queueSnapshot</span><span class=p>);</span>
</span></code></pre></div> <hr> <h2 id=omdbservicejs>omdbService.js<a class=headerlink href=#omdbservicejs title="Permanent link">&para;</a></h2> <p>Integration mit der <a href=https://www.omdbapi.com/ >OMDb API</a>.</p> <h3 id=methoden>Methoden<a class=headerlink href=#methoden title="Permanent link">&para;</a></h3> <table> <thead> <tr> <th>Methode</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>searchByTitle(title, type)</code></td> <td>Suche nach Titel (movie/series)</td> </tr> <tr> <td><code>fetchById(imdbId)</code></td> <td>Vollständige Metadaten per IMDb-ID</td> </tr> </tbody> </table> <h3 id=zuruckgegebene-daten>Zurückgegebene Daten<a class=headerlink href=#zuruckgegebene-daten title="Permanent link">&para;</a></h3> <div class="language-json highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a><span class=p>{</span>
</span><span id=__span-3-2><a id=__codelineno-3-2 name=__codelineno-3-2 href=#__codelineno-3-2></a><span class=w> </span><span class=nt>&quot;imdbId&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;tt1375666&quot;</span><span class=p>,</span>
</span><span id=__span-3-3><a id=__codelineno-3-3 name=__codelineno-3-3 href=#__codelineno-3-3></a><span class=w> </span><span class=nt>&quot;title&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Inception&quot;</span><span class=p>,</span>
</span><span id=__span-3-4><a id=__codelineno-3-4 name=__codelineno-3-4 href=#__codelineno-3-4></a><span class=w> </span><span class=nt>&quot;year&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2010&quot;</span><span class=p>,</span>
</span><span id=__span-3-5><a id=__codelineno-3-5 name=__codelineno-3-5 href=#__codelineno-3-5></a><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;movie&quot;</span><span class=p>,</span>
</span><span id=__span-3-6><a id=__codelineno-3-6 name=__codelineno-3-6 href=#__codelineno-3-6></a><span class=w> </span><span class=nt>&quot;poster&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;https://...&quot;</span><span class=p>,</span>
</span><span id=__span-3-7><a id=__codelineno-3-7 name=__codelineno-3-7 href=#__codelineno-3-7></a><span class=w> </span><span class=nt>&quot;plot&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;...&quot;</span><span class=p>,</span>
</span><span id=__span-3-8><a id=__codelineno-3-8 name=__codelineno-3-8 href=#__codelineno-3-8></a><span class=w> </span><span class=nt>&quot;director&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Christopher Nolan&quot;</span>
</span><span id=__span-3-9><a id=__codelineno-3-9 name=__codelineno-3-9 href=#__codelineno-3-9></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=settingsservicejs>settingsService.js<a class=headerlink href=#settingsservicejs title="Permanent link">&para;</a></h2> <p>Verwaltet alle Anwendungseinstellungen.</p> <h3 id=features_1>Features<a class=headerlink href=#features_1 title="Permanent link">&para;</a></h3> <ul> <li><strong>Schema-getriebene Validierung</strong>: Jede Einstellung hat Typ, Grenzen und Pflichtfeld-Flag</li> <li><strong>Kategorisierung</strong>: Einstellungen sind in Kategorien gruppiert (Paths, Tools, Encoding, ...)</li> <li><strong>Persistenz</strong>: Werte in SQLite, Schema ebenfalls in SQLite</li> <li><strong>Defaults</strong>: <code>defaultSettings.js</code> definiert Standardwerte</li> </ul> <h3 id=einstellungs-kategorien>Einstellungs-Kategorien<a class=headerlink href=#einstellungs-kategorien title="Permanent link">&para;</a></h3> <table> <thead> <tr> <th>Kategorie</th> <th>Einstellungen</th> </tr> </thead> <tbody> <tr> <td><code>Pfade</code></td> <td><code>raw_dir</code>, <code>movie_dir</code>, <code>log_dir</code></td> </tr> <tr> <td><code>Laufwerk</code></td> <td><code>drive_mode</code>, <code>drive_device</code>, <code>disc_poll_interval_ms</code>, <code>makemkv_source_index</code></td> </tr> <tr> <td><code>Monitoring</code></td> <td><code>hardware_monitoring_enabled</code>, <code>hardware_monitoring_interval_ms</code></td> </tr> <tr> <td><code>Tools</code></td> <td><code>makemkv_command</code>, <code>handbrake_command</code>, <code>mediainfo_command</code>, <code>pipeline_max_parallel_jobs</code></td> </tr> <tr> <td><code>Metadaten</code></td> <td><code>omdb_api_key</code>, <code>omdb_default_type</code></td> </tr> <tr> <td><code>Benachrichtigungen</code></td> <td><code>pushover_user_key</code>, <code>pushover_api_token</code></td> </tr> </tbody> </table> <hr> <h2 id=historyservicejs>historyService.js<a class=headerlink href=#historyservicejs title="Permanent link">&para;</a></h2> <p>Datenbankoperationen für Job-Historie.</p> <h3 id=hauptoperationen>Hauptoperationen<a class=headerlink href=#hauptoperationen title="Permanent link">&para;</a></h3> <table> <thead> <tr> <th>Operation</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>listJobs(filters)</code></td> <td>Jobs nach Status/Titel filtern</td> </tr> <tr> <td><code>getJob(id)</code></td> <td>Job-Details mit Logs abrufen</td> </tr> <tr> <td><code>findOrphanRawFolders()</code></td> <td>Nicht-getrackte Raw-Ordner finden</td> </tr> <tr> <td><code>importOrphanRaw(path)</code></td> <td>Orphan-Ordner als Job importieren</td> </tr> <tr> <td><code>assignOmdb(id, omdbData)</code></td> <td>OMDb-Metadaten nachträglich zuweisen</td> </tr> <tr> <td><code>deleteJob(id, deleteFiles)</code></td> <td>Job und optional Dateien löschen</td> </tr> </tbody> </table> <hr> <h2 id=notificationservicejs>notificationService.js<a class=headerlink href=#notificationservicejs title="Permanent link">&para;</a></h2> <p>PushOver-Push-Benachrichtigungen.</p> <div class="language-js highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a><span class=k>await</span><span class=w> </span><span class=nx>notify</span><span class=p>({</span>
</span><span id=__span-4-2><a id=__codelineno-4-2 name=__codelineno-4-2 href=#__codelineno-4-2></a><span class=w> </span><span class=nx>title</span><span class=o>:</span><span class=w> </span><span class=s1>&#39;Ripster: Job abgeschlossen&#39;</span><span class=p>,</span>
</span><span id=__span-4-3><a id=__codelineno-4-3 name=__codelineno-4-3 href=#__codelineno-4-3></a><span class=w> </span><span class=nx>message</span><span class=o>:</span><span class=w> </span><span class=s1>&#39;Inception (2010) wurde erfolgreich encodiert&#39;</span>
</span><span id=__span-4-4><a id=__codelineno-4-4 name=__codelineno-4-4 href=#__codelineno-4-4></a><span class=p>});</span>
</span></code></pre></div> <hr> <h2 id=loggerjs>logger.js<a class=headerlink href=#loggerjs title="Permanent link">&para;</a></h2> <p>Strukturiertes Logging mit täglicher Log-Rotation.</p> <h3 id=log-level>Log-Level<a class=headerlink href=#log-level title="Permanent link">&para;</a></h3> <table> <thead> <tr> <th>Level</th> <th>Verwendung</th> </tr> </thead> <tbody> <tr> <td><code>debug</code></td> <td>Detaillierte Entwicklungs-Informationen</td> </tr> <tr> <td><code>info</code></td> <td>Normale Betriebsereignisse</td> </tr> <tr> <td><code>warn</code></td> <td>Warnungen, die Aufmerksamkeit benötigen</td> </tr> <tr> <td><code>error</code></td> <td>Fehler, die den Betrieb beeinträchtigen</td> </tr> </tbody> </table> <h3 id=log-dateien>Log-Dateien<a class=headerlink href=#log-dateien title="Permanent link">&para;</a></h3> <div class="language-text highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a>logs/
</span><span id=__span-5-2><a id=__codelineno-5-2 name=__codelineno-5-2 href=#__codelineno-5-2></a>├── ripster-2024-01-15.log ← Tages-Log
</span><span id=__span-5-3><a id=__codelineno-5-3 name=__codelineno-5-3 href=#__codelineno-5-3></a>└── jobs/
</span><span id=__span-5-4><a id=__codelineno-5-4 name=__codelineno-5-4 href=#__codelineno-5-4></a> └── job-42-handbrake.log ← Prozess-spezifische Logs
</span></code></pre></div> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/architecture/backend/ rel=canonical><link href=../overview/ rel=prev><link href=../frontend/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Backend-Services - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#backend-services class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Backend-Services </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 Line truncated
+15 -65
View File
@@ -1,66 +1,16 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/architecture/database/ rel=canonical><link href=../frontend/ rel=prev><link href=../../pipeline/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Datenbank - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#datenbank class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Datenbank </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.Line truncated
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a><span class=n>settings_schema</span><span class=w> </span><span class=c1>-- Einstellungs-Definitionen</span>
</span><span id=__span-0-3><a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a><span class=n>settings_values</span><span class=w> </span><span class=c1>-- Benutzer-Werte</span>
</span><span id=__span-0-4><a id=__codelineno-0-4 name=__codelineno-0-4 href=#__codelineno-0-4></a><span class=n>jobs</span><span class=w> </span><span class=c1>-- Rip-Job-Datensätze</span>
</span><span id=__span-0-5><a id=__codelineno-0-5 name=__codelineno-0-5 href=#__codelineno-0-5></a><span class=n>pipeline_state</span><span class=w> </span><span class=c1>-- Aktueller Pipeline-Zustand (Singleton)</span>
</span></code></pre></div> <hr> <h2 id=tabelle-jobs>Tabelle: jobs<a class=headerlink href=#tabelle-jobs title="Permanent link">&para;</a></h2> <p>Die wichtigste Tabelle – speichert alle Ripping-Jobs.</p> <div class="language-sql highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a><span class=k>CREATE</span><span class=w> </span><span class=k>TABLE</span><span class=w> </span><span class=n>jobs</span><span class=w> </span><span class=p>(</span>
</span><span id=__span-1-2><a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a><span class=w> </span><span class=n>id</span><span class=w> </span><span class=nb>INTEGER</span><span class=w> </span><span class=k>PRIMARY</span><span class=w> </span><span class=k>KEY</span><span class=w> </span><span class=n>AUTOINCREMENT</span><span class=p>,</span>
</span><span id=__span-1-3><a id=__codelineno-1-3 name=__codelineno-1-3 href=#__codelineno-1-3></a><span class=w> </span><span class=n>created_at</span><span class=w> </span><span class=nb>TEXT</span><span class=w> </span><span class=k>NOT</span><span class=w> </span><span class=k>NULL</span><span class=p>,</span>
</span><span id=__span-1-4><a id=__codelineno-1-4 name=__codelineno-1-4 href=#__codelineno-1-4></a><span class=w> </span><span class=n>updated_at</span><span class=w> </span><span class=nb>TEXT</span><span class=w> </span><span class=k>NOT</span><span class=w> </span><span class=k>NULL</span><span class=p>,</span>
</span><span id=__span-1-5><a id=__codelineno-1-5 name=__codelineno-1-5 href=#__codelineno-1-5></a><span class=w> </span><span class=n>status</span><span class=w> </span><span class=nb>TEXT</span><span class=w> </span><span class=k>NOT</span><span class=w> </span><span class=k>NULL</span><span class=p>,</span><span class=w> </span><span class=c1>-- Aktueller Status</span>
</span><span id=__span-1-6><a id=__codelineno-1-6 name=__codelineno-1-6 href=#__codelineno-1-6></a><span class=w> </span><span class=n>title</span><span class=w> </span><span class=nb>TEXT</span><span class=p>,</span><span class=w> </span><span class=c1>-- Filmtitel (von OMDb)</span>
</span><span id=__span-1-7><a id=__codelineno-1-7 name=__codelineno-1-7 href=#__codelineno-1-7></a><span class=w> </span><span class=n>imdb_id</span><span class=w> </span><span class=nb>TEXT</span><span class=p>,</span><span class=w> </span><span class=c1>-- IMDb-ID</span>
</span><span id=__span-1-8><a id=__codelineno-1-8 name=__codelineno-1-8 href=#__codelineno-1-8></a><span class=w> </span><span class=n>omdb_year</span><span class=w> </span><span class=nb>TEXT</span><span class=p>,</span><span class=w> </span><span class=c1>-- Erscheinungsjahr</span>
</span><span id=__span-1-9><a id=__codelineno-1-9 name=__codelineno-1-9 href=#__codelineno-1-9></a><span class=w> </span><span class=n>omdb_type</span><span class=w> </span><span class=nb>TEXT</span><span class=p>,</span><span class=w> </span><span class=c1>-- movie/series</span>
</span><span id=__span-1-10><a id=__codelineno-1-10 name=__codelineno-1-10 href=#__codelineno-1-10></a><span class=w> </span><span class=n>omdb_poster</span><span class=w> </span><span class=nb>TEXT</span><span class=p>,</span><span class=w> </span><span class=c1>-- Poster-URL</span>
</span><span id=__span-1-11><a id=__codelineno-1-11 name=__codelineno-1-11 href=#__codelineno-1-11></a><span class=w> </span><span class=n>raw_path</span><span class=w> </span><span class=nb>TEXT</span><span class=p>,</span><span class=w> </span><span class=c1>-- Pfad zur Raw-MKV</span>
</span><span id=__span-1-12><a id=__codelineno-1-12 name=__codelineno-1-12 href=#__codelineno-1-12></a><span class=w> </span><span class=n>output_path</span><span class=w> </span><span class=nb>TEXT</span><span class=p>,</span><span class=w> </span><span class=c1>-- Pfad zur Ausgabedatei</span>
</span><span id=__span-1-13><a id=__codelineno-1-13 name=__codelineno-1-13 href=#__codelineno-1-13></a><span class=w> </span><span class=n>playlist</span><span class=w> </span><span class=nb>TEXT</span><span class=p>,</span><span class=w> </span><span class=c1>-- Gewählte Blu-ray Playlist</span>
</span><span id=__span-1-14><a id=__codelineno-1-14 name=__codelineno-1-14 href=#__codelineno-1-14></a><span class=w> </span><span class=n>makemkv_output</span><span class=w> </span><span class=nb>TEXT</span><span class=p>,</span><span class=w> </span><span class=c1>-- MakeMKV-Ausgabe (JSON)</span>
</span><span id=__span-1-15><a id=__codelineno-1-15 name=__codelineno-1-15 href=#__codelineno-1-15></a><span class=w> </span><span class=n>mediainfo_output</span><span class=w> </span><span class=nb>TEXT</span><span class=p>,</span><span class=w> </span><span class=c1>-- MediaInfo-Ausgabe (JSON)</span>
</span><span id=__span-1-16><a id=__codelineno-1-16 name=__codelineno-1-16 href=#__codelineno-1-16></a><span class=w> </span><span class=n>encode_plan</span><span class=w> </span><span class=nb>TEXT</span><span class=p>,</span><span class=w> </span><span class=c1>-- Encode-Plan (JSON)</span>
</span><span id=__span-1-17><a id=__codelineno-1-17 name=__codelineno-1-17 href=#__codelineno-1-17></a><span class=w> </span><span class=n>handbrake_log</span><span class=w> </span><span class=nb>TEXT</span><span class=p>,</span><span class=w> </span><span class=c1>-- HandBrake Log-Pfad</span>
</span><span id=__span-1-18><a id=__codelineno-1-18 name=__codelineno-1-18 href=#__codelineno-1-18></a><span class=w> </span><span class=n>error_message</span><span class=w> </span><span class=nb>TEXT</span><span class=p>,</span><span class=w> </span><span class=c1>-- Fehlermeldung bei ERROR</span>
</span><span id=__span-1-19><a id=__codelineno-1-19 name=__codelineno-1-19 href=#__codelineno-1-19></a><span class=w> </span><span class=n>error_details</span><span class=w> </span><span class=nb>TEXT</span><span class=w> </span><span class=c1>-- Detaillierte Fehler-Infos</span>
</span><span id=__span-1-20><a id=__codelineno-1-20 name=__codelineno-1-20 href=#__codelineno-1-20></a><span class=p>);</span>
</span></code></pre></div> <h3 id=job-status-werte>Job-Status-Werte<a class=headerlink href=#job-status-werte title="Permanent link">&para;</a></h3> <table> <thead> <tr> <th>Status</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>ANALYZING</code></td> <td>MakeMKV analysiert die Disc</td> </tr> <tr> <td><code>METADATA_SELECTION</code></td> <td>Wartet auf Benutzer-Metadaten-Auswahl</td> </tr> <tr> <td><code>READY_TO_START</code></td> <td>Bereit zum Starten</td> </tr> <tr> <td><code>RIPPING</code></td> <td>MakeMKV rippt die Disc</td> </tr> <tr> <td><code>MEDIAINFO_CHECK</code></td> <td>MediaInfo analysiert die Raw-Datei</td> </tr> <tr> <td><code>READY_TO_ENCODE</code></td> <td>Wartet auf Encode-Bestätigung</td> </tr> <tr> <td><code>ENCODING</code></td> <td>HandBrake encodiert</td> </tr> <tr> <td><code>FINISHED</code></td> <td>Erfolgreich abgeschlossen</td> </tr> <tr> <td><code>ERROR</code></td> <td>Fehler aufgetreten</td> </tr> </tbody> </table> <hr> <h2 id=tabelle-pipeline_state>Tabelle: pipeline_state<a class=headerlink href=#tabelle-pipeline_state title="Permanent link">&para;</a></h2> <p>Singleton-Tabelle für den aktuellen Pipeline-Zustand (immer genau 1 Zeile).</p> <div class="language-sql highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a><span class=k>CREATE</span><span class=w> </span><span class=k>TABLE</span><span class=w> </span><span class=n>pipeline_state</span><span class=w> </span><span class=p>(</span>
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a><span class=w> </span><span class=n>id</span><span class=w> </span><span class=nb>INTEGER</span><span class=w> </span><span class=k>PRIMARY</span><span class=w> </span><span class=k>KEY</span><span class=w> </span><span class=k>CHECK</span><span class=p>(</span><span class=n>id</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=mi>1</span><span class=p>),</span>
</span><span id=__span-2-3><a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a><span class=w> </span><span class=k>state</span><span class=w> </span><span class=nb>TEXT</span><span class=w> </span><span class=k>NOT</span><span class=w> </span><span class=k>NULL</span><span class=w> </span><span class=k>DEFAULT</span><span class=w> </span><span class=s1>&#39;IDLE&#39;</span><span class=p>,</span>
</span><span id=__span-2-4><a id=__codelineno-2-4 name=__codelineno-2-4 href=#__codelineno-2-4></a><span class=w> </span><span class=n>job_id</span><span class=w> </span><span class=nb>INTEGER</span><span class=p>,</span><span class=w> </span><span class=c1>-- Aktiver Job (NULL wenn IDLE)</span>
</span><span id=__span-2-5><a id=__codelineno-2-5 name=__codelineno-2-5 href=#__codelineno-2-5></a><span class=w> </span><span class=n>progress</span><span class=w> </span><span class=nb>REAL</span><span class=p>,</span><span class=w> </span><span class=c1>-- Fortschritt 0-100</span>
</span><span id=__span-2-6><a id=__codelineno-2-6 name=__codelineno-2-6 href=#__codelineno-2-6></a><span class=w> </span><span class=n>eta</span><span class=w> </span><span class=nb>TEXT</span><span class=p>,</span><span class=w> </span><span class=c1>-- Geschätzte Restzeit</span>
</span><span id=__span-2-7><a id=__codelineno-2-7 name=__codelineno-2-7 href=#__codelineno-2-7></a><span class=w> </span><span class=n>updated_at</span><span class=w> </span><span class=nb>TEXT</span><span class=w> </span><span class=k>NOT</span><span class=w> </span><span class=k>NULL</span>
</span><span id=__span-2-8><a id=__codelineno-2-8 name=__codelineno-2-8 href=#__codelineno-2-8></a><span class=p>);</span>
</span></code></pre></div> <hr> <h2 id=tabelle-settings_schema>Tabelle: settings_schema<a class=headerlink href=#tabelle-settings_schema title="Permanent link">&para;</a></h2> <p>Definiert alle verfügbaren Einstellungen mit Metadaten.</p> <div class="language-sql highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a><span class=k>CREATE</span><span class=w> </span><span class=k>TABLE</span><span class=w> </span><span class=n>settings_schema</span><span class=w> </span><span class=p>(</span>
</span><span id=__span-3-2><a id=__codelineno-3-2 name=__codelineno-3-2 href=#__codelineno-3-2></a><span class=w> </span><span class=k>key</span><span class=w> </span><span class=nb>TEXT</span><span class=w> </span><span class=k>PRIMARY</span><span class=w> </span><span class=k>KEY</span><span class=p>,</span>
</span><span id=__span-3-3><a id=__codelineno-3-3 name=__codelineno-3-3 href=#__codelineno-3-3></a><span class=w> </span><span class=n>category</span><span class=w> </span><span class=nb>TEXT</span><span class=w> </span><span class=k>NOT</span><span class=w> </span><span class=k>NULL</span><span class=p>,</span><span class=w> </span><span class=c1>-- paths, tools, encoding, ...</span>
</span><span id=__span-3-4><a id=__codelineno-3-4 name=__codelineno-3-4 href=#__codelineno-3-4></a><span class=w> </span><span class=k>type</span><span class=w> </span><span class=nb>TEXT</span><span class=w> </span><span class=k>NOT</span><span class=w> </span><span class=k>NULL</span><span class=p>,</span><span class=w> </span><span class=c1>-- string, number, boolean, select</span>
</span><span id=__span-3-5><a id=__codelineno-3-5 name=__codelineno-3-5 href=#__codelineno-3-5></a><span class=w> </span><span class=n>label</span><span class=w> </span><span class=nb>TEXT</span><span class=w> </span><span class=k>NOT</span><span class=w> </span><span class=k>NULL</span><span class=p>,</span><span class=w> </span><span class=c1>-- Anzeigename</span>
</span><span id=__span-3-6><a id=__codelineno-3-6 name=__codelineno-3-6 href=#__codelineno-3-6></a><span class=w> </span><span class=n>description</span><span class=w> </span><span class=nb>TEXT</span><span class=p>,</span><span class=w> </span><span class=c1>-- Hilfetext</span>
</span><span id=__span-3-7><a id=__codelineno-3-7 name=__codelineno-3-7 href=#__codelineno-3-7></a><span class=w> </span><span class=n>default_val</span><span class=w> </span><span class=nb>TEXT</span><span class=p>,</span><span class=w> </span><span class=c1>-- Standardwert</span>
</span><span id=__span-3-8><a id=__codelineno-3-8 name=__codelineno-3-8 href=#__codelineno-3-8></a><span class=w> </span><span class=n>required</span><span class=w> </span><span class=nb>INTEGER</span><span class=p>,</span><span class=w> </span><span class=c1>-- 1 = Pflichtfeld</span>
</span><span id=__span-3-9><a id=__codelineno-3-9 name=__codelineno-3-9 href=#__codelineno-3-9></a><span class=w> </span><span class=n>min_val</span><span class=w> </span><span class=nb>REAL</span><span class=p>,</span><span class=w> </span><span class=c1>-- Minimalwert (für number)</span>
</span><span id=__span-3-10><a id=__codelineno-3-10 name=__codelineno-3-10 href=#__codelineno-3-10></a><span class=w> </span><span class=n>max_val</span><span class=w> </span><span class=nb>REAL</span><span class=p>,</span><span class=w> </span><span class=c1>-- Maximalwert (für number)</span>
</span><span id=__span-3-11><a id=__codelineno-3-11 name=__codelineno-3-11 href=#__codelineno-3-11></a><span class=w> </span><span class=k>options</span><span class=w> </span><span class=nb>TEXT</span><span class=w> </span><span class=c1>-- JSON-Array für select-Typ</span>
</span><span id=__span-3-12><a id=__codelineno-3-12 name=__codelineno-3-12 href=#__codelineno-3-12></a><span class=p>);</span>
</span></code></pre></div> <hr> <h2 id=tabelle-settings_values>Tabelle: settings_values<a class=headerlink href=#tabelle-settings_values title="Permanent link">&para;</a></h2> <p>Speichert benutzer-konfigurierte Werte.</p> <div class="language-sql highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a><span class=k>CREATE</span><span class=w> </span><span class=k>TABLE</span><span class=w> </span><span class=n>settings_values</span><span class=w> </span><span class=p>(</span>
</span><span id=__span-4-2><a id=__codelineno-4-2 name=__codelineno-4-2 href=#__codelineno-4-2></a><span class=w> </span><span class=k>key</span><span class=w> </span><span class=nb>TEXT</span><span class=w> </span><span class=k>PRIMARY</span><span class=w> </span><span class=k>KEY</span><span class=w> </span><span class=k>REFERENCES</span><span class=w> </span><span class=n>settings_schema</span><span class=p>(</span><span class=k>key</span><span class=p>),</span>
</span><span id=__span-4-3><a id=__codelineno-4-3 name=__codelineno-4-3 href=#__codelineno-4-3></a><span class=w> </span><span class=n>value</span><span class=w> </span><span class=nb>TEXT</span><span class=w> </span><span class=k>NOT</span><span class=w> </span><span class=k>NULL</span><span class=p>,</span>
</span><span id=__span-4-4><a id=__codelineno-4-4 name=__codelineno-4-4 href=#__codelineno-4-4></a><span class=w> </span><span class=n>updated_at</span><span class=w> </span><span class=nb>TEXT</span><span class=w> </span><span class=k>NOT</span><span class=w> </span><span class=k>NULL</span>
</span><span id=__span-4-5><a id=__codelineno-4-5 name=__codelineno-4-5 href=#__codelineno-4-5></a><span class=p>);</span>
</span></code></pre></div> <hr> <h2 id=schema-migrationen>Schema-Migrationen<a class=headerlink href=#schema-migrationen title="Permanent link">&para;</a></h2> <p><code>database.js</code> implementiert <strong>automatische Migrationen</strong>:</p> <ol> <li>Beim Start wird das aktuelle Schema geprüft</li> <li>Fehlende Tabellen werden erstellt</li> <li>Fehlende Spalten werden hinzugefügt</li> <li>Neue Default-Einstellungen werden eingefügt</li> </ol> <h3 id=korruptions-recovery>Korruptions-Recovery<a class=headerlink href=#korruptions-recovery title="Permanent link">&para;</a></h3> <p>Falls die Datenbankdatei korrupt ist:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a>1. Korrupte Datei wird erkannt (Verbindungsfehler / Integritätsprüfung)
</span><span id=__span-5-2><a id=__codelineno-5-2 name=__codelineno-5-2 href=#__codelineno-5-2></a>2. Datei wird in /backend/data/quarantine/ verschoben
</span><span id=__span-5-3><a id=__codelineno-5-3 name=__codelineno-5-3 href=#__codelineno-5-3></a>3. Neue, leere Datenbank wird erstellt
</span><span id=__span-5-4><a id=__codelineno-5-4 name=__codelineno-5-4 href=#__codelineno-5-4></a>4. Schema wird neu initialisiert
</span><span id=__span-5-5><a id=__codelineno-5-5 name=__codelineno-5-5 href=#__codelineno-5-5></a>5. Log-Eintrag mit Warnung
</span></code></pre></div> <hr> <h2 id=datenbankpfad-konfigurieren>Datenbankpfad konfigurieren<a class=headerlink href=#datenbankpfad-konfigurieren title="Permanent link">&para;</a></h2> <p>Standard: <code>./data/ripster.db</code> (relativ zum Backend-Verzeichnis)</p> <p>Über Umgebungsvariable anpassen:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-6-1><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a>DB_PATH=/var/lib/ripster/ripster.db
</span></code></pre></div> <hr> <h2 id=direkte-datenbankinspektion>Direkte Datenbankinspektion<a class=headerlink href=#direkte-datenbankinspektion title="Permanent link">&para;</a></h2> <div class="language-bash highlight"><pre><span></span><code><span id=__span-7-1><a id=__codelineno-7-1 name=__codelineno-7-1 href=#__codelineno-7-1></a><span class=c1># SQLite3-CLI</span>
</span><span id=__span-7-2><a id=__codelineno-7-2 name=__codelineno-7-2 href=#__codelineno-7-2></a>sqlite3<span class=w> </span>backend/data/ripster.db
</span><span id=__span-7-3><a id=__codelineno-7-3 name=__codelineno-7-3 href=#__codelineno-7-3></a>
</span><span id=__span-7-4><a id=__codelineno-7-4 name=__codelineno-7-4 href=#__codelineno-7-4></a><span class=c1># Alle Jobs anzeigen</span>
</span><span id=__span-7-5><a id=__codelineno-7-5 name=__codelineno-7-5 href=#__codelineno-7-5></a>.mode<span class=w> </span>table
</span><span id=__span-7-6><a id=__codelineno-7-6 name=__codelineno-7-6 href=#__codelineno-7-6></a>SELECT<span class=w> </span>id,<span class=w> </span>status,<span class=w> </span>title,<span class=w> </span>created_at<span class=w> </span>FROM<span class=w> </span><span class=nb>jobs</span><span class=w> </span>ORDER<span class=w> </span>BY<span class=w> </span>created_at<span class=w> </span>DESC<span class=p>;</span>
</span><span id=__span-7-7><a id=__codelineno-7-7 name=__codelineno-7-7 href=#__codelineno-7-7></a>
</span><span id=__span-7-8><a id=__codelineno-7-8 name=__codelineno-7-8 href=#__codelineno-7-8></a><span class=c1># Einstellungen anzeigen</span>
</span><span id=__span-7-9><a id=__codelineno-7-9 name=__codelineno-7-9 href=#__codelineno-7-9></a>SELECT<span class=w> </span>key,<span class=w> </span>value<span class=w> </span>FROM<span class=w> </span>settings_values<span class=p>;</span>
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/architecture/database/ rel=canonical><link href=../frontend/ rel=prev><link href=../../pipeline/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Datenbank - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#datenbank class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Datenbank </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.Line truncated
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a>settings_values
</span><span id=__span-0-3><a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a>jobs
</span><span id=__span-0-4><a id=__codelineno-0-4 name=__codelineno-0-4 href=#__codelineno-0-4></a>pipeline_state
</span><span id=__span-0-5><a id=__codelineno-0-5 name=__codelineno-0-5 href=#__codelineno-0-5></a>scripts
</span><span id=__span-0-6><a id=__codelineno-0-6 name=__codelineno-0-6 href=#__codelineno-0-6></a>script_chains
</span><span id=__span-0-7><a id=__codelineno-0-7 name=__codelineno-0-7 href=#__codelineno-0-7></a>script_chain_steps
</span><span id=__span-0-8><a id=__codelineno-0-8 name=__codelineno-0-8 href=#__codelineno-0-8></a>user_presets
</span><span id=__span-0-9><a id=__codelineno-0-9 name=__codelineno-0-9 href=#__codelineno-0-9></a>cron_jobs
</span><span id=__span-0-10><a id=__codelineno-0-10 name=__codelineno-0-10 href=#__codelineno-0-10></a>cron_run_logs
</span></code></pre></div> <hr> <h2 id=jobs><code>jobs</code><a class=headerlink href=#jobs title="Permanent link">&para;</a></h2> <p>Speichert Pipeline-Lifecycle und Artefakte pro Job.</p> <p>Zentrale Felder:</p> <ul> <li>Metadaten: <code>title</code>, <code>year</code>, <code>imdb_id</code>, <code>poster_url</code>, <code>omdb_json</code>, <code>selected_from_omdb</code></li> <li>Laufzeit: <code>start_time</code>, <code>end_time</code>, <code>status</code>, <code>last_state</code></li> <li>Pfade: <code>raw_path</code>, <code>output_path</code>, <code>encode_input_path</code></li> <li>Tool-Ausgaben: <code>makemkv_info_json</code>, <code>handbrake_info_json</code>, <code>mediainfo_info_json</code>, <code>encode_plan_json</code></li> <li>Kontrolle: <code>encode_review_confirmed</code>, <code>rip_successful</code>, <code>error_message</code></li> <li>Audit: <code>created_at</code>, <code>updated_at</code></li> </ul> <hr> <h2 id=pipeline_state><code>pipeline_state</code><a class=headerlink href=#pipeline_state title="Permanent link">&para;</a></h2> <p>Singleton-Tabelle (<code>id = 1</code>) für aktiven Snapshot:</p> <ul> <li><code>state</code></li> <li><code>active_job_id</code></li> <li><code>progress</code></li> <li><code>eta</code></li> <li><code>status_text</code></li> <li><code>context_json</code></li> <li><code>updated_at</code></li> </ul> <hr> <h2 id=settings_schema-settings_values><code>settings_schema</code> + <code>settings_values</code><a class=headerlink href=#settings_schema-settings_values title="Permanent link">&para;</a></h2> <ul> <li><code>settings_schema</code>: Definition (Typ, Default, Validation, Reihenfolge)</li> <li><code>settings_values</code>: aktueller Wert pro Key</li> </ul> <hr> <h2 id=scripts-script_chains-script_chain_steps><code>scripts</code>, <code>script_chains</code>, <code>script_chain_steps</code><a class=headerlink href=#scripts-script_chains-script_chain_steps title="Permanent link">&para;</a></h2> <ul> <li><code>scripts</code>: Shell-Skripte (<code>name</code>, <code>script_body</code>, <code>order_index</code>)</li> <li><code>script_chains</code>: Ketten (<code>name</code>, <code>order_index</code>)</li> <li><code>script_chain_steps</code>: Schritte je Kette</li> <li><code>step_type</code>: <code>script</code> oder <code>wait</code></li> <li><code>script_id</code> oder <code>wait_seconds</code></li> </ul> <hr> <h2 id=user_presets><code>user_presets</code><a class=headerlink href=#user_presets title="Permanent link">&para;</a></h2> <p>Benannte HandBrake-Preset-Sets:</p> <ul> <li><code>name</code></li> <li><code>media_type</code> (<code>bluray|dvd|other|all</code>)</li> <li><code>handbrake_preset</code></li> <li><code>extra_args</code></li> <li><code>description</code></li> </ul> <hr> <h2 id=cron_jobs-cron_run_logs><code>cron_jobs</code> + <code>cron_run_logs</code><a class=headerlink href=#cron_jobs-cron_run_logs title="Permanent link">&para;</a></h2> <ul> <li><code>cron_jobs</code>: Zeitplan + Status</li> <li><code>cron_run_logs</code>: einzelne Läufe</li> <li><code>status</code>: <code>running|success|error</code></li> <li><code>output</code></li> <li><code>error_message</code></li> </ul> <hr> <h2 id=migrationrecovery>Migration/Recovery<a class=headerlink href=#migrationrecovery title="Permanent link">&para;</a></h2> <p>Beim Start werden Schema und Settings-Metadaten automatisch abgeglichen.</p> <p>Bei korruptem SQLite-File:</p> <ol> <li>Datei wird nach <code>backend/data/corrupt-backups/</code> verschoben</li> <li>neue DB wird initialisiert</li> <li>Schema wird neu aufgebaut</li> </ol> <hr> <h2 id=direkte-inspektion>Direkte Inspektion<a class=headerlink href=#direkte-inspektion title="Permanent link">&para;</a></h2> <div class="language-bash highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a>sqlite3<span class=w> </span>backend/data/ripster.db
</span><span id=__span-1-2><a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a>
</span><span id=__span-1-3><a id=__codelineno-1-3 name=__codelineno-1-3 href=#__codelineno-1-3></a>.mode<span class=w> </span>table
</span><span id=__span-1-4><a id=__codelineno-1-4 name=__codelineno-1-4 href=#__codelineno-1-4></a>SELECT<span class=w> </span>id,<span class=w> </span>status,<span class=w> </span>title,<span class=w> </span>created_at<span class=w> </span>FROM<span class=w> </span><span class=nb>jobs</span><span class=w> </span>ORDER<span class=w> </span>BY<span class=w> </span>created_at<span class=w> </span>DESC<span class=p>;</span>
</span><span id=__span-1-5><a id=__codelineno-1-5 name=__codelineno-1-5 href=#__codelineno-1-5></a>SELECT<span class=w> </span>key,<span class=w> </span>value<span class=w> </span>FROM<span class=w> </span>settings_values<span class=w> </span>ORDER<span class=w> </span>BY<span class=w> </span>key<span class=p>;</span>
</span></code></pre></div> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
+5 -55
View File
@@ -1,56 +1,6 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/architecture/frontend/ rel=canonical><link href=../backend/ rel=prev><link href=../database/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Frontend-Komponenten - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#frontend-komponenten class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Frontend-Komponenten </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.Line truncated
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a>│ Metadaten auswählen │
</span><span id=__span-0-3><a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a>├─────────────────────────────────────┤
</span><span id=__span-0-4><a id=__codelineno-0-4 name=__codelineno-0-4 href=#__codelineno-0-4></a>│ Suche: [Inception ] 🔍 │
</span><span id=__span-0-5><a id=__codelineno-0-5 name=__codelineno-0-5 href=#__codelineno-0-5></a>├─────────────────────────────────────┤
</span><span id=__span-0-6><a id=__codelineno-0-6 name=__codelineno-0-6 href=#__codelineno-0-6></a>│ Ergebnisse: │
</span><span id=__span-0-7><a id=__codelineno-0-7 name=__codelineno-0-7 href=#__codelineno-0-7></a>│ ▶ Inception (2010) – Movie │
</span><span id=__span-0-8><a id=__codelineno-0-8 name=__codelineno-0-8 href=#__codelineno-0-8></a>│ Inception: ... (2011) – Series │
</span><span id=__span-0-9><a id=__codelineno-0-9 name=__codelineno-0-9 href=#__codelineno-0-9></a>├─────────────────────────────────────┤
</span><span id=__span-0-10><a id=__codelineno-0-10 name=__codelineno-0-10 href=#__codelineno-0-10></a>│ Playlist (nur Blu-ray): │
</span><span id=__span-0-11><a id=__codelineno-0-11 name=__codelineno-0-11 href=#__codelineno-0-11></a>│ ▶ 00800.mpls (2:30:15) ✓ Empfohlen │
</span><span id=__span-0-12><a id=__codelineno-0-12 name=__codelineno-0-12 href=#__codelineno-0-12></a>│ 00801.mpls (0:01:23) │
</span><span id=__span-0-13><a id=__codelineno-0-13 name=__codelineno-0-13 href=#__codelineno-0-13></a>├─────────────────────────────────────┤
</span><span id=__span-0-14><a id=__codelineno-0-14 name=__codelineno-0-14 href=#__codelineno-0-14></a>│ [Bestätigen] │
</span><span id=__span-0-15><a id=__codelineno-0-15 name=__codelineno-0-15 href=#__codelineno-0-15></a>└─────────────────────────────────────┘
</span></code></pre></div> <h3 id=mediainforeviewpaneljsx>MediaInfoReviewPanel.jsx<a class=headerlink href=#mediainforeviewpaneljsx title="Permanent link">&para;</a></h3> <p>Track-Auswahl-Panel vor dem Encoding.</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a>┌─────────────────────────────────────┐
</span><span id=__span-1-2><a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a>│ Encode-Review │
</span><span id=__span-1-3><a id=__codelineno-1-3 name=__codelineno-1-3 href=#__codelineno-1-3></a>├─────────────────────────────────────┤
</span><span id=__span-1-4><a id=__codelineno-1-4 name=__codelineno-1-4 href=#__codelineno-1-4></a>│ Audio-Tracks: │
</span><span id=__span-1-5><a id=__codelineno-1-5 name=__codelineno-1-5 href=#__codelineno-1-5></a>│ ☑ Track 1: Deutsch (AC-3, 5.1) │
</span><span id=__span-1-6><a id=__codelineno-1-6 name=__codelineno-1-6 href=#__codelineno-1-6></a>│ ☑ Track 2: English (TrueHD, 7.1) │
</span><span id=__span-1-7><a id=__codelineno-1-7 name=__codelineno-1-7 href=#__codelineno-1-7></a>│ ☐ Track 3: Français (AC-3, 2.0) │
</span><span id=__span-1-8><a id=__codelineno-1-8 name=__codelineno-1-8 href=#__codelineno-1-8></a>├─────────────────────────────────────┤
</span><span id=__span-1-9><a id=__codelineno-1-9 name=__codelineno-1-9 href=#__codelineno-1-9></a>│ Untertitel: │
</span><span id=__span-1-10><a id=__codelineno-1-10 name=__codelineno-1-10 href=#__codelineno-1-10></a>│ ☑ Track 1: Deutsch │
</span><span id=__span-1-11><a id=__codelineno-1-11 name=__codelineno-1-11 href=#__codelineno-1-11></a>│ ☐ Track 2: English │
</span><span id=__span-1-12><a id=__codelineno-1-12 name=__codelineno-1-12 href=#__codelineno-1-12></a>├─────────────────────────────────────┤
</span><span id=__span-1-13><a id=__codelineno-1-13 name=__codelineno-1-13 href=#__codelineno-1-13></a>│ [Encoding starten] │
</span><span id=__span-1-14><a id=__codelineno-1-14 name=__codelineno-1-14 href=#__codelineno-1-14></a>└─────────────────────────────────────┘
</span></code></pre></div> <h3 id=dynamicsettingsformjsx>DynamicSettingsForm.jsx<a class=headerlink href=#dynamicsettingsformjsx title="Permanent link">&para;</a></h3> <p>Wiederverwendbares Formular, das aus dem Settings-Schema generiert wird.</p> <p><strong>Unterstützte Feldtypen:</strong></p> <table> <thead> <tr> <th>Typ</th> <th>UI-Element</th> </tr> </thead> <tbody> <tr> <td><code>string</code></td> <td>Text-Input</td> </tr> <tr> <td><code>number</code></td> <td>Zahlen-Input mit Min/Max</td> </tr> <tr> <td><code>boolean</code></td> <td>Toggle/Checkbox</td> </tr> <tr> <td><code>select</code></td> <td>Dropdown</td> </tr> <tr> <td><code>password</code></td> <td>Passwort-Input</td> </tr> </tbody> </table> <h3 id=pipelinestatuscardjsx>PipelineStatusCard.jsx<a class=headerlink href=#pipelinestatuscardjsx title="Permanent link">&para;</a></h3> <p>Status-Anzeige-Widget für die Dashboard-Seite.</p> <h3 id=jobdetaildialogjsx>JobDetailDialog.jsx<a class=headerlink href=#jobdetaildialogjsx title="Permanent link">&para;</a></h3> <p>Vollständiger Job-Detail-Dialog mit Logs-Viewer.</p> <hr> <h2 id=hooks>Hooks<a class=headerlink href=#hooks title="Permanent link">&para;</a></h2> <h3 id=usewebsocketjs>useWebSocket.js<a class=headerlink href=#usewebsocketjs title="Permanent link">&para;</a></h3> <p>Zentraler Custom-Hook für die WebSocket-Verbindung.</p> <div class="language-js highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a><span class=nx>useWebSocket</span><span class=p>({</span>
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a><span class=w> </span><span class=nx>onMessage</span><span class=o>:</span><span class=w> </span><span class=p>(</span><span class=nx>msg</span><span class=p>)</span><span class=w> </span><span class=p>=&gt;</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-2-3><a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a><span class=w> </span><span class=k>if</span><span class=w> </span><span class=p>(</span><span class=nx>msg</span><span class=p>.</span><span class=nx>type</span><span class=w> </span><span class=o>===</span><span class=w> </span><span class=s1>&#39;PIPELINE_STATE_CHANGED&#39;</span><span class=p>)</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-2-4><a id=__codelineno-2-4 name=__codelineno-2-4 href=#__codelineno-2-4></a><span class=w> </span><span class=nx>setPipelineState</span><span class=p>(</span><span class=nx>msg</span><span class=p>.</span><span class=nx>payload</span><span class=p>);</span>
</span><span id=__span-2-5><a id=__codelineno-2-5 name=__codelineno-2-5 href=#__codelineno-2-5></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-2-6><a id=__codelineno-2-6 name=__codelineno-2-6 href=#__codelineno-2-6></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-2-7><a id=__codelineno-2-7 name=__codelineno-2-7 href=#__codelineno-2-7></a><span class=p>});</span>
</span></code></pre></div> <p><strong>Features:</strong> - Automatische Verbindung zu <code>/ws</code> - Reconnect mit festem Intervall (<code>1500ms</code>) - Message-Parsing (JSON)</p> <hr> <h2 id=api-client-clientjs>API-Client (client.js)<a class=headerlink href=#api-client-clientjs title="Permanent link">&para;</a></h2> <p>Zentraler HTTP-Client für alle Backend-Anfragen.</p> <div class="language-js highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a><span class=c1>// Beispiel-Aufrufe</span>
</span><span id=__span-3-2><a id=__codelineno-3-2 name=__codelineno-3-2 href=#__codelineno-3-2></a><span class=kd>const</span><span class=w> </span><span class=nx>state</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=k>await</span><span class=w> </span><span class=nx>api</span><span class=p>.</span><span class=nx>getPipelineState</span><span class=p>();</span>
</span><span id=__span-3-3><a id=__codelineno-3-3 name=__codelineno-3-3 href=#__codelineno-3-3></a><span class=kd>const</span><span class=w> </span><span class=nx>results</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=k>await</span><span class=w> </span><span class=nx>api</span><span class=p>.</span><span class=nx>searchOmdb</span><span class=p>(</span><span class=s1>&#39;Inception&#39;</span><span class=p>);</span>
</span><span id=__span-3-4><a id=__codelineno-3-4 name=__codelineno-3-4 href=#__codelineno-3-4></a><span class=k>await</span><span class=w> </span><span class=nx>api</span><span class=p>.</span><span class=nx>selectMetadata</span><span class=p>({</span><span class=w> </span><span class=nx>jobId</span><span class=p>,</span><span class=w> </span><span class=nx>title</span><span class=p>,</span><span class=w> </span><span class=nx>year</span><span class=p>,</span><span class=w> </span><span class=nx>imdbId</span><span class=p>,</span><span class=w> </span><span class=nx>selectedPlaylist</span><span class=w> </span><span class=p>});</span>
</span><span id=__span-3-5><a id=__codelineno-3-5 name=__codelineno-3-5 href=#__codelineno-3-5></a><span class=k>await</span><span class=w> </span><span class=nx>api</span><span class=p>.</span><span class=nx>confirmEncodeReview</span><span class=p>(</span><span class=nx>jobId</span><span class=p>,</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-3-6><a id=__codelineno-3-6 name=__codelineno-3-6 href=#__codelineno-3-6></a><span class=w> </span><span class=nx>selectedEncodeTitleId</span><span class=o>:</span><span class=w> </span><span class=mf>1</span><span class=p>,</span>
</span><span id=__span-3-7><a id=__codelineno-3-7 name=__codelineno-3-7 href=#__codelineno-3-7></a><span class=w> </span><span class=nx>selectedTrackSelection</span><span class=o>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=mf>1</span><span class=o>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nx>audioTrackIds</span><span class=o>:</span><span class=w> </span><span class=p>[</span><span class=mf>1</span><span class=p>],</span><span class=w> </span><span class=nx>subtitleTrackIds</span><span class=o>:</span><span class=w> </span><span class=p>[</span><span class=mf>3</span><span class=p>]</span><span class=w> </span><span class=p>}</span><span class=w> </span><span class=p>}</span>
</span><span id=__span-3-8><a id=__codelineno-3-8 name=__codelineno-3-8 href=#__codelineno-3-8></a><span class=p>});</span>
</span></code></pre></div> <p><strong>Features:</strong> - Zentralisierte Fehlerbehandlung - Automatische JSON-Serialisierung - Basis-URL aus Umgebungsvariable (<code>VITE_API_BASE</code>)</p> <hr> <h2 id=build-entwicklung>Build &amp; Entwicklung<a class=headerlink href=#build-entwicklung title="Permanent link">&para;</a></h2> <h3 id=entwicklungsserver>Entwicklungsserver<a class=headerlink href=#entwicklungsserver title="Permanent link">&para;</a></h3> <div class="language-bash highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a><span class=nb>cd</span><span class=w> </span>frontend
</span><span id=__span-4-2><a id=__codelineno-4-2 name=__codelineno-4-2 href=#__codelineno-4-2></a>npm<span class=w> </span>run<span class=w> </span>dev
</span><span id=__span-4-3><a id=__codelineno-4-3 name=__codelineno-4-3 href=#__codelineno-4-3></a><span class=c1># → http://localhost:5173</span>
</span></code></pre></div> <h3 id=vite-proxy-konfiguration>Vite-Proxy-Konfiguration<a class=headerlink href=#vite-proxy-konfiguration title="Permanent link">&para;</a></h3> <p>In der Entwicklungsumgebung proxied Vite API-Anfragen zum Backend:</p> <div class="language-js highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a><span class=c1>// vite.config.js</span>
</span><span id=__span-5-2><a id=__codelineno-5-2 name=__codelineno-5-2 href=#__codelineno-5-2></a><span class=nx>proxy</span><span class=o>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-5-3><a id=__codelineno-5-3 name=__codelineno-5-3 href=#__codelineno-5-3></a><span class=w> </span><span class=s1>&#39;/api&#39;</span><span class=o>:</span><span class=w> </span><span class=s1>&#39;http://localhost:3001&#39;</span><span class=p>,</span>
</span><span id=__span-5-4><a id=__codelineno-5-4 name=__codelineno-5-4 href=#__codelineno-5-4></a><span class=w> </span><span class=s1>&#39;/ws&#39;</span><span class=o>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nx>target</span><span class=o>:</span><span class=w> </span><span class=s1>&#39;ws://localhost:3001&#39;</span><span class=p>,</span><span class=w> </span><span class=nx>ws</span><span class=o>:</span><span class=w> </span><span class=kc>true</span><span class=w> </span><span class=p>}</span>
</span><span id=__span-5-5><a id=__codelineno-5-5 name=__codelineno-5-5 href=#__codelineno-5-5></a><span class=p>}</span>
</span></code></pre></div> <h3 id=production-build>Production-Build<a class=headerlink href=#production-build title="Permanent link">&para;</a></h3> <div class="language-bash highlight"><pre><span></span><code><span id=__span-6-1><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a><span class=nb>cd</span><span class=w> </span>frontend
</span><span id=__span-6-2><a id=__codelineno-6-2 name=__codelineno-6-2 href=#__codelineno-6-2></a>npm<span class=w> </span>run<span class=w> </span>build
</span><span id=__span-6-3><a id=__codelineno-6-3 name=__codelineno-6-3 href=#__codelineno-6-3></a><span class=c1># → frontend/dist/</span>
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/architecture/frontend/ rel=canonical><link href=../backend/ rel=prev><link href=../database/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Frontend-Komponenten - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#frontend-komponenten class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Frontend-Komponenten </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.Line truncated
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a>npm<span class=w> </span>run<span class=w> </span>dev<span class=w> </span>--prefix<span class=w> </span>frontend
</span><span id=__span-0-3><a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a>
</span><span id=__span-0-4><a id=__codelineno-0-4 name=__codelineno-0-4 href=#__codelineno-0-4></a><span class=c1># prod build</span>
</span><span id=__span-0-5><a id=__codelineno-0-5 name=__codelineno-0-5 href=#__codelineno-0-5></a>npm<span class=w> </span>run<span class=w> </span>build<span class=w> </span>--prefix<span class=w> </span>frontend
</span></code></pre></div> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
+16 -59
View File
@@ -1,72 +1,29 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/architecture/ rel=canonical><link href=../getting-started/quickstart/ rel=prev><link href=overview/ rel=next><link rel=icon href=../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Architektur - Ripster</title><link rel=stylesheet href=../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../stylesheets/extra.css><script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#architektur class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Architektur </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.Line truncated
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/architecture/ rel=canonical><link href=../getting-started/quickstart/ rel=prev><link href=overview/ rel=next><link rel=icon href=../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Architektur - Ripster</title><link rel=stylesheet href=../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../stylesheets/extra.css><script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#architektur class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Architektur </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.Line truncated
subgraph Browser["Browser (React)"]
Dashboard["Dashboard"]
Settings["Einstellungen"]
History["History"]
Dashboard[Dashboard]
Settings[Einstellungen]
History[Historie]
end
subgraph Backend["Node.js Backend"]
API["REST API\n(Express)"]
WS["WebSocket\nServer"]
Pipeline["Pipeline\nService"]
DB["SQLite\nDatenbank"]
API[REST API\nExpress]
WS[WebSocket\n/ws]
Pipeline[pipelineService]
Cron[cronService]
DB[(SQLite)]
end
subgraph ExternalTools["Externe Tools"]
MakeMKV["makemkvcon"]
HandBrake["HandBrakeCLI"]
MediaInfo["mediainfo"]
subgraph Tools["Externe Tools"]
MakeMKV[makemkvcon]
HandBrake[HandBrakeCLI]
MediaInfo[mediainfo]
end
subgraph ExternalAPIs["Externe APIs"]
OMDb["OMDb API"]
PushOver["PushOver"]
end
Browser &lt;--&gt;|HTTP REST| API
Browser &lt;--&gt;|HTTP| API
Browser &lt;--&gt;|WebSocket| WS
Pipeline --&gt; MakeMKV
Pipeline --&gt; HandBrake
Pipeline --&gt; MediaInfo
Pipeline &lt;--&gt;|Metadaten| OMDb
Pipeline --&gt;|Benachrichtigungen| PushOver
API --&gt; DB
Pipeline --&gt; DB</code></pre> <hr> <h2 id=schichten-architektur>Schichten-Architektur<a class=headerlink href=#schichten-architektur title="Permanent link">&para;</a></h2> <h3 id=backend>Backend<a class=headerlink href=#backend title="Permanent link">&para;</a></h3> <div class="language-text highlight"><pre><span></span><code><span id=__span-0-1><a id=__codelineno-0-1 name=__codelineno-0-1 href=#__codelineno-0-1></a>index.js (Express Server)
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a>├── Routes (API-Endpunkte)
</span><span id=__span-0-3><a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a>│ ├── pipelineRoutes.js
</span><span id=__span-0-4><a id=__codelineno-0-4 name=__codelineno-0-4 href=#__codelineno-0-4></a>│ ├── settingsRoutes.js
</span><span id=__span-0-5><a id=__codelineno-0-5 name=__codelineno-0-5 href=#__codelineno-0-5></a>│ └── historyRoutes.js
</span><span id=__span-0-6><a id=__codelineno-0-6 name=__codelineno-0-6 href=#__codelineno-0-6></a>├── Services (Business Logic)
</span><span id=__span-0-7><a id=__codelineno-0-7 name=__codelineno-0-7 href=#__codelineno-0-7></a>│ ├── pipelineService.js ← Kern-Orchestrierung
</span><span id=__span-0-8><a id=__codelineno-0-8 name=__codelineno-0-8 href=#__codelineno-0-8></a>│ ├── diskDetectionService.js
</span><span id=__span-0-9><a id=__codelineno-0-9 name=__codelineno-0-9 href=#__codelineno-0-9></a>│ ├── processRunner.js
</span><span id=__span-0-10><a id=__codelineno-0-10 name=__codelineno-0-10 href=#__codelineno-0-10></a>│ ├── websocketService.js
</span><span id=__span-0-11><a id=__codelineno-0-11 name=__codelineno-0-11 href=#__codelineno-0-11></a>│ ├── omdbService.js
</span><span id=__span-0-12><a id=__codelineno-0-12 name=__codelineno-0-12 href=#__codelineno-0-12></a>│ ├── settingsService.js
</span><span id=__span-0-13><a id=__codelineno-0-13 name=__codelineno-0-13 href=#__codelineno-0-13></a>│ ├── notificationService.js
</span><span id=__span-0-14><a id=__codelineno-0-14 name=__codelineno-0-14 href=#__codelineno-0-14></a>│ ├── historyService.js
</span><span id=__span-0-15><a id=__codelineno-0-15 name=__codelineno-0-15 href=#__codelineno-0-15></a>│ └── logger.js
</span><span id=__span-0-16><a id=__codelineno-0-16 name=__codelineno-0-16 href=#__codelineno-0-16></a>├── Database
</span><span id=__span-0-17><a id=__codelineno-0-17 name=__codelineno-0-17 href=#__codelineno-0-17></a>│ ├── database.js
</span><span id=__span-0-18><a id=__codelineno-0-18 name=__codelineno-0-18 href=#__codelineno-0-18></a>│ └── defaultSettings.js
</span><span id=__span-0-19><a id=__codelineno-0-19 name=__codelineno-0-19 href=#__codelineno-0-19></a>└── Utils
</span><span id=__span-0-20><a id=__codelineno-0-20 name=__codelineno-0-20 href=#__codelineno-0-20></a> ├── encodePlan.js
</span><span id=__span-0-21><a id=__codelineno-0-21 name=__codelineno-0-21 href=#__codelineno-0-21></a> ├── playlistAnalysis.js
</span><span id=__span-0-22><a id=__codelineno-0-22 name=__codelineno-0-22 href=#__codelineno-0-22></a> ├── progressParsers.js
</span><span id=__span-0-23><a id=__codelineno-0-23 name=__codelineno-0-23 href=#__codelineno-0-23></a> └── files.js
</span></code></pre></div> <h3 id=frontend>Frontend<a class=headerlink href=#frontend title="Permanent link">&para;</a></h3> <div class="language-text highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a>App.jsx (React Router)
</span><span id=__span-1-2><a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a>├── Pages
</span><span id=__span-1-3><a id=__codelineno-1-3 name=__codelineno-1-3 href=#__codelineno-1-3></a>│ ├── DashboardPage.jsx ← Haupt-Interface
</span><span id=__span-1-4><a id=__codelineno-1-4 name=__codelineno-1-4 href=#__codelineno-1-4></a>│ ├── SettingsPage.jsx
</span><span id=__span-1-5><a id=__codelineno-1-5 name=__codelineno-1-5 href=#__codelineno-1-5></a>│ └── DatabasePage.jsx ← Historie/DB-Ansicht
</span><span id=__span-1-6><a id=__codelineno-1-6 name=__codelineno-1-6 href=#__codelineno-1-6></a>├── Components
</span><span id=__span-1-7><a id=__codelineno-1-7 name=__codelineno-1-7 href=#__codelineno-1-7></a>│ ├── PipelineStatusCard.jsx
</span><span id=__span-1-8><a id=__codelineno-1-8 name=__codelineno-1-8 href=#__codelineno-1-8></a>│ ├── MetadataSelectionDialog.jsx
</span><span id=__span-1-9><a id=__codelineno-1-9 name=__codelineno-1-9 href=#__codelineno-1-9></a>│ ├── MediaInfoReviewPanel.jsx
</span><span id=__span-1-10><a id=__codelineno-1-10 name=__codelineno-1-10 href=#__codelineno-1-10></a>│ ├── DynamicSettingsForm.jsx
</span><span id=__span-1-11><a id=__codelineno-1-11 name=__codelineno-1-11 href=#__codelineno-1-11></a>│ └── JobDetailDialog.jsx
</span><span id=__span-1-12><a id=__codelineno-1-12 name=__codelineno-1-12 href=#__codelineno-1-12></a>├── Hooks
</span><span id=__span-1-13><a id=__codelineno-1-13 name=__codelineno-1-13 href=#__codelineno-1-13></a>│ └── useWebSocket.js
</span><span id=__span-1-14><a id=__codelineno-1-14 name=__codelineno-1-14 href=#__codelineno-1-14></a>└── API
</span><span id=__span-1-15><a id=__codelineno-1-15 name=__codelineno-1-15 href=#__codelineno-1-15></a> └── client.js
</span></code></pre></div> <hr> <h2 id=weiterfuhrende-dokumentation>Weiterführende Dokumentation<a class=headerlink href=#weiterfuhrende-dokumentation title="Permanent link">&para;</a></h2> <div class="grid cards"> <ul> <li> <p><a href=overview/ ><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13.22 19.03a.75.75 0 0 1 0-1.06L18.19 13H3.75a.75.75 0 0 1 0-1.5h14.44l-4.97-4.97a.749.749 0 0 1 .326-1.275.75.75 0 0 1 .734.215l6.25 6.25a.75.75 0 0 1 0 1.06l-6.25 6.25a.75.75 0 0 1-1.06 0"/></svg></span> Übersicht</a></p> </li> <li> <p><a href=backend/ ><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13.22 19.03a.75.75 0 0 1 0-1.06L18.19 13H3.75a.75.75 0 0 1 0-1.5h14.44l-4.97-4.97a.749.749 0 0 1 .326-1.275.75.75 0 0 1 .734.215l6.25 6.25a.75.75 0 0 1 0 1.06l-6.25 6.25a.75.75 0 0 1-1.06 0"/></svg></span> Backend-Services</a></p> </li> <li> <p><a href=frontend/ ><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13.22 19.03a.75.75 0 0 1 0-1.06L18.19 13H3.75a.75.75 0 0 1 0-1.5h14.44l-4.97-4.97a.749.749 0 0 1 .326-1.275.75.75 0 0 1 .734.215l6.25 6.25a.75.75 0 0 1 0 1.06l-6.25 6.25a.75.75 0 0 1-1.06 0"/></svg></span> Frontend-Komponenten</a></p> </li> <li> <p><a href=database/ ><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13.22 19.03a.75.75 0 0 1 0-1.06L18.19 13H3.75a.75.75 0 0 1 0-1.5h14.44l-4.97-4.97a.749.749 0 0 1 .326-1.275.75.75 0 0 1 .734.215l6.25 6.25a.75.75 0 0 1 0 1.06l-6.25 6.25a.75.75 0 0 1-1.06 0"/></svg></span> Datenbank</a></p> </li> </ul> </div> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copLine truncated
Pipeline --&gt; DB
Cron --&gt; DB</code></pre> <hr> <h2 id=schichten>Schichten<a class=headerlink href=#schichten title="Permanent link">&para;</a></h2> <h3 id=backend>Backend<a class=headerlink href=#backend title="Permanent link">&para;</a></h3> <ul> <li><code>src/index.js</code> (Bootstrapping, Routes, WS, Services)</li> <li><code>src/routes/*</code> (Pipeline, Settings, History, Crons)</li> <li><code>src/services/*</code> (Business-Logik)</li> <li><code>src/db/database.js</code> (Init/Migration)</li> <li><code>src/utils/*</code> (Parser, Dateifunktionen, Validierung)</li> </ul> <h3 id=frontend>Frontend<a class=headerlink href=#frontend title="Permanent link">&para;</a></h3> <ul> <li><code>App.jsx</code> + <code>pages/*</code> (Dashboard, Settings, History)</li> <li><code>components/*</code> (Status-/Review-/Dialog-Komponenten)</li> <li><code>api/client.js</code> (REST-Client)</li> <li><code>hooks/useWebSocket.js</code> (WS-Reconnect)</li> </ul> <hr> <h2 id=weiterfuhrend>Weiterführend<a class=headerlink href=#weiterfuhrend title="Permanent link">&para;</a></h2> <div class="grid cards"> <ul> <li><a href=overview/ ><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13.22 19.03a.75.75 0 0 1 0-1.06L18.19 13H3.75a.75.75 0 0 1 0-1.5h14.44l-4.97-4.97a.749.749 0 0 1 .326-1.275.75.75 0 0 1 .734.215l6.25 6.25a.75.75 0 0 1 0 1.06l-6.25 6.25a.75.75 0 0 1-1.06 0"/></svg></span> Übersicht</a></li> <li><a href=backend/ ><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13.22 19.03a.75.75 0 0 1 0-1.06L18.19 13H3.75a.75.75 0 0 1 0-1.5h14.44l-4.97-4.97a.749.749 0 0 1 .326-1.275.75.75 0 0 1 .734.215l6.25 6.25a.75.75 0 0 1 0 1.06l-6.25 6.25a.75.75 0 0 1-1.06 0"/></svg></span> Backend-Services</a></li> <li><a href=frontend/ ><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13.22 19.03a.75.75 0 0 1 0-1.06L18.19 13H3.75a.75.75 0 0 1 0-1.5h14.44l-4.97-4.97a.749.749 0 0 1 .326-1.275.75.75 0 0 1 .734.215l6.25 6.25a.75.75 0 0 1 0 1.06l-6.25 6.25a.75.75 0 0 1-1.06 0"/></svg></span> Frontend-Komponenten</a></li> <li><a href=database/ ><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13.22 19.03a.75.75 0 0 1 0-1.06L18.19 13H3.75a.75.75 0 0 1 0-1.5h14.44l-4.97-4.97a.749.749 0 0 1 .326-1.275.75.75 0 0 1 .734.215l6.25 6.25a.75.75 0 0 1 0 1.06l-6.25 6.25a.75.75 0 0 1-1.06 0"/></svg></span> Datenbank</a></li> </ul> </div> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 Line truncated
+11 -21
View File
@@ -1,21 +1,11 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/architecture/overview/ rel=canonical><link href=../ rel=prev><link href=../backend/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Übersicht - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#architektur-ubersicht class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Übersicht </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1Line truncated
</span></code></pre></div> <h3 id=service-layer-muster>Service-Layer-Muster<a class=headerlink href=#service-layer-muster title="Permanent link">&para;</a></h3> <div class="language-text highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a>HTTP-Route → Service → Datenbank
</span></code></pre></div> <p>Routes delegieren die gesamte Business-Logik an Services. Services sind voneinander unabhängig und können einzeln getestet werden.</p> <h3 id=schema-getriebene-einstellungen>Schema-getriebene Einstellungen<a class=headerlink href=#schema-getriebene-einstellungen title="Permanent link">&para;</a></h3> <p>Die Settings-Konfiguration definiert <strong>sowohl</strong> die Validierungsregeln als auch die UI-Struktur in einer einzigen Quelle (<code>settings_schema</code>-Tabelle). Die <code>DynamicSettingsForm</code>-Komponente rendert das Formular dynamisch aus dem Schema.</p> <hr> <h2 id=echtzeit-kommunikation>Echtzeit-Kommunikation<a class=headerlink href=#echtzeit-kommunikation title="Permanent link">&para;</a></h2> <h3 id=websocket-protokoll>WebSocket-Protokoll<a class=headerlink href=#websocket-protokoll title="Permanent link">&para;</a></h3> <p>Der WebSocket-Server läuft unter dem Pfad <code>/ws</code>. Nachrichten werden als JSON übertragen:</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a><span class=p>{</span>
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;PIPELINE_STATE_CHANGED&quot;</span><span class=p>,</span>
</span><span id=__span-2-3><a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a><span class=w> </span><span class=nt>&quot;payload&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-2-4><a id=__codelineno-2-4 name=__codelineno-2-4 href=#__codelineno-2-4></a><span class=w> </span><span class=nt>&quot;state&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;ENCODING&quot;</span><span class=p>,</span>
</span><span id=__span-2-5><a id=__codelineno-2-5 name=__codelineno-2-5 href=#__codelineno-2-5></a><span class=w> </span><span class=nt>&quot;activeJobId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>42</span><span class=p>,</span>
</span><span id=__span-2-6><a id=__codelineno-2-6 name=__codelineno-2-6 href=#__codelineno-2-6></a><span class=w> </span><span class=nt>&quot;progress&quot;</span><span class=p>:</span><span class=w> </span><span class=mf>73.5</span><span class=p>,</span>
</span><span id=__span-2-7><a id=__codelineno-2-7 name=__codelineno-2-7 href=#__codelineno-2-7></a><span class=w> </span><span class=nt>&quot;eta&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;00:12:34&quot;</span>
</span><span id=__span-2-8><a id=__codelineno-2-8 name=__codelineno-2-8 href=#__codelineno-2-8></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-2-9><a id=__codelineno-2-9 name=__codelineno-2-9 href=#__codelineno-2-9></a><span class=p>}</span>
</span></code></pre></div> <p><strong>Nachrichtentypen:</strong></p> <table> <thead> <tr> <th>Typ</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>PIPELINE_STATE_CHANGED</code></td> <td>Pipeline-Zustand hat gewechselt</td> </tr> <tr> <td><code>PIPELINE_PROGRESS</code></td> <td>Fortschritt (% und ETA)</td> </tr> <tr> <td><code>PIPELINE_QUEUE_CHANGED</code></td> <td>Queue-Status geändert</td> </tr> <tr> <td><code>DISC_DETECTED</code></td> <td>Disc wurde erkannt</td> </tr> <tr> <td><code>DISC_REMOVED</code></td> <td>Disc wurde entfernt</td> </tr> <tr> <td><code>PIPELINE_ERROR</code></td> <td>Pipeline-Fehler aufgetreten</td> </tr> <tr> <td><code>DISK_DETECTION_ERROR</code></td> <td>Laufwerkserkennung-Fehler</td> </tr> </tbody> </table> <h3 id=reconnect-logik>Reconnect-Logik<a class=headerlink href=#reconnect-logik title="Permanent link">&para;</a></h3> <p>Der Frontend-Hook <code>useWebSocket.js</code> implementiert automatisches Reconnect mit festem Intervall von 1500ms bei Verbindungsabbrüchen.</p> <hr> <h2 id=prozess-management>Prozess-Management<a class=headerlink href=#prozess-management title="Permanent link">&para;</a></h2> <h3 id=processrunnerjs>processRunner.js<a class=headerlink href=#processrunnerjs title="Permanent link">&para;</a></h3> <p>Externe Tools (MakeMKV, HandBrake, MediaInfo) werden als <strong>Child Processes</strong> gestartet:</p> <div class="language-js highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a><span class=nx>spawn</span><span class=p>(</span><span class=nx>command</span><span class=p>,</span><span class=w> </span><span class=nx>args</span><span class=p>,</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nx>stdio</span><span class=o>:</span><span class=w> </span><span class=p>[</span><span class=s1>&#39;ignore&#39;</span><span class=p>,</span><span class=w> </span><span class=s1>&#39;pipe&#39;</span><span class=p>,</span><span class=w> </span><span class=s1>&#39;pipe&#39;</span><span class=p>]</span><span class=w> </span><span class=p>})</span>
</span></code></pre></div> <ul> <li><strong>stdout/stderr</strong> werden zeilenweise gelesen und in Echtzeit verarbeitet</li> <li><strong>Progress-Parsing</strong> erfolgt über reguläre Ausdrücke in <code>progressParsers.js</code></li> <li><strong>Graceful Shutdown</strong>: SIGINT → Timeout → SIGKILL</li> <li><strong>Prozess-Tracking</strong>: Aktive Prozesse werden registriert für sauberes Beenden</li> </ul> <hr> <h2 id=datenpersistenz>Datenpersistenz<a class=headerlink href=#datenpersistenz title="Permanent link">&para;</a></h2> <h3 id=sqlite-datenbank>SQLite-Datenbank<a class=headerlink href=#sqlite-datenbank title="Permanent link">&para;</a></h3> <p>Ripster verwendet eine <strong>einzige SQLite-Datei</strong> für alle persistenten Daten:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a>backend/data/ripster.db
</span></code></pre></div> <p><strong>Tabellen:</strong></p> <table> <thead> <tr> <th>Tabelle</th> <th>Inhalt</th> </tr> </thead> <tbody> <tr> <td><code>jobs</code></td> <td>Alle Rip-Jobs mit Status, Logs, Metadaten</td> </tr> <tr> <td><code>pipeline_state</code></td> <td>Aktueller Pipeline-Zustand (Singleton)</td> </tr> <tr> <td><code>settings_schema</code></td> <td>Schema aller verfügbaren Einstellungen</td> </tr> <tr> <td><code>settings_values</code></td> <td>Benutzer-konfigurierte Werte</td> </tr> </tbody> </table> <h3 id=migrations-strategie>Migrations-Strategie<a class=headerlink href=#migrations-strategie title="Permanent link">&para;</a></h3> <p>Beim Start prüft <code>database.js</code> automatisch, ob das Schema aktuell ist, und führt fehlende Migrationen aus. Korrupte Datenbankdateien werden in ein Quarantäne-Verzeichnis verschoben und eine neue Datenbank erstellt.</p> <hr> <h2 id=fehlerbehandlung>Fehlerbehandlung<a class=headerlink href=#fehlerbehandlung title="Permanent link">&para;</a></h2> <h3 id=strukturierte-fehler>Strukturierte Fehler<a class=headerlink href=#strukturierte-fehler title="Permanent link">&para;</a></h3> <p>Alle Fehler werden mit Kontext-Metadaten protokolliert:</p> <div class="language-js highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a><span class=nx>logger</span><span class=p>.</span><span class=nx>error</span><span class=p>(</span><span class=s1>&#39;Encoding fehlgeschlagen&#39;</span><span class=p>,</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-5-2><a id=__codelineno-5-2 name=__codelineno-5-2 href=#__codelineno-5-2></a><span class=w> </span><span class=nx>jobId</span><span class=o>:</span><span class=w> </span><span class=nx>job</span><span class=p>.</span><span class=nx>id</span><span class=p>,</span>
</span><span id=__span-5-3><a id=__codelineno-5-3 name=__codelineno-5-3 href=#__codelineno-5-3></a><span class=w> </span><span class=nx>command</span><span class=o>:</span><span class=w> </span><span class=nx>cmd</span><span class=p>,</span>
</span><span id=__span-5-4><a id=__codelineno-5-4 name=__codelineno-5-4 href=#__codelineno-5-4></a><span class=w> </span><span class=nx>exitCode</span><span class=o>:</span><span class=w> </span><span class=nx>code</span><span class=p>,</span>
</span><span id=__span-5-5><a id=__codelineno-5-5 name=__codelineno-5-5 href=#__codelineno-5-5></a><span class=w> </span><span class=nx>stderr</span><span class=o>:</span><span class=w> </span><span class=nx>lastLines</span>
</span><span id=__span-5-6><a id=__codelineno-5-6 name=__codelineno-5-6 href=#__codelineno-5-6></a><span class=p>});</span>
</span></code></pre></div> <h3 id=job-fehler-recovery>Job-Fehler-Recovery<a class=headerlink href=#job-fehler-recovery title="Permanent link">&para;</a></h3> <ul> <li>Fehlgeschlagene Jobs bleiben in der Datenbank (Status <code>ERROR</code>)</li> <li>Vollständige Fehler-Logs werden im Job-Datensatz gespeichert</li> <li><strong>Retry-Funktion</strong> ermöglicht Neustart von einem Fehler-Zustand</li> <li><strong>Re-Encode</strong> erlaubt erneutes Encodieren ohne neu zu rippen</li> </ul> <hr> <h2 id=sicherheit>Sicherheit<a class=headerlink href=#sicherheit title="Permanent link">&para;</a></h2> <h3 id=eingabe-validierung>Eingabe-Validierung<a class=headerlink href=#eingabe-validierung title="Permanent link">&para;</a></h3> <ul> <li>Alle Benutzer-Eingaben werden in <code>validators.js</code> validiert</li> <li>CLI-Argumente werden sicher über <code>commandLine.js</code> konstruiert (kein Shell-Injection-Risiko)</li> <li>Pfade werden sanitisiert bevor sie an externe Prozesse übergeben werden</li> </ul> <h3 id=cors-konfiguration>CORS-Konfiguration<a class=headerlink href=#cors-konfiguration title="Permanent link">&para;</a></h3> <div class="language-text highlight"><pre><span></span><code><span id=__span-6-1><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a>CORS_ORIGIN=http://localhost:5173
</span></code></pre></div> <p>In Produktion sollte dieser Wert auf die tatsächliche Frontend-URL gesetzt werden.</p> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/architecture/overview/ rel=canonical><link href=../ rel=prev><link href=../backend/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Übersicht - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#architektur-ubersicht class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Übersicht </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1Line truncated
</span></code></pre></div> <h3 id=service-layer>Service-Layer<a class=headerlink href=#service-layer title="Permanent link">&para;</a></h3> <div class="language-text highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a>Route -&gt; Service -&gt; DB/Tool-Execution
</span></code></pre></div> <p>Routes enthalten kaum Business-Logik.</p> <h3 id=schema-getriebene-settings>Schema-getriebene Settings<a class=headerlink href=#schema-getriebene-settings title="Permanent link">&para;</a></h3> <p>Settings sind DB-schema-getrieben (<code>settings_schema</code> + <code>settings_values</code>), UI rendert dynamisch aus diesen Daten.</p> <hr> <h2 id=echtzeit-kommunikation>Echtzeit-Kommunikation<a class=headerlink href=#echtzeit-kommunikation title="Permanent link">&para;</a></h2> <p>WebSocket läuft auf <code>/ws</code>.</p> <p>Wichtige Events:</p> <ul> <li><code>PIPELINE_STATE_CHANGED</code>, <code>PIPELINE_PROGRESS</code>, <code>PIPELINE_QUEUE_CHANGED</code></li> <li><code>DISC_DETECTED</code>, <code>DISC_REMOVED</code></li> <li><code>HARDWARE_MONITOR_UPDATE</code></li> <li><code>SETTINGS_UPDATED</code>, <code>SETTINGS_BULK_UPDATED</code></li> <li><code>SETTINGS_SCRIPTS_UPDATED</code>, <code>SETTINGS_SCRIPT_CHAINS_UPDATED</code>, <code>USER_PRESETS_UPDATED</code></li> <li><code>CRON_JOBS_UPDATED</code>, <code>CRON_JOB_UPDATED</code></li> <li><code>PIPELINE_ERROR</code>, <code>DISK_DETECTION_ERROR</code></li> </ul> <hr> <h2 id=prozessausfuhrung>Prozessausführung<a class=headerlink href=#prozessausfuhrung title="Permanent link">&para;</a></h2> <p>Externe Tools werden als Child-Processes gestartet (<code>processRunner</code>):</p> <ul> <li>Streaming von stdout/stderr</li> <li>Progress-Parsing (<code>progressParsers.js</code>)</li> <li>kontrollierter Abbruch (SIGINT/SIGKILL-Fallback)</li> </ul> <hr> <h2 id=persistenz>Persistenz<a class=headerlink href=#persistenz title="Permanent link">&para;</a></h2> <p>SQLite-Datei: <code>backend/data/ripster.db</code></p> <p>Kern-Tabellen:</p> <ul> <li><code>jobs</code>, <code>pipeline_state</code></li> <li><code>settings_schema</code>, <code>settings_values</code></li> <li><code>scripts</code>, <code>script_chains</code>, <code>script_chain_steps</code></li> <li><code>user_presets</code></li> <li><code>cron_jobs</code>, <code>cron_run_logs</code></li> </ul> <p>Beim Start werden Schema und Settings-Migrationen automatisch ausgeführt.</p> <hr> <h2 id=fehlerbehandlung>Fehlerbehandlung<a class=headerlink href=#fehlerbehandlung title="Permanent link">&para;</a></h2> <p>Zentrales Error-Handling liefert:</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a><span class=p>{</span>
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a><span class=w> </span><span class=nt>&quot;error&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-2-3><a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a><span class=w> </span><span class=nt>&quot;message&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;...&quot;</span><span class=p>,</span>
</span><span id=__span-2-4><a id=__codelineno-2-4 name=__codelineno-2-4 href=#__codelineno-2-4></a><span class=w> </span><span class=nt>&quot;statusCode&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>400</span><span class=p>,</span>
</span><span id=__span-2-5><a id=__codelineno-2-5 name=__codelineno-2-5 href=#__codelineno-2-5></a><span class=w> </span><span class=nt>&quot;reqId&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;...&quot;</span><span class=p>,</span>
</span><span id=__span-2-6><a id=__codelineno-2-6 name=__codelineno-2-6 href=#__codelineno-2-6></a><span class=w> </span><span class=nt>&quot;details&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[]</span>
</span><span id=__span-2-7><a id=__codelineno-2-7 name=__codelineno-2-7 href=#__codelineno-2-7></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-2-8><a id=__codelineno-2-8 name=__codelineno-2-8 href=#__codelineno-2-8></a><span class=p>}</span>
</span></code></pre></div> <p>Fehlgeschlagene Jobs bleiben in der Historie (<code>ERROR</code> oder <code>CANCELLED</code>) und können erneut gestartet werden.</p> <hr> <h2 id=cors-runtime-konfig>CORS &amp; Runtime-Konfig<a class=headerlink href=#cors-runtime-konfig title="Permanent link">&para;</a></h2> <ul> <li><code>CORS_ORIGIN</code> default: <code>*</code></li> <li><code>LOG_LEVEL</code> default: <code>info</code></li> <li>DB-/Log-Pfade über <code>DB_PATH</code>/<code>LOG_DIR</code> konfigurierbar</li> </ul> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
+14 -17
View File
@@ -1,19 +1,16 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/configuration/environment/ rel=canonical><link href=../settings-reference/ rel=prev><link href=../../deployment/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Umgebungsvariablen - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#umgebungsvariablen class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Umgebungsvariablen </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24Line truncated
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/configuration/environment/ rel=canonical><link href=../settings-reference/ rel=prev><link href=../../deployment/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Umgebungsvariablen - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#umgebungsvariablen class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Umgebungsvariablen </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24Line truncated
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a>DB_PATH=/var/lib/ripster/ripster.db
</span><span id=__span-0-3><a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a>CORS_ORIGIN=http://192.168.1.100:5173
</span><span id=__span-0-4><a id=__codelineno-0-4 name=__codelineno-0-4 href=#__codelineno-0-4></a>LOG_DIR=/var/log/ripster
</span><span id=__span-0-3><a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a>LOG_DIR=/var/log/ripster
</span><span id=__span-0-4><a id=__codelineno-0-4 name=__codelineno-0-4 href=#__codelineno-0-4></a>CORS_ORIGIN=http://192.168.1.50:5173
</span><span id=__span-0-5><a id=__codelineno-0-5 name=__codelineno-0-5 href=#__codelineno-0-5></a>LOG_LEVEL=info
</span></code></pre></div> <hr> <h2 id=frontend-umgebungsvariablen>Frontend-Umgebungsvariablen<a class=headerlink href=#frontend-umgebungsvariablen title="Permanent link">&para;</a></h2> <p>Konfigurationsdatei: <code>frontend/.env</code></p> <table> <thead> <tr> <th>Variable</th> <th>Standard</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>VITE_API_BASE</code></td> <td><code>http://localhost:3001</code></td> <td>Backend-API-URL</td> </tr> <tr> <td><code>VITE_WS_URL</code></td> <td><code>ws://localhost:3001</code></td> <td>WebSocket-URL</td> </tr> <tr> <td><code>VITE_PUBLIC_ORIGIN</code></td> <td>—</td> <td>Öffentliche Origin-URL (für CORS)</td> </tr> <tr> <td><code>VITE_HMR_HOST</code></td> <td>—</td> <td>Vite HMR-Host (für Remote-Entwicklung)</td> </tr> <tr> <td><code>VITE_HMR_PORT</code></td> <td>—</td> <td>Vite HMR-Port</td> </tr> </tbody> </table> <h3 id=beispiel-frontendenv-entwicklung>Beispiel: frontend/.env (Entwicklung)<a class=headerlink href=#beispiel-frontendenv-entwicklung title="Permanent link">&para;</a></h3> <div class="language-text highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a>VITE_API_BASE=http://localhost:3001
</span><span id=__span-1-2><a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a>VITE_WS_URL=ws://localhost:3001
</span></code></pre></div> <h3 id=beispiel-frontendenv-netzwerk-zugriff>Beispiel: frontend/.env (Netzwerk-Zugriff)<a class=headerlink href=#beispiel-frontendenv-netzwerk-zugriff title="Permanent link">&para;</a></h3> <div class="language-text highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a>VITE_API_BASE=http://192.168.1.100:3001
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a>VITE_WS_URL=ws://192.168.1.100:3001
</span><span id=__span-2-3><a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a>VITE_PUBLIC_ORIGIN=http://192.168.1.100:5173
</span></code></pre></div> <hr> <h2 id=envexample-dateien>.env.example Dateien<a class=headerlink href=#envexample-dateien title="Permanent link">&para;</a></h2> <p>Das Repository enthält Vorlagen für beide Konfigurationsdateien:</p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a><span class=c1># Backend</span>
</span><span id=__span-3-2><a id=__codelineno-3-2 name=__codelineno-3-2 href=#__codelineno-3-2></a>cp<span class=w> </span>backend/.env.example<span class=w> </span>backend/.env
</span><span id=__span-3-3><a id=__codelineno-3-3 name=__codelineno-3-3 href=#__codelineno-3-3></a>
</span><span id=__span-3-4><a id=__codelineno-3-4 name=__codelineno-3-4 href=#__codelineno-3-4></a><span class=c1># Frontend</span>
</span><span id=__span-3-5><a id=__codelineno-3-5 name=__codelineno-3-5 href=#__codelineno-3-5></a>cp<span class=w> </span>frontend/.env.example<span class=w> </span>frontend/.env
</span></code></pre></div> <hr> <h2 id=prioritat-der-konfiguration>Priorität der Konfiguration<a class=headerlink href=#prioritat-der-konfiguration title="Permanent link">&para;</a></h2> <p>Einstellungen werden in folgender Reihenfolge geladen (höhere Priorität überschreibt niedrigere):</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a>1. Systemumgebungsvariablen (export VAR=value)
</span><span id=__span-4-2><a id=__codelineno-4-2 name=__codelineno-4-2 href=#__codelineno-4-2></a>2. .env-Datei
</span><span id=__span-4-3><a id=__codelineno-4-3 name=__codelineno-4-3 href=#__codelineno-4-3></a>3. Hardcodierte Standardwerte in config.js
</span></code></pre></div> <hr> <h2 id=log_level>LOG_LEVEL<a class=headerlink href=#log_level title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>Level</th> <th>Ausgabe</th> </tr> </thead> <tbody> <tr> <td><code>debug</code></td> <td>Alle Meldungen inkl. Debugging</td> </tr> <tr> <td><code>info</code></td> <td>Normale Betriebsinformationen</td> </tr> <tr> <td><code>warn</code></td> <td>Warnungen + Fehler</td> </tr> <tr> <td><code>error</code></td> <td>Nur Fehler</td> </tr> </tbody> </table> <div class="admonition tip"> <p class=admonition-title>Produktionsempfehlung</p> <p>Für Produktionsumgebungen <code>LOG_LEVEL=info</code> oder <code>LOG_LEVEL=warn</code> verwenden. <code>debug</code> erzeugt sehr viele Log-Einträge.</p> </div> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
</span></code></pre></div> <p>Hinweis: <code>backend/.env.example</code> enthält bewusst dev-freundliche Werte (z. B. lokaler <code>CORS_ORIGIN</code>).</p> <hr> <h2 id=frontend-frontendenv>Frontend (<code>frontend/.env</code>)<a class=headerlink href=#frontend-frontendenv title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>Variable</th> <th>Default</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>VITE_API_BASE</code></td> <td><code>/api</code></td> <td>API-Basis für Fetch-Client</td> </tr> <tr> <td><code>VITE_WS_URL</code></td> <td>automatisch aus <code>window.location</code> + <code>/ws</code></td> <td>Optional explizite WebSocket-URL</td> </tr> <tr> <td><code>VITE_PUBLIC_ORIGIN</code></td> <td>leer</td> <td>Öffentliche Vite-Origin (Remote-Dev)</td> </tr> <tr> <td><code>VITE_ALLOWED_HOSTS</code></td> <td><code>true</code></td> <td>Komma-separierte Hostliste für Vite <code>allowedHosts</code></td> </tr> <tr> <td><code>VITE_HMR_PROTOCOL</code></td> <td>abgeleitet aus <code>VITE_PUBLIC_ORIGIN</code></td> <td>HMR-Protokoll (<code>ws</code>/<code>wss</code>)</td> </tr> <tr> <td><code>VITE_HMR_HOST</code></td> <td>abgeleitet aus <code>VITE_PUBLIC_ORIGIN</code></td> <td>HMR-Host</td> </tr> <tr> <td><code>VITE_HMR_CLIENT_PORT</code></td> <td>abgeleitet aus <code>VITE_PUBLIC_ORIGIN</code></td> <td>HMR-Client-Port</td> </tr> </tbody> </table> <p>Beispiele:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a># lokal (mit Vite-Proxy)
</span><span id=__span-1-2><a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a>VITE_API_BASE=/api
</span></code></pre></div> <div class="language-text highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a># remote dev
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a>VITE_API_BASE=http://192.168.1.50:3001/api
</span><span id=__span-2-3><a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a>VITE_WS_URL=ws://192.168.1.50:3001/ws
</span><span id=__span-2-4><a id=__codelineno-2-4 name=__codelineno-2-4 href=#__codelineno-2-4></a>VITE_PUBLIC_ORIGIN=http://192.168.1.50:5173
</span><span id=__span-2-5><a id=__codelineno-2-5 name=__codelineno-2-5 href=#__codelineno-2-5></a>VITE_ALLOWED_HOSTS=192.168.1.50,ripster.local
</span><span id=__span-2-6><a id=__codelineno-2-6 name=__codelineno-2-6 href=#__codelineno-2-6></a>VITE_HMR_PROTOCOL=ws
</span><span id=__span-2-7><a id=__codelineno-2-7 name=__codelineno-2-7 href=#__codelineno-2-7></a>VITE_HMR_HOST=192.168.1.50
</span><span id=__span-2-8><a id=__codelineno-2-8 name=__codelineno-2-8 href=#__codelineno-2-8></a>VITE_HMR_CLIENT_PORT=5173
</span></code></pre></div> <hr> <h2 id=prioritat>Priorität<a class=headerlink href=#prioritat title="Permanent link">&para;</a></h2> <ol> <li>Prozess-Umgebungsvariablen</li> <li><code>.env</code></li> <li>Code-Defaults</li> </ol> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
+1 -1
View File
@@ -1 +1 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/configuration/ rel=canonical><link href=../api/websocket/ rel=prev><link href=settings-reference/ rel=next><link rel=icon href=../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Konfiguration - Ripster</title><link rel=stylesheet href=../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../stylesheets/extra.css><script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#konfiguration class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Konfiguration </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.5Line truncated
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/configuration/ rel=canonical><link href=../api/websocket/ rel=prev><link href=settings-reference/ rel=next><link rel=icon href=../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Konfiguration - Ripster</title><link rel=stylesheet href=../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../stylesheets/extra.css><script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#konfiguration class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Konfiguration </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.5Line truncated
@@ -1,10 +1 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/configuration/settings-reference/ rel=canonical><link href=../ rel=prev><link href=../environment/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Alle Einstellungen - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#einstellungsreferenz class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Alle Einstellungen </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="MLine truncated
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a>movie_dir = /mnt/nas/movies
</span><span id=__span-0-3><a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a>log_dir = /var/log/ripster
</span></code></pre></div> </div> <hr> <h2 id=kategorie-tools-tools>Kategorie: Tools (tools)<a class=headerlink href=#kategorie-tools-tools title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>Schlüssel</th> <th>Typ</th> <th>Standard</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>makemkv_command</code></td> <td>string</td> <td><code>makemkvcon</code></td> <td>Befehl oder absoluter Pfad zu MakeMKV</td> </tr> <tr> <td><code>handbrake_command</code></td> <td>string</td> <td><code>HandBrakeCLI</code></td> <td>Befehl oder absoluter Pfad zu HandBrake</td> </tr> <tr> <td><code>mediainfo_command</code></td> <td>string</td> <td><code>mediainfo</code></td> <td>Befehl oder absoluter Pfad zu MediaInfo</td> </tr> </tbody> </table> <div class="admonition tip"> <p class=admonition-title>Absolute Pfade verwenden</p> <p>Falls die Tools nicht im <code>PATH</code> des Systems sind: <div class="language-text highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a>makemkv_command = /usr/local/bin/makemkvcon
</span><span id=__span-1-2><a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a>handbrake_command = /usr/local/bin/HandBrakeCLI
</span><span id=__span-1-3><a id=__codelineno-1-3 name=__codelineno-1-3 href=#__codelineno-1-3></a>mediainfo_command = /usr/bin/mediainfo
</span></code></pre></div></p> </div> <hr> <h2 id=kategorie-encoding-encoding>Kategorie: Encoding (encoding)<a class=headerlink href=#kategorie-encoding-encoding title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>Schlüssel</th> <th>Typ</th> <th>Standard</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>handbrake_preset</code></td> <td>string</td> <td><code>H.265 MKV 1080p30</code></td> <td>Name des HandBrake-Presets</td> </tr> <tr> <td><code>handbrake_extra_args</code></td> <td>string</td> <td><em>(leer)</em></td> <td>Zusätzliche HandBrake CLI-Argumente</td> </tr> <tr> <td><code>output_extension</code></td> <td>string</td> <td><code>mkv</code></td> <td>Dateiendung der Ausgabedatei</td> </tr> <tr> <td><code>filename_template</code></td> <td>string</td> <td><code>{title} ({year})</code></td> <td>Template für den Dateinamen</td> </tr> </tbody> </table> <h3 id=verfugbare-handbrake-presets>Verfügbare HandBrake-Presets<a class=headerlink href=#verfugbare-handbrake-presets title="Permanent link">&para;</a></h3> <p>Eine vollständige Liste der verfügbaren Presets:</p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a>HandBrakeCLI<span class=w> </span>--preset-list
</span></code></pre></div> <p>Häufig verwendete Presets:</p> <table> <thead> <tr> <th>Preset</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>H.265 MKV 1080p30</code></td> <td>H.265/HEVC, Full-HD, 30fps</td> </tr> <tr> <td><code>H.265 MKV 720p30</code></td> <td>H.265/HEVC, HD, 30fps</td> </tr> <tr> <td><code>H.264 MKV 1080p30</code></td> <td>H.264/AVC, Full-HD, 30fps</td> </tr> <tr> <td><code>HQ 1080p30 Surround</code></td> <td>Hohe Qualität, Full-HD mit Surround</td> </tr> </tbody> </table> <h3 id=dateiname-template-platzhalter>Dateiname-Template-Platzhalter<a class=headerlink href=#dateiname-template-platzhalter title="Permanent link">&para;</a></h3> <table> <thead> <tr> <th>Platzhalter</th> <th>Beispiel</th> </tr> </thead> <tbody> <tr> <td><code>{title}</code></td> <td><code>Inception</code></td> </tr> <tr> <td><code>{year}</code></td> <td><code>2010</code></td> </tr> <tr> <td><code>{imdb_id}</code></td> <td><code>tt1375666</code></td> </tr> <tr> <td><code>{type}</code></td> <td><code>movie</code></td> </tr> </tbody> </table> <hr> <h2 id=kategorie-laufwerk-drive>Kategorie: Laufwerk (drive)<a class=headerlink href=#kategorie-laufwerk-drive title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>Schlüssel</th> <th>Typ</th> <th>Standard</th> <th>Optionen</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>drive_mode</code></td> <td>select</td> <td><code>auto</code></td> <td><code>auto</code>, <code>explicit</code></td> <td>Laufwerk-Erkennungsmodus</td> </tr> <tr> <td><code>drive_device</code></td> <td>string</td> <td><code>/dev/sr0</code></td> <td>—</td> <td>Geräte-Pfad (nur bei <code>explicit</code>)</td> </tr> <tr> <td><code>disc_poll_interval_ms</code></td> <td>number</td> <td><code>4000</code></td> <td>1000–60000</td> <td>Polling-Intervall in Millisekunden</td> </tr> </tbody> </table> <p><strong><code>drive_mode</code> Optionen:</strong></p> <table> <thead> <tr> <th>Modus</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>auto</code></td> <td>Ripster erkennt das Laufwerk automatisch</td> </tr> <tr> <td><code>explicit</code></td> <td>Verwendet das in <code>drive_device</code> konfigurierte Gerät</td> </tr> </tbody> </table> <hr> <h2 id=kategorie-makemkv-makemkv>Kategorie: MakeMKV (makemkv)<a class=headerlink href=#kategorie-makemkv-makemkv title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>Schlüssel</th> <th>Typ</th> <th>Standard</th> <th>Min</th> <th>Max</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>makemkv_min_length_minutes</code></td> <td>number</td> <td><code>15</code></td> <td><code>0</code></td> <td><code>999</code></td> <td>Mindest-Titellänge in Minuten</td> </tr> <tr> <td><code>makemkv_backup_mode</code></td> <td>boolean</td> <td><code>false</code></td> <td>—</td> <td>—</td> <td>Backup-Modus statt MKV-Modus</td> </tr> </tbody> </table> <p><strong><code>makemkv_min_length_minutes</code>:</strong> Titel kürzer als dieser Wert werden von MakeMKV ignoriert. Verhindert das Rippen von Menü-Schleifen und kurzen Extra-Clips.</p> <p><strong><code>makemkv_backup_mode</code>:</strong> Im Backup-Modus erstellt MakeMKV eine vollständige Disc-Kopie mit Menüs. Im Standard-Modus werden direkt MKV-Dateien erstellt.</p> <hr> <h2 id=kategorie-omdb-omdb>Kategorie: OMDb (omdb)<a class=headerlink href=#kategorie-omdb-omdb title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>Schlüssel</th> <th>Typ</th> <th>Standard</th> <th>Pflicht</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>omdb_api_key</code></td> <td>string</td> <td>—</td> <td>✅</td> <td>API-Key von <a href=https://www.omdbapi.com/ >omdbapi.com</a></td> </tr> <tr> <td><code>omdb_default_type</code></td> <td>select</td> <td><code>movie</code></td> <td>—</td> <td>Standard-Suchtyp: <code>movie</code> oder <code>series</code></td> </tr> </tbody> </table> <hr> <h2 id=kategorie-benachrichtigungen-notifications>Kategorie: Benachrichtigungen (notifications)<a class=headerlink href=#kategorie-benachrichtigungen-notifications title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>Schlüssel</th> <th>Typ</th> <th>Standard</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>pushover_user_key</code></td> <td>string</td> <td>—</td> <td>PushOver User-Key</td> </tr> <tr> <td><code>pushover_api_token</code></td> <td>string</td> <td>—</td> <td>PushOver API-Token</td> </tr> </tbody> </table> <p>Beide Felder müssen konfiguriert sein, um PushOver-Benachrichtigungen zu aktivieren. Die Verbindung kann mit dem <strong>Test-Button</strong> in den Einstellungen geprüft werden.</p> <hr> <h2 id=standard-einstellungen-zurucksetzen>Standard-Einstellungen zurücksetzen<a class=headerlink href=#standard-einstellungen-zurucksetzen title="Permanent link">&para;</a></h2> <p>Über die Datenbank können Einstellungen auf Standardwerte zurückgesetzt werden:</p> <div class="language-bash highlight"><pre><span></span><code><span id=__spLine truncated
</span><span id=__span-3-2><a id=__codelineno-3-2 name=__codelineno-3-2 href=#__codelineno-3-2></a><span class=w> </span><span class=s2>&quot;DELETE FROM settings_values WHERE key = &#39;handbrake_preset&#39;;&quot;</span>
</span></code></pre></div> <p>Beim nächsten Laden der Einstellungen wird der Standardwert verwendet.</p> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/configuration/settings-reference/ rel=canonical><link href=../ rel=prev><link href=../environment/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Alle Einstellungen - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#einstellungsreferenz class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Alle Einstellungen </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="MLine truncated
+20 -35
View File
@@ -1,39 +1,24 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/deployment/development/ rel=canonical><link href=../ rel=prev><link href=../production/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Entwicklungsumgebung - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#entwicklungsumgebung class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Entwicklungsumgebung </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6Line truncated
</span></code></pre></div> <p>Das Skript startet automatisch: - <strong>Backend</strong> auf Port 3001 (mit Nodemon für Hot-Reload) - <strong>Frontend</strong> auf Port 5173 (mit Vite HMR)</p> <hr> <h2 id=manuelle-entwicklungsumgebung>Manuelle Entwicklungsumgebung<a class=headerlink href=#manuelle-entwicklungsumgebung title="Permanent link">&para;</a></h2> <h3 id=terminal-1-backend>Terminal 1 – Backend<a class=headerlink href=#terminal-1-backend title="Permanent link">&para;</a></h3> <div class="language-bash highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a><span class=nb>cd</span><span class=w> </span>backend
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/deployment/development/ rel=canonical><link href=../ rel=prev><link href=../production/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Entwicklungsumgebung - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#entwicklungsumgebung class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Entwicklungsumgebung </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6Line truncated
</span></code></pre></div> <p>Startet:</p> <ul> <li>Backend (<code>http://localhost:3001</code>, mit nodemon)</li> <li>Frontend (<code>http://localhost:5173</code>, mit Vite HMR)</li> </ul> <p>Stoppen: <code>Ctrl+C</code>.</p> <hr> <h2 id=manuell>Manuell<a class=headerlink href=#manuell title="Permanent link">&para;</a></h2> <h3 id=backend>Backend<a class=headerlink href=#backend title="Permanent link">&para;</a></h3> <div class="language-bash highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a><span class=nb>cd</span><span class=w> </span>backend
</span><span id=__span-1-2><a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a>npm<span class=w> </span>install
</span><span id=__span-1-3><a id=__codelineno-1-3 name=__codelineno-1-3 href=#__codelineno-1-3></a>npm<span class=w> </span>run<span class=w> </span>dev
</span></code></pre></div> <p>Backend läuft auf <code>http://localhost:3001</code> mit <strong>Nodemon</strong> – Neustart bei Dateiänderungen.</p> <h3 id=terminal-2-frontend>Terminal 2 – Frontend<a class=headerlink href=#terminal-2-frontend title="Permanent link">&para;</a></h3> <div class="language-bash highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a><span class=nb>cd</span><span class=w> </span>frontend
</span></code></pre></div> <h3 id=frontend>Frontend<a class=headerlink href=#frontend title="Permanent link">&para;</a></h3> <div class="language-bash highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a><span class=nb>cd</span><span class=w> </span>frontend
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a>npm<span class=w> </span>install
</span><span id=__span-2-3><a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a>npm<span class=w> </span>run<span class=w> </span>dev
</span></code></pre></div> <p>Frontend läuft auf <code>http://localhost:5173</code> mit <strong>Vite HMR</strong> – sofortige Browser-Updates.</p> <hr> <h2 id=vite-proxy>Vite-Proxy<a class=headerlink href=#vite-proxy title="Permanent link">&para;</a></h2> <p>Im Entwicklungsmodus proxied Vite alle API- und WebSocket-Anfragen zum Backend:</p> <div class="language-js highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a><span class=c1>// frontend/vite.config.js</span>
</span><span id=__span-3-2><a id=__codelineno-3-2 name=__codelineno-3-2 href=#__codelineno-3-2></a><span class=nx>server</span><span class=o>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-3-3><a id=__codelineno-3-3 name=__codelineno-3-3 href=#__codelineno-3-3></a><span class=w> </span><span class=nx>proxy</span><span class=o>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-3-4><a id=__codelineno-3-4 name=__codelineno-3-4 href=#__codelineno-3-4></a><span class=w> </span><span class=s1>&#39;/api&#39;</span><span class=o>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-3-5><a id=__codelineno-3-5 name=__codelineno-3-5 href=#__codelineno-3-5></a><span class=w> </span><span class=nx>target</span><span class=o>:</span><span class=w> </span><span class=s1>&#39;http://localhost:3001&#39;</span><span class=p>,</span>
</span><span id=__span-3-6><a id=__codelineno-3-6 name=__codelineno-3-6 href=#__codelineno-3-6></a><span class=w> </span><span class=nx>changeOrigin</span><span class=o>:</span><span class=w> </span><span class=kc>true</span>
</span><span id=__span-3-7><a id=__codelineno-3-7 name=__codelineno-3-7 href=#__codelineno-3-7></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-3-8><a id=__codelineno-3-8 name=__codelineno-3-8 href=#__codelineno-3-8></a><span class=w> </span><span class=s1>&#39;/ws&#39;</span><span class=o>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-3-9><a id=__codelineno-3-9 name=__codelineno-3-9 href=#__codelineno-3-9></a><span class=w> </span><span class=nx>target</span><span class=o>:</span><span class=w> </span><span class=s1>&#39;ws://localhost:3001&#39;</span><span class=p>,</span>
</span><span id=__span-3-10><a id=__codelineno-3-10 name=__codelineno-3-10 href=#__codelineno-3-10></a><span class=w> </span><span class=nx>ws</span><span class=o>:</span><span class=w> </span><span class=kc>true</span>
</span><span id=__span-3-11><a id=__codelineno-3-11 name=__codelineno-3-11 href=#__codelineno-3-11></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-3-12><a id=__codelineno-3-12 name=__codelineno-3-12 href=#__codelineno-3-12></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-3-13><a id=__codelineno-3-13 name=__codelineno-3-13 href=#__codelineno-3-13></a><span class=p>}</span>
</span></code></pre></div> <p>Das bedeutet: Im Browser macht das Frontend Anfragen an <code>localhost:5173/api/...</code> – Vite leitet diese an <code>localhost:3001/api/...</code> weiter.</p> <hr> <h2 id=remote-entwicklung>Remote-Entwicklung<a class=headerlink href=#remote-entwicklung title="Permanent link">&para;</a></h2> <p>Falls Ripster auf einem entfernten Server entwickelt wird (z.B. Homeserver), muss die Vite-Konfiguration angepasst werden:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a># frontend/.env.local
</span><span id=__span-4-2><a id=__codelineno-4-2 name=__codelineno-4-2 href=#__codelineno-4-2></a>VITE_API_BASE=http://192.168.1.100:3001
</span><span id=__span-4-3><a id=__codelineno-4-3 name=__codelineno-4-3 href=#__codelineno-4-3></a>VITE_WS_URL=ws://192.168.1.100:3001
</span><span id=__span-4-4><a id=__codelineno-4-4 name=__codelineno-4-4 href=#__codelineno-4-4></a>VITE_HMR_HOST=192.168.1.100
</span><span id=__span-4-5><a id=__codelineno-4-5 name=__codelineno-4-5 href=#__codelineno-4-5></a>VITE_HMR_PORT=5173
</span></code></pre></div> <hr> <h2 id=log-level-fur-entwicklung>Log-Level für Entwicklung<a class=headerlink href=#log-level-fur-entwicklung title="Permanent link">&para;</a></h2> <div class="language-text highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a># backend/.env
</span><span id=__span-5-2><a id=__codelineno-5-2 name=__codelineno-5-2 href=#__codelineno-5-2></a>LOG_LEVEL=debug
</span></code></pre></div> <p>Im Debug-Modus werden alle Ausgaben der externen Tools (MakeMKV, HandBrake) vollständig geloggt.</p> <hr> <h2 id=stoppen>Stoppen<a class=headerlink href=#stoppen title="Permanent link">&para;</a></h2> <div class="language-bash highlight"><pre><span></span><code><span id=__span-6-1><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a>./kill.sh
</span></code></pre></div> <hr> <h2 id=linting-type-checking>Linting &amp; Type-Checking<a class=headerlink href=#linting-type-checking title="Permanent link">&para;</a></h2> <div class="language-bash highlight"><pre><span></span><code><span id=__span-7-1><a id=__codelineno-7-1 name=__codelineno-7-1 href=#__codelineno-7-1></a><span class=c1># Frontend (ESLint)</span>
</span><span id=__span-7-2><a id=__codelineno-7-2 name=__codelineno-7-2 href=#__codelineno-7-2></a><span class=nb>cd</span><span class=w> </span>frontend<span class=w> </span><span class=o>&amp;&amp;</span><span class=w> </span>npm<span class=w> </span>run<span class=w> </span>lint
</span><span id=__span-7-3><a id=__codelineno-7-3 name=__codelineno-7-3 href=#__codelineno-7-3></a>
</span><span id=__span-7-4><a id=__codelineno-7-4 name=__codelineno-7-4 href=#__codelineno-7-4></a><span class=c1># Backend hat keine separaten Lint-Scripts,</span>
</span><span id=__span-7-5><a id=__codelineno-7-5 name=__codelineno-7-5 href=#__codelineno-7-5></a><span class=c1># nutze direkt eslint falls konfiguriert</span>
</span></code></pre></div> <hr> <h2 id=deployment-script>Deployment-Script<a class=headerlink href=#deployment-script title="Permanent link">&para;</a></h2> <p>Das <code>deploy-ripster.sh</code>-Script überträgt Code auf einen Remote-Server per SSH:</p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-8-1><a id=__codelineno-8-1 name=__codelineno-8-1 href=#__codelineno-8-1></a>./deploy-ripster.sh
</span></code></pre></div> <p><strong>Was das Script tut:</strong> 1. <code>rsync</code> synchronisiert den Code (Backend-Quellcode ohne <code>data/</code>) 2. Die Datenbank (<code>backend/data/</code>) wird <strong>nicht</strong> überschrieben 3. Verbindung via SSH (konfigurierbar im Script)</p> <p><strong>Anpassung des Scripts:</strong></p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-9-1><a id=__codelineno-9-1 name=__codelineno-9-1 href=#__codelineno-9-1></a><span class=c1># deploy-ripster.sh</span>
</span><span id=__span-9-2><a id=__codelineno-9-2 name=__codelineno-9-2 href=#__codelineno-9-2></a><span class=nv>REMOTE_HOST</span><span class=o>=</span><span class=s2>&quot;192.168.1.100&quot;</span>
</span><span id=__span-9-3><a id=__codelineno-9-3 name=__codelineno-9-3 href=#__codelineno-9-3></a><span class=nv>REMOTE_USER</span><span class=o>=</span><span class=s2>&quot;michael&quot;</span>
</span><span id=__span-9-4><a id=__codelineno-9-4 name=__codelineno-9-4 href=#__codelineno-9-4></a><span class=nv>REMOTE_PATH</span><span class=o>=</span><span class=s2>&quot;/home/michael/ripster&quot;</span>
</span></code></pre></div> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
</span></code></pre></div> <hr> <h2 id=vite-proxy-dev>Vite-Proxy (Dev)<a class=headerlink href=#vite-proxy-dev title="Permanent link">&para;</a></h2> <p><code>frontend/vite.config.js</code> proxied standardmäßig:</p> <ul> <li><code>/api</code> -&gt; <code>http://127.0.0.1:3001</code></li> <li><code>/ws</code> -&gt; <code>ws://127.0.0.1:3001</code></li> </ul> <hr> <h2 id=remote-dev-optional>Remote-Dev (optional)<a class=headerlink href=#remote-dev-optional title="Permanent link">&para;</a></h2> <p>Beispiel <code>frontend/.env.local</code>:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a>VITE_API_BASE=http://192.168.1.100:3001/api
</span><span id=__span-3-2><a id=__codelineno-3-2 name=__codelineno-3-2 href=#__codelineno-3-2></a>VITE_WS_URL=ws://192.168.1.100:3001/ws
</span><span id=__span-3-3><a id=__codelineno-3-3 name=__codelineno-3-3 href=#__codelineno-3-3></a>VITE_PUBLIC_ORIGIN=http://192.168.1.100:5173
</span><span id=__span-3-4><a id=__codelineno-3-4 name=__codelineno-3-4 href=#__codelineno-3-4></a>VITE_ALLOWED_HOSTS=192.168.1.100,ripster.local
</span><span id=__span-3-5><a id=__codelineno-3-5 name=__codelineno-3-5 href=#__codelineno-3-5></a>VITE_HMR_PROTOCOL=ws
</span><span id=__span-3-6><a id=__codelineno-3-6 name=__codelineno-3-6 href=#__codelineno-3-6></a>VITE_HMR_HOST=192.168.1.100
</span><span id=__span-3-7><a id=__codelineno-3-7 name=__codelineno-3-7 href=#__codelineno-3-7></a>VITE_HMR_CLIENT_PORT=5173
</span></code></pre></div> <hr> <h2 id=nutzliche-kommandos>Nützliche Kommandos<a class=headerlink href=#nutzliche-kommandos title="Permanent link">&para;</a></h2> <div class="language-bash highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a><span class=c1># Root dev (backend + frontend)</span>
</span><span id=__span-4-2><a id=__codelineno-4-2 name=__codelineno-4-2 href=#__codelineno-4-2></a>npm<span class=w> </span>run<span class=w> </span>dev
</span><span id=__span-4-3><a id=__codelineno-4-3 name=__codelineno-4-3 href=#__codelineno-4-3></a>
</span><span id=__span-4-4><a id=__codelineno-4-4 name=__codelineno-4-4 href=#__codelineno-4-4></a><span class=c1># einzeln</span>
</span><span id=__span-4-5><a id=__codelineno-4-5 name=__codelineno-4-5 href=#__codelineno-4-5></a>npm<span class=w> </span>run<span class=w> </span>dev:backend
</span><span id=__span-4-6><a id=__codelineno-4-6 name=__codelineno-4-6 href=#__codelineno-4-6></a>npm<span class=w> </span>run<span class=w> </span>dev:frontend
</span><span id=__span-4-7><a id=__codelineno-4-7 name=__codelineno-4-7 href=#__codelineno-4-7></a>
</span><span id=__span-4-8><a id=__codelineno-4-8 name=__codelineno-4-8 href=#__codelineno-4-8></a><span class=c1># Frontend Build</span>
</span><span id=__span-4-9><a id=__codelineno-4-9 name=__codelineno-4-9 href=#__codelineno-4-9></a>npm<span class=w> </span>run<span class=w> </span>build:frontend
</span></code></pre></div> <hr> <h2 id=deploy-script-optional>Deploy-Script (optional)<a class=headerlink href=#deploy-script-optional title="Permanent link">&para;</a></h2> <p><code>deploy-ripster.sh</code> synchronisiert den lokalen Stand auf einen Remote-Host per <code>rsync</code>/SSH und schützt <code>backend/data</code>.</p> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
+1 -1
View File
@@ -1 +1 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/deployment/ rel=canonical><link href=../configuration/environment/ rel=prev><link href=development/ rel=next><link rel=icon href=../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Deployment - Ripster</title><link rel=stylesheet href=../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../stylesheets/extra.css><script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#deployment class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Deployment </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-Line truncated
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/deployment/ rel=canonical><link href=../configuration/environment/ rel=prev><link href=development/ rel=next><link rel=icon href=../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Deployment - Ripster</title><link rel=stylesheet href=../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../stylesheets/extra.css><script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#deployment class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Deployment </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-Line truncated
+53 -95
View File
@@ -1,99 +1,57 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/deployment/production/ rel=canonical><link href=../development/ rel=prev><link href=../../tools/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Produktion - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#produktions-deployment class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Produktion </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 Line truncated
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a> ↓
</span><span id=__span-0-3><a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a> nginx (Reverse Proxy)
</span><span id=__span-0-4><a id=__codelineno-0-4 name=__codelineno-0-4 href=#__codelineno-0-4></a> ↓
</span><span id=__span-0-5><a id=__codelineno-0-5 name=__codelineno-0-5 href=#__codelineno-0-5></a> ┌────┴────┐
</span><span id=__span-0-6><a id=__codelineno-0-6 name=__codelineno-0-6 href=#__codelineno-0-6></a> │ │
</span><span id=__span-0-7><a id=__codelineno-0-7 name=__codelineno-0-7 href=#__codelineno-0-7></a>Backend Frontend
</span><span id=__span-0-8><a id=__codelineno-0-8 name=__codelineno-0-8 href=#__codelineno-0-8></a> :3001 (statische Dateien)
</span></code></pre></div> <hr> <h2 id=systemd-service>systemd-Service<a class=headerlink href=#systemd-service title="Permanent link">&para;</a></h2> <p>Für ein dauerhaftes Betreiben als systemd-Service:</p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a>sudo<span class=w> </span>nano<span class=w> </span>/etc/systemd/system/ripster.service
</span></code></pre></div> <div class="language-ini highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a><span class=k>[Unit]</span>
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a><span class=na>Description</span><span class=o>=</span><span class=s>Ripster - Disc Ripping Service</span>
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/deployment/production/ rel=canonical><link href=../development/ rel=prev><link href=../../tools/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Produktion - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#produktions-deployment class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Produktion </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 Line truncated
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a> -&gt; nginx (Reverse Proxy + statisches Frontend)
</span><span id=__span-0-3><a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a> -&gt; Backend API/WebSocket (Node.js, Port 3001)
</span></code></pre></div> <p>Wichtig: Das Backend serviert im aktuellen Stand keine <code>frontend/dist</code>-Dateien automatisch.</p> <hr> <h2 id=1-frontend-builden>1) Frontend builden<a class=headerlink href=#1-frontend-builden title="Permanent link">&para;</a></h2> <div class="language-bash highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a><span class=nb>cd</span><span class=w> </span>frontend
</span><span id=__span-1-2><a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a>npm<span class=w> </span>install
</span><span id=__span-1-3><a id=__codelineno-1-3 name=__codelineno-1-3 href=#__codelineno-1-3></a>npm<span class=w> </span>run<span class=w> </span>build
</span></code></pre></div> <p>Artefakte liegen in <code>frontend/dist/</code>.</p> <hr> <h2 id=2-backend-als-systemd-service>2) Backend als systemd-Service<a class=headerlink href=#2-backend-als-systemd-service title="Permanent link">&para;</a></h2> <p>Beispiel <code>/etc/systemd/system/ripster-backend.service</code>:</p> <div class="language-ini highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a><span class=k>[Unit]</span>
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a><span class=na>Description</span><span class=o>=</span><span class=s>Ripster Backend</span>
</span><span id=__span-2-3><a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a><span class=na>After</span><span class=o>=</span><span class=s>network.target</span>
</span><span id=__span-2-4><a id=__codelineno-2-4 name=__codelineno-2-4 href=#__codelineno-2-4></a>
</span><span id=__span-2-5><a id=__codelineno-2-5 name=__codelineno-2-5 href=#__codelineno-2-5></a><span class=k>[Service]</span>
</span><span id=__span-2-6><a id=__codelineno-2-6 name=__codelineno-2-6 href=#__codelineno-2-6></a><span class=na>Type</span><span class=o>=</span><span class=s>simple</span>
</span><span id=__span-2-7><a id=__codelineno-2-7 name=__codelineno-2-7 href=#__codelineno-2-7></a><span class=na>User</span><span class=o>=</span><span class=s>michael</span>
</span><span id=__span-2-8><a id=__codelineno-2-8 name=__codelineno-2-8 href=#__codelineno-2-8></a><span class=na>WorkingDirectory</span><span class=o>=</span><span class=s>/home/michael/ripster</span>
</span><span id=__span-2-9><a id=__codelineno-2-9 name=__codelineno-2-9 href=#__codelineno-2-9></a><span class=na>ExecStart</span><span class=o>=</span><span class=s>/bin/bash /home/michael/ripster/start.sh</span>
</span><span id=__span-2-10><a id=__codelineno-2-10 name=__codelineno-2-10 href=#__codelineno-2-10></a><span class=na>ExecStop</span><span class=o>=</span><span class=s>/bin/bash /home/michael/ripster/kill.sh</span>
</span><span id=__span-2-11><a id=__codelineno-2-11 name=__codelineno-2-11 href=#__codelineno-2-11></a><span class=na>Restart</span><span class=o>=</span><span class=s>on-failure</span>
</span><span id=__span-2-12><a id=__codelineno-2-12 name=__codelineno-2-12 href=#__codelineno-2-12></a><span class=na>RestartSec</span><span class=o>=</span><span class=s>10s</span>
</span><span id=__span-2-13><a id=__codelineno-2-13 name=__codelineno-2-13 href=#__codelineno-2-13></a>
</span><span id=__span-2-14><a id=__codelineno-2-14 name=__codelineno-2-14 href=#__codelineno-2-14></a><span class=c1># Umgebungsvariablen</span>
</span><span id=__span-2-15><a id=__codelineno-2-15 name=__codelineno-2-15 href=#__codelineno-2-15></a><span class=na>Environment</span><span class=o>=</span><span class=s>NODE_ENV=production</span>
</span><span id=__span-2-16><a id=__codelineno-2-16 name=__codelineno-2-16 href=#__codelineno-2-16></a><span class=na>Environment</span><span class=o>=</span><span class=s>PORT=3001</span>
</span><span id=__span-2-17><a id=__codelineno-2-17 name=__codelineno-2-17 href=#__codelineno-2-17></a><span class=na>Environment</span><span class=o>=</span><span class=s>LOG_LEVEL=info</span>
</span><span id=__span-2-18><a id=__codelineno-2-18 name=__codelineno-2-18 href=#__codelineno-2-18></a>
</span><span id=__span-2-19><a id=__codelineno-2-19 name=__codelineno-2-19 href=#__codelineno-2-19></a><span class=k>[Install]</span>
</span><span id=__span-2-20><a id=__codelineno-2-20 name=__codelineno-2-20 href=#__codelineno-2-20></a><span class=na>WantedBy</span><span class=o>=</span><span class=s>multi-user.target</span>
</span></code></pre></div> <div class="language-bash highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a><span class=c1># Service aktivieren und starten</span>
</span><span id=__span-3-2><a id=__codelineno-3-2 name=__codelineno-3-2 href=#__codelineno-3-2></a>sudo<span class=w> </span>systemctl<span class=w> </span>daemon-reload
</span><span id=__span-3-3><a id=__codelineno-3-3 name=__codelineno-3-3 href=#__codelineno-3-3></a>sudo<span class=w> </span>systemctl<span class=w> </span><span class=nb>enable</span><span class=w> </span>ripster
</span><span id=__span-3-4><a id=__codelineno-3-4 name=__codelineno-3-4 href=#__codelineno-3-4></a>sudo<span class=w> </span>systemctl<span class=w> </span>start<span class=w> </span>ripster
</span><span id=__span-3-5><a id=__codelineno-3-5 name=__codelineno-3-5 href=#__codelineno-3-5></a>
</span><span id=__span-3-6><a id=__codelineno-3-6 name=__codelineno-3-6 href=#__codelineno-3-6></a><span class=c1># Status prüfen</span>
</span><span id=__span-3-7><a id=__codelineno-3-7 name=__codelineno-3-7 href=#__codelineno-3-7></a>sudo<span class=w> </span>systemctl<span class=w> </span>status<span class=w> </span>ripster
</span><span id=__span-3-8><a id=__codelineno-3-8 name=__codelineno-3-8 href=#__codelineno-3-8></a>
</span><span id=__span-3-9><a id=__codelineno-3-9 name=__codelineno-3-9 href=#__codelineno-3-9></a><span class=c1># Logs anzeigen</span>
</span><span id=__span-3-10><a id=__codelineno-3-10 name=__codelineno-3-10 href=#__codelineno-3-10></a>journalctl<span class=w> </span>-u<span class=w> </span>ripster<span class=w> </span>-f
</span></code></pre></div> <hr> <h2 id=frontend-build>Frontend-Build<a class=headerlink href=#frontend-build title="Permanent link">&para;</a></h2> <p>Für Produktion das Frontend bauen:</p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a><span class=nb>cd</span><span class=w> </span>frontend
</span><span id=__span-4-2><a id=__codelineno-4-2 name=__codelineno-4-2 href=#__codelineno-4-2></a>npm<span class=w> </span>run<span class=w> </span>build
</span></code></pre></div> <p>Die statischen Dateien landen in <code>frontend/dist/</code>.</p> <hr> <h2 id=nginx-konfiguration>nginx-Konfiguration<a class=headerlink href=#nginx-konfiguration title="Permanent link">&para;</a></h2> <div class="language-nginx highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a><span class=c1># /etc/nginx/sites-available/ripster</span>
</span><span id=__span-5-2><a id=__codelineno-5-2 name=__codelineno-5-2 href=#__codelineno-5-2></a><span class=k>server</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-5-3><a id=__codelineno-5-3 name=__codelineno-5-3 href=#__codelineno-5-3></a><span class=w> </span><span class=kn>listen</span><span class=w> </span><span class=mi>80</span><span class=p>;</span>
</span><span id=__span-5-4><a id=__codelineno-5-4 name=__codelineno-5-4 href=#__codelineno-5-4></a><span class=w> </span><span class=kn>server_name</span><span class=w> </span><span class=s>ripster.local</span><span class=p>;</span>
</span><span id=__span-5-5><a id=__codelineno-5-5 name=__codelineno-5-5 href=#__codelineno-5-5></a>
</span><span id=__span-5-6><a id=__codelineno-5-6 name=__codelineno-5-6 href=#__codelineno-5-6></a><span class=w> </span><span class=c1># Statisches Frontend</span>
</span><span id=__span-5-7><a id=__codelineno-5-7 name=__codelineno-5-7 href=#__codelineno-5-7></a><span class=w> </span><span class=kn>root</span><span class=w> </span><span class=s>/home/michael/ripster/frontend/dist</span><span class=p>;</span>
</span><span id=__span-5-8><a id=__codelineno-5-8 name=__codelineno-5-8 href=#__codelineno-5-8></a><span class=w> </span><span class=kn>index</span><span class=w> </span><span class=s>index.html</span><span class=p>;</span>
</span><span id=__span-5-9><a id=__codelineno-5-9 name=__codelineno-5-9 href=#__codelineno-5-9></a>
</span><span id=__span-5-10><a id=__codelineno-5-10 name=__codelineno-5-10 href=#__codelineno-5-10></a><span class=w> </span><span class=c1># SPA Fallback (React Router)</span>
</span><span id=__span-5-11><a id=__codelineno-5-11 name=__codelineno-5-11 href=#__codelineno-5-11></a><span class=w> </span><span class=kn>location</span><span class=w> </span><span class=s>/</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-5-12><a id=__codelineno-5-12 name=__codelineno-5-12 href=#__codelineno-5-12></a><span class=w> </span><span class=kn>try_files</span><span class=w> </span><span class=nv>$uri</span><span class=w> </span><span class=nv>$uri/</span><span class=w> </span><span class=s>/index.html</span><span class=p>;</span>
</span><span id=__span-5-13><a id=__codelineno-5-13 name=__codelineno-5-13 href=#__codelineno-5-13></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-5-14><a id=__codelineno-5-14 name=__codelineno-5-14 href=#__codelineno-5-14></a>
</span><span id=__span-5-15><a id=__codelineno-5-15 name=__codelineno-5-15 href=#__codelineno-5-15></a><span class=w> </span><span class=c1># API-Proxy zum Backend</span>
</span><span id=__span-5-16><a id=__codelineno-5-16 name=__codelineno-5-16 href=#__codelineno-5-16></a><span class=w> </span><span class=kn>location</span><span class=w> </span><span class=s>/api/</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-5-17><a id=__codelineno-5-17 name=__codelineno-5-17 href=#__codelineno-5-17></a><span class=w> </span><span class=kn>proxy_pass</span><span class=w> </span><span class=s>http://localhost:3001</span><span class=p>;</span>
</span><span id=__span-5-18><a id=__codelineno-5-18 name=__codelineno-5-18 href=#__codelineno-5-18></a><span class=w> </span><span class=kn>proxy_set_header</span><span class=w> </span><span class=s>Host</span><span class=w> </span><span class=nv>$host</span><span class=p>;</span>
</span><span id=__span-5-19><a id=__codelineno-5-19 name=__codelineno-5-19 href=#__codelineno-5-19></a><span class=w> </span><span class=kn>proxy_set_header</span><span class=w> </span><span class=s>X-Real-IP</span><span class=w> </span><span class=nv>$remote_addr</span><span class=p>;</span>
</span><span id=__span-5-20><a id=__codelineno-5-20 name=__codelineno-5-20 href=#__codelineno-5-20></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-5-21><a id=__codelineno-5-21 name=__codelineno-5-21 href=#__codelineno-5-21></a>
</span><span id=__span-5-22><a id=__codelineno-5-22 name=__codelineno-5-22 href=#__codelineno-5-22></a><span class=w> </span><span class=c1># WebSocket-Proxy</span>
</span><span id=__span-5-23><a id=__codelineno-5-23 name=__codelineno-5-23 href=#__codelineno-5-23></a><span class=w> </span><span class=kn>location</span><span class=w> </span><span class=s>/ws</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-5-24><a id=__codelineno-5-24 name=__codelineno-5-24 href=#__codelineno-5-24></a><span class=w> </span><span class=kn>proxy_pass</span><span class=w> </span><span class=s>http://localhost:3001</span><span class=p>;</span>
</span><span id=__span-5-25><a id=__codelineno-5-25 name=__codelineno-5-25 href=#__codelineno-5-25></a><span class=w> </span><span class=kn>proxy_http_version</span><span class=w> </span><span class=mi>1</span><span class=s>.1</span><span class=p>;</span>
</span><span id=__span-5-26><a id=__codelineno-5-26 name=__codelineno-5-26 href=#__codelineno-5-26></a><span class=w> </span><span class=kn>proxy_set_header</span><span class=w> </span><span class=s>Upgrade</span><span class=w> </span><span class=nv>$http_upgrade</span><span class=p>;</span>
</span><span id=__span-5-27><a id=__codelineno-5-27 name=__codelineno-5-27 href=#__codelineno-5-27></a><span class=w> </span><span class=kn>proxy_set_header</span><span class=w> </span><span class=s>Connection</span><span class=w> </span><span class=s>&quot;upgrade&quot;</span><span class=p>;</span>
</span><span id=__span-5-28><a id=__codelineno-5-28 name=__codelineno-5-28 href=#__codelineno-5-28></a><span class=w> </span><span class=kn>proxy_set_header</span><span class=w> </span><span class=s>Host</span><span class=w> </span><span class=nv>$host</span><span class=p>;</span>
</span><span id=__span-5-29><a id=__codelineno-5-29 name=__codelineno-5-29 href=#__codelineno-5-29></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-5-30><a id=__codelineno-5-30 name=__codelineno-5-30 href=#__codelineno-5-30></a><span class=p>}</span>
</span></code></pre></div> <div class="language-bash highlight"><pre><span></span><code><span id=__span-6-1><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a>sudo<span class=w> </span>ln<span class=w> </span>-s<span class=w> </span>/etc/nginx/sites-available/ripster<span class=w> </span>/etc/nginx/sites-enabled/
</span><span id=__span-6-2><a id=__codelineno-6-2 name=__codelineno-6-2 href=#__codelineno-6-2></a>sudo<span class=w> </span>nginx<span class=w> </span>-t<span class=w> </span><span class=o>&amp;&amp;</span><span class=w> </span>sudo<span class=w> </span>systemctl<span class=w> </span>reload<span class=w> </span>nginx
</span></code></pre></div> <hr> <h2 id=nur-backend-produktion-ohne-nginx>Nur-Backend-Produktion (ohne nginx)<a class=headerlink href=#nur-backend-produktion-ohne-nginx title="Permanent link">&para;</a></h2> <p>Falls kein Reverse Proxy gewünscht ist, kann das Backend die Frontend-Dateien direkt ausliefern:</p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-7-1><a id=__codelineno-7-1 name=__codelineno-7-1 href=#__codelineno-7-1></a><span class=c1># Frontend bauen</span>
</span><span id=__span-7-2><a id=__codelineno-7-2 name=__codelineno-7-2 href=#__codelineno-7-2></a><span class=nb>cd</span><span class=w> </span>frontend<span class=w> </span><span class=o>&amp;&amp;</span><span class=w> </span>npm<span class=w> </span>run<span class=w> </span>build
</span><span id=__span-7-3><a id=__codelineno-7-3 name=__codelineno-7-3 href=#__codelineno-7-3></a>
</span><span id=__span-7-4><a id=__codelineno-7-4 name=__codelineno-7-4 href=#__codelineno-7-4></a><span class=c1># Backend startet und serviert frontend/dist/</span>
</span><span id=__span-7-5><a id=__codelineno-7-5 name=__codelineno-7-5 href=#__codelineno-7-5></a><span class=nb>cd</span><span class=w> </span>backend<span class=w> </span><span class=o>&amp;&amp;</span><span class=w> </span><span class=nv>NODE_ENV</span><span class=o>=</span>production<span class=w> </span>npm<span class=w> </span>start
</span></code></pre></div> <p>Das Backend ist so konfiguriert, dass es im Produktionsmodus die <code>frontend/dist/</code>-Dateien als statische Assets ausliefert.</p> <hr> <h2 id=datenbank-backup>Datenbank-Backup<a class=headerlink href=#datenbank-backup title="Permanent link">&para;</a></h2> <div class="language-bash highlight"><pre><span></span><code><span id=__span-8-1><a id=__codelineno-8-1 name=__codelineno-8-1 href=#__codelineno-8-1></a><span class=c1># Datenbank sichern</span>
</span><span id=__span-8-2><a id=__codelineno-8-2 name=__codelineno-8-2 href=#__codelineno-8-2></a>cp<span class=w> </span>backend/data/ripster.db<span class=w> </span>backend/data/ripster.db.backup.<span class=k>$(</span>date<span class=w> </span>+%Y%m%d<span class=k>)</span>
</span><span id=__span-8-3><a id=__codelineno-8-3 name=__codelineno-8-3 href=#__codelineno-8-3></a>
</span><span id=__span-8-4><a id=__codelineno-8-4 name=__codelineno-8-4 href=#__codelineno-8-4></a><span class=c1># Oder mit SQLite-eigenem Backup-Befehl</span>
</span><span id=__span-8-5><a id=__codelineno-8-5 name=__codelineno-8-5 href=#__codelineno-8-5></a>sqlite3<span class=w> </span>backend/data/ripster.db<span class=w> </span><span class=s2>&quot;.backup &#39;/mnt/backup/ripster.db&#39;&quot;</span>
</span></code></pre></div> <div class="admonition tip"> <p class=admonition-title>Automatisches Backup</p> <p>Cron-Job für tägliches Backup: <div class="language-text highlight"><pre><span></span><code><span id=__span-9-1><a id=__codelineno-9-1 name=__codelineno-9-1 href=#__codelineno-9-1></a>0 3 * * * sqlite3 /home/michael/ripster/backend/data/ripster.db &quot;.backup &#39;/mnt/backup/ripster-$(date +\%Y\%m\%d).db&#39;&quot;
</span></code></pre></div></p> </div> <hr> <h2 id=log-rotation>Log-Rotation<a class=headerlink href=#log-rotation title="Permanent link">&para;</a></h2> <p>Ripster rotiert Logs automatisch täglich. Falls zusätzlich systemd-Journal-Rotation gewünscht ist:</p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-10-1><a id=__codelineno-10-1 name=__codelineno-10-1 href=#__codelineno-10-1></a><span class=c1># /etc/logrotate.d/ripster</span>
</span><span id=__span-10-2><a id=__codelineno-10-2 name=__codelineno-10-2 href=#__codelineno-10-2></a>/home/michael/ripster/backend/logs/*.log<span class=w> </span><span class=o>{</span>
</span><span id=__span-10-3><a id=__codelineno-10-3 name=__codelineno-10-3 href=#__codelineno-10-3></a><span class=w> </span>daily
</span><span id=__span-10-4><a id=__codelineno-10-4 name=__codelineno-10-4 href=#__codelineno-10-4></a><span class=w> </span>rotate<span class=w> </span><span class=m>14</span>
</span><span id=__span-10-5><a id=__codelineno-10-5 name=__codelineno-10-5 href=#__codelineno-10-5></a><span class=w> </span>compress
</span><span id=__span-10-6><a id=__codelineno-10-6 name=__codelineno-10-6 href=#__codelineno-10-6></a><span class=w> </span>missingok
</span><span id=__span-10-7><a id=__codelineno-10-7 name=__codelineno-10-7 href=#__codelineno-10-7></a><span class=w> </span>notifempty
</span><span id=__span-10-8><a id=__codelineno-10-8 name=__codelineno-10-8 href=#__codelineno-10-8></a><span class=o>}</span>
</span></code></pre></div> <hr> <h2 id=sicherheitshinweise>Sicherheitshinweise<a class=headerlink href=#sicherheitshinweise title="Permanent link">&para;</a></h2> <div class="admonition warning"> <p class=admonition-title>Heimnetz-Einsatz</p> <p>Ripster ist für den Einsatz im <strong>lokalen Heimnetz</strong> konzipiert und enthält <strong>keine Authentifizierung</strong>. Stelle sicher, dass der Dienst nicht öffentlich erreichbar ist.</p> </div> <p>Falls öffentlicher Zugang benötigt wird:</p> <ol> <li> <p><strong>Basic Auth</strong> via nginx: <div class="language-bash highlight"><pre><span></span><code><span id=__span-11-1><a id=__codelineno-11-1 name=__codelineno-11-1 href=#__codelineno-11-1></a>sudo<span class=w> </span>htpasswd<span class=w> </span>-c<span class=w> </span>/etc/nginx/.htpasswd<span class=w> </span>michael
</span></code></pre></div> <div class="language-nginx highlight"><pre><span></span><code><span id=__span-12-1><a id=__codelineno-12-1 name=__codelineno-12-1 href=#__codelineno-12-1></a><span class=k>location</span><span class=w> </span><span class=s>/</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-12-2><a id=__codelineno-12-2 name=__codelineno-12-2 href=#__codelineno-12-2></a><span class=w> </span><span class=kn>auth_basic</span><span class=w> </span><span class=s>&quot;Ripster&quot;</span><span class=p>;</span>
</span><span id=__span-12-3><a id=__codelineno-12-3 name=__codelineno-12-3 href=#__codelineno-12-3></a><span class=w> </span><span class=kn>auth_basic_user_file</span><span class=w> </span><span class=s>/etc/nginx/.htpasswd</span><span class=p>;</span>
</span><span id=__span-12-4><a id=__codelineno-12-4 name=__codelineno-12-4 href=#__codelineno-12-4></a><span class=w> </span><span class=c1># ...</span>
</span><span id=__span-12-5><a id=__codelineno-12-5 name=__codelineno-12-5 href=#__codelineno-12-5></a><span class=p>}</span>
</span></code></pre></div></p> </li> <li> <p><strong>VPN-Zugang</strong> (empfohlen): Zugriff nur über WireGuard/OpenVPN</p> </li> <li> <p><strong>SSL/TLS</strong>: Let's Encrypt mit certbot für HTTPS</p> </li> </ol> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
</span><span id=__span-2-7><a id=__codelineno-2-7 name=__codelineno-2-7 href=#__codelineno-2-7></a><span class=na>User</span><span class=o>=</span><span class=s>ripster</span>
</span><span id=__span-2-8><a id=__codelineno-2-8 name=__codelineno-2-8 href=#__codelineno-2-8></a><span class=na>WorkingDirectory</span><span class=o>=</span><span class=s>/opt/ripster/backend</span>
</span><span id=__span-2-9><a id=__codelineno-2-9 name=__codelineno-2-9 href=#__codelineno-2-9></a><span class=na>ExecStart</span><span class=o>=</span><span class=s>/usr/bin/env node src/index.js</span>
</span><span id=__span-2-10><a id=__codelineno-2-10 name=__codelineno-2-10 href=#__codelineno-2-10></a><span class=na>Restart</span><span class=o>=</span><span class=s>on-failure</span>
</span><span id=__span-2-11><a id=__codelineno-2-11 name=__codelineno-2-11 href=#__codelineno-2-11></a><span class=na>RestartSec</span><span class=o>=</span><span class=s>5</span>
</span><span id=__span-2-12><a id=__codelineno-2-12 name=__codelineno-2-12 href=#__codelineno-2-12></a><span class=na>Environment</span><span class=o>=</span><span class=s>NODE_ENV=production</span>
</span><span id=__span-2-13><a id=__codelineno-2-13 name=__codelineno-2-13 href=#__codelineno-2-13></a><span class=na>Environment</span><span class=o>=</span><span class=s>PORT=3001</span>
</span><span id=__span-2-14><a id=__codelineno-2-14 name=__codelineno-2-14 href=#__codelineno-2-14></a><span class=na>Environment</span><span class=o>=</span><span class=s>LOG_LEVEL=info</span>
</span><span id=__span-2-15><a id=__codelineno-2-15 name=__codelineno-2-15 href=#__codelineno-2-15></a>
</span><span id=__span-2-16><a id=__codelineno-2-16 name=__codelineno-2-16 href=#__codelineno-2-16></a><span class=k>[Install]</span>
</span><span id=__span-2-17><a id=__codelineno-2-17 name=__codelineno-2-17 href=#__codelineno-2-17></a><span class=na>WantedBy</span><span class=o>=</span><span class=s>multi-user.target</span>
</span></code></pre></div> <p>Aktivieren:</p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a>sudo<span class=w> </span>systemctl<span class=w> </span>daemon-reload
</span><span id=__span-3-2><a id=__codelineno-3-2 name=__codelineno-3-2 href=#__codelineno-3-2></a>sudo<span class=w> </span>systemctl<span class=w> </span><span class=nb>enable</span><span class=w> </span>--now<span class=w> </span>ripster-backend
</span><span id=__span-3-3><a id=__codelineno-3-3 name=__codelineno-3-3 href=#__codelineno-3-3></a>sudo<span class=w> </span>systemctl<span class=w> </span>status<span class=w> </span>ripster-backend
</span></code></pre></div> <hr> <h2 id=3-nginx-konfigurieren>3) nginx konfigurieren<a class=headerlink href=#3-nginx-konfigurieren title="Permanent link">&para;</a></h2> <p>Beispiel <code>/etc/nginx/sites-available/ripster</code>:</p> <div class="language-nginx highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a><span class=k>server</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-4-2><a id=__codelineno-4-2 name=__codelineno-4-2 href=#__codelineno-4-2></a><span class=w> </span><span class=kn>listen</span><span class=w> </span><span class=mi>80</span><span class=p>;</span>
</span><span id=__span-4-3><a id=__codelineno-4-3 name=__codelineno-4-3 href=#__codelineno-4-3></a><span class=w> </span><span class=kn>server_name</span><span class=w> </span><span class=s>ripster.local</span><span class=p>;</span>
</span><span id=__span-4-4><a id=__codelineno-4-4 name=__codelineno-4-4 href=#__codelineno-4-4></a>
</span><span id=__span-4-5><a id=__codelineno-4-5 name=__codelineno-4-5 href=#__codelineno-4-5></a><span class=w> </span><span class=kn>root</span><span class=w> </span><span class=s>/opt/ripster/frontend/dist</span><span class=p>;</span>
</span><span id=__span-4-6><a id=__codelineno-4-6 name=__codelineno-4-6 href=#__codelineno-4-6></a><span class=w> </span><span class=kn>index</span><span class=w> </span><span class=s>index.html</span><span class=p>;</span>
</span><span id=__span-4-7><a id=__codelineno-4-7 name=__codelineno-4-7 href=#__codelineno-4-7></a>
</span><span id=__span-4-8><a id=__codelineno-4-8 name=__codelineno-4-8 href=#__codelineno-4-8></a><span class=w> </span><span class=kn>location</span><span class=w> </span><span class=s>/</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-4-9><a id=__codelineno-4-9 name=__codelineno-4-9 href=#__codelineno-4-9></a><span class=w> </span><span class=kn>try_files</span><span class=w> </span><span class=nv>$uri</span><span class=w> </span><span class=nv>$uri/</span><span class=w> </span><span class=s>/index.html</span><span class=p>;</span>
</span><span id=__span-4-10><a id=__codelineno-4-10 name=__codelineno-4-10 href=#__codelineno-4-10></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-4-11><a id=__codelineno-4-11 name=__codelineno-4-11 href=#__codelineno-4-11></a>
</span><span id=__span-4-12><a id=__codelineno-4-12 name=__codelineno-4-12 href=#__codelineno-4-12></a><span class=w> </span><span class=kn>location</span><span class=w> </span><span class=s>/api/</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-4-13><a id=__codelineno-4-13 name=__codelineno-4-13 href=#__codelineno-4-13></a><span class=w> </span><span class=kn>proxy_pass</span><span class=w> </span><span class=s>http://127.0.0.1:3001</span><span class=p>;</span>
</span><span id=__span-4-14><a id=__codelineno-4-14 name=__codelineno-4-14 href=#__codelineno-4-14></a><span class=w> </span><span class=kn>proxy_set_header</span><span class=w> </span><span class=s>Host</span><span class=w> </span><span class=nv>$host</span><span class=p>;</span>
</span><span id=__span-4-15><a id=__codelineno-4-15 name=__codelineno-4-15 href=#__codelineno-4-15></a><span class=w> </span><span class=kn>proxy_set_header</span><span class=w> </span><span class=s>X-Real-IP</span><span class=w> </span><span class=nv>$remote_addr</span><span class=p>;</span>
</span><span id=__span-4-16><a id=__codelineno-4-16 name=__codelineno-4-16 href=#__codelineno-4-16></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-4-17><a id=__codelineno-4-17 name=__codelineno-4-17 href=#__codelineno-4-17></a>
</span><span id=__span-4-18><a id=__codelineno-4-18 name=__codelineno-4-18 href=#__codelineno-4-18></a><span class=w> </span><span class=kn>location</span><span class=w> </span><span class=s>/ws</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-4-19><a id=__codelineno-4-19 name=__codelineno-4-19 href=#__codelineno-4-19></a><span class=w> </span><span class=kn>proxy_pass</span><span class=w> </span><span class=s>http://127.0.0.1:3001</span><span class=p>;</span>
</span><span id=__span-4-20><a id=__codelineno-4-20 name=__codelineno-4-20 href=#__codelineno-4-20></a><span class=w> </span><span class=kn>proxy_http_version</span><span class=w> </span><span class=mi>1</span><span class=s>.1</span><span class=p>;</span>
</span><span id=__span-4-21><a id=__codelineno-4-21 name=__codelineno-4-21 href=#__codelineno-4-21></a><span class=w> </span><span class=kn>proxy_set_header</span><span class=w> </span><span class=s>Upgrade</span><span class=w> </span><span class=nv>$http_upgrade</span><span class=p>;</span>
</span><span id=__span-4-22><a id=__codelineno-4-22 name=__codelineno-4-22 href=#__codelineno-4-22></a><span class=w> </span><span class=kn>proxy_set_header</span><span class=w> </span><span class=s>Connection</span><span class=w> </span><span class=s>&quot;upgrade&quot;</span><span class=p>;</span>
</span><span id=__span-4-23><a id=__codelineno-4-23 name=__codelineno-4-23 href=#__codelineno-4-23></a><span class=w> </span><span class=kn>proxy_set_header</span><span class=w> </span><span class=s>Host</span><span class=w> </span><span class=nv>$host</span><span class=p>;</span>
</span><span id=__span-4-24><a id=__codelineno-4-24 name=__codelineno-4-24 href=#__codelineno-4-24></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-4-25><a id=__codelineno-4-25 name=__codelineno-4-25 href=#__codelineno-4-25></a><span class=p>}</span>
</span></code></pre></div> <p>Aktivieren:</p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a>sudo<span class=w> </span>ln<span class=w> </span>-s<span class=w> </span>/etc/nginx/sites-available/ripster<span class=w> </span>/etc/nginx/sites-enabled/
</span><span id=__span-5-2><a id=__codelineno-5-2 name=__codelineno-5-2 href=#__codelineno-5-2></a>sudo<span class=w> </span>nginx<span class=w> </span>-t
</span><span id=__span-5-3><a id=__codelineno-5-3 name=__codelineno-5-3 href=#__codelineno-5-3></a>sudo<span class=w> </span>systemctl<span class=w> </span>reload<span class=w> </span>nginx
</span></code></pre></div> <hr> <h2 id=datenbank-backup>Datenbank-Backup<a class=headerlink href=#datenbank-backup title="Permanent link">&para;</a></h2> <div class="language-bash highlight"><pre><span></span><code><span id=__span-6-1><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a>sqlite3<span class=w> </span>/opt/ripster/backend/data/ripster.db<span class=w> </span><span class=se>\</span>
</span><span id=__span-6-2><a id=__codelineno-6-2 name=__codelineno-6-2 href=#__codelineno-6-2></a><span class=w> </span><span class=s2>&quot;.backup &#39;/var/backups/ripster-</span><span class=k>$(</span>date<span class=w> </span>+%Y%m%d<span class=k>)</span><span class=s2>.db&#39;&quot;</span>
</span></code></pre></div> <hr> <h2 id=sicherheit>Sicherheit<a class=headerlink href=#sicherheit title="Permanent link">&para;</a></h2> <ul> <li>Ripster hat keine eingebaute Authentifizierung.</li> <li>Für externen Zugriff mindestens Basic Auth + TLS + Netzwerksegmentierung/VPN einsetzen.</li> <li>Secrets nicht ins Repo committen (<code>.env</code>, Settings-Felder).</li> </ul> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
@@ -1,7 +1,3 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/getting-started/configuration/ rel=canonical><link href=../installation/ rel=prev><link href=../quickstart/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Konfiguration - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#konfiguration class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Konfiguration </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.Line truncated
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a>sudo<span class=w> </span>mkdir<span class=w> </span>-p<span class=w> </span>/mnt/nas/<span class=o>{</span>raw,movies<span class=o>}</span>
</span><span id=__span-0-3><a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a>sudo<span class=w> </span>chown<span class=w> </span><span class=nv>$USER</span>:<span class=nv>$USER</span><span class=w> </span>/mnt/nas/<span class=o>{</span>raw,movies<span class=o>}</span>
</span></code></pre></div> </div> <h3 id=omdb-api>OMDb API<a class=headerlink href=#omdb-api title="Permanent link">&para;</a></h3> <table> <thead> <tr> <th>Einstellung</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>omdb_api_key</code></td> <td>API-Key von omdbapi.com</td> </tr> <tr> <td><code>omdb_default_type</code></td> <td>Standard-Suchtyp: <code>movie</code> oder <code>series</code></td> </tr> </tbody> </table> <hr> <h2 id=tool-konfiguration>Tool-Konfiguration<a class=headerlink href=#tool-konfiguration title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>Einstellung</th> <th>Standard</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>makemkv_command</code></td> <td><code>makemkvcon</code></td> <td>Pfad oder Befehl für MakeMKV</td> </tr> <tr> <td><code>handbrake_command</code></td> <td><code>HandBrakeCLI</code></td> <td>Pfad oder Befehl für HandBrake</td> </tr> <tr> <td><code>mediainfo_command</code></td> <td><code>mediainfo</code></td> <td>Pfad oder Befehl für MediaInfo</td> </tr> </tbody> </table> <div class="admonition tip"> <p class=admonition-title>Absolute Pfade</p> <p>Falls die Tools nicht im <code>PATH</code> sind, verwende absolute Pfade: <div class="language-text highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a>/usr/local/bin/HandBrakeCLI
</span></code></pre></div></p> </div> <hr> <h2 id=encoding-konfiguration>Encoding-Konfiguration<a class=headerlink href=#encoding-konfiguration title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>Einstellung</th> <th>Standard</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>handbrake_preset</code></td> <td><code>H.265 MKV 1080p30</code></td> <td>HandBrake-Preset-Name</td> </tr> <tr> <td><code>handbrake_extra_args</code></td> <td><em>(leer)</em></td> <td>Zusätzliche HandBrake-Argumente</td> </tr> <tr> <td><code>output_extension</code></td> <td><code>mkv</code></td> <td>Dateiendung der Ausgabedatei</td> </tr> <tr> <td><code>filename_template</code></td> <td><code>{title} ({year})</code></td> <td>Template für Dateinamen</td> </tr> </tbody> </table> <h3 id=dateiname-template>Dateiname-Template<a class=headerlink href=#dateiname-template title="Permanent link">&para;</a></h3> <p>Das Template unterstützt folgende Platzhalter:</p> <table> <thead> <tr> <th>Platzhalter</th> <th>Beschreibung</th> <th>Beispiel</th> </tr> </thead> <tbody> <tr> <td><code>{title}</code></td> <td>Filmtitel</td> <td><code>Inception</code></td> </tr> <tr> <td><code>{year}</code></td> <td>Erscheinungsjahr</td> <td><code>2010</code></td> </tr> <tr> <td><code>{imdb_id}</code></td> <td>IMDb-ID</td> <td><code>tt1375666</code></td> </tr> <tr> <td><code>{type}</code></td> <td><code>movie</code> oder <code>series</code></td> <td><code>movie</code></td> </tr> </tbody> </table> <p><strong>Beispiel-Template:</strong> <div class="language-text highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a>{title} ({year})
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a>→ Inception (2010).mkv
</span></code></pre></div></p> <hr> <h2 id=laufwerk-konfiguration>Laufwerk-Konfiguration<a class=headerlink href=#laufwerk-konfiguration title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>Einstellung</th> <th>Standard</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>drive_mode</code></td> <td><code>auto</code></td> <td><code>auto</code> (automatisch erkennen) oder <code>explicit</code> (festes Gerät)</td> </tr> <tr> <td><code>drive_device</code></td> <td><code>/dev/sr0</code></td> <td>Geräte-Pfad (nur bei <code>explicit</code>)</td> </tr> <tr> <td><code>disc_poll_interval_ms</code></td> <td><code>4000</code></td> <td>Polling-Intervall in Millisekunden</td> </tr> </tbody> </table> <hr> <h2 id=makemkv-konfiguration>MakeMKV-Konfiguration<a class=headerlink href=#makemkv-konfiguration title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>Einstellung</th> <th>Standard</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>makemkv_min_length_minutes</code></td> <td><code>15</code></td> <td>Mindestlänge für Titel in Minuten</td> </tr> <tr> <td><code>makemkv_backup_mode</code></td> <td><code>false</code></td> <td>Backup-Modus statt MKV-Modus</td> </tr> </tbody> </table> <div class="admonition info"> <p class=admonition-title>Backup-Modus</p> <p>Im Backup-Modus erstellt MakeMKV eine vollständige Kopie der Disc (inkl. Menüs). Der Standardmodus erstellt direkt MKV-Dateien.</p> </div> <hr> <h2 id=benachrichtigungen-pushover>Benachrichtigungen (PushOver)<a class=headerlink href=#benachrichtigungen-pushover title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>Einstellung</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>pushover_user_key</code></td> <td>Dein PushOver User-Key</td> </tr> <tr> <td><code>pushover_api_token</code></td> <td>API-Token deiner PushOver-App</td> </tr> </tbody> </table> <p>Nach der Eingabe kann die Verbindung mit dem <strong>Test-Button</strong> geprüft werden.</p> <hr> <h2 id=vollstandige-einstellungsreferenz>Vollständige Einstellungsreferenz<a class=headerlink href=#vollstandige-einstellungsreferenz title="Permanent link">&para;</a></h2> <p>Eine vollständige Liste aller Einstellungen mit Typen, Validierung und Standardwerten findest du unter:</p> <p><a href=../../configuration/settings-reference/ ><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13.22 19.03a.75.75 0 0 1 0-1.06L18.19 13H3.75a.75.75 0 0 1 0-1.5h14.44l-4.97-4.97a.749.749 0 0 1 .326-1.275.75.75 0 0 1 .734.215l6.25 6.25a.75.75 0 0 1 0 1.06l-6.25 6.25a.75.75 0 0 1-1.06 0"/></svg></span> Einstellungsreferenz</a></p> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.Line truncated
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/getting-started/configuration/ rel=canonical><link href=../installation/ rel=prev><link href=../quickstart/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Konfiguration - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#konfiguration class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Konfiguration </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.Line truncated
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a>-&gt; Inception (2010).mkv
</span></code></pre></div> <hr> <h2 id=makemkv-spezifisch>MakeMKV-spezifisch<a class=headerlink href=#makemkv-spezifisch title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>Einstellung</th> <th>Standard</th> <th>Hinweis</th> </tr> </thead> <tbody> <tr> <td><code>makemkv_min_length_minutes</code></td> <td><code>60</code></td> <td>Kandidaten-Filter</td> </tr> <tr> <td><code>makemkv_rip_mode_bluray</code></td> <td><code>backup</code></td> <td><code>mkv</code> oder <code>backup</code></td> </tr> <tr> <td><code>makemkv_rip_mode_dvd</code></td> <td><code>mkv</code></td> <td><code>mkv</code> oder <code>backup</code></td> </tr> <tr> <td><code>makemkv_registration_key</code></td> <td>leer</td> <td>optional, wird via <code>makemkvcon reg</code> gesetzt</td> </tr> </tbody> </table> <hr> <h2 id=monitoring-queue>Monitoring &amp; Queue<a class=headerlink href=#monitoring-queue title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>Einstellung</th> <th>Standard</th> </tr> </thead> <tbody> <tr> <td><code>hardware_monitoring_enabled</code></td> <td><code>true</code></td> </tr> <tr> <td><code>hardware_monitoring_interval_ms</code></td> <td><code>5000</code></td> </tr> <tr> <td><code>pipeline_max_parallel_jobs</code></td> <td><code>1</code></td> </tr> </tbody> </table> <hr> <h2 id=pushover-optional>PushOver (optional)<a class=headerlink href=#pushover-optional title="Permanent link">&para;</a></h2> <p>Basis:</p> <ul> <li><code>pushover_enabled</code></li> <li><code>pushover_token</code></li> <li><code>pushover_user</code></li> </ul> <p>Zusätzlich pro Event ein/aus (z. B. <code>pushover_notify_job_finished</code>).</p> <hr> <h2 id=verwandte-doku>Verwandte Doku<a class=headerlink href=#verwandte-doku title="Permanent link">&para;</a></h2> <ul> <li><a href=../../configuration/settings-reference/ >Einstellungsreferenz</a></li> <li><a href=../../configuration/environment/ >Umgebungsvariablen</a></li> </ul> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigatiLine truncated
+1 -1
View File
@@ -1 +1 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/getting-started/ rel=canonical><link rel=prev href=..><link href=prerequisites/ rel=next><link rel=icon href=../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Erste Schritte - Ripster</title><link rel=stylesheet href=../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../stylesheets/extra.css><script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#erste-schritte class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Erste Schritte </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.2Line truncated
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/getting-started/ rel=canonical><link rel=prev href=..><link href=prerequisites/ rel=next><link rel=icon href=../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Erste Schritte - Ripster</title><link rel=stylesheet href=../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../stylesheets/extra.css><script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#erste-schritte class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Erste Schritte </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.2Line truncated
+20 -40
View File
@@ -1,41 +1,21 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/getting-started/installation/ rel=canonical><link href=../prerequisites/ rel=prev><link href=../configuration/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Installation - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#installation class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Installation </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.Line truncated
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/getting-started/installation/ rel=canonical><link href=../prerequisites/ rel=prev><link href=../configuration/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Installation - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#installation class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Installation </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.Line truncated
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a><span class=nb>cd</span><span class=w> </span>ripster
</span></code></pre></div> <hr> <h2 id=automatischer-start>Automatischer Start<a class=headerlink href=#automatischer-start title="Permanent link">&para;</a></h2> <p>Ripster enthält ein <code>start.sh</code>-Skript, das alle Abhängigkeiten installiert und Backend + Frontend gleichzeitig startet:</p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a>./start.sh
</span></code></pre></div> <p>Das Skript führt automatisch folgende Schritte durch:</p> <ol> <li><strong>Node.js-Versionscheck</strong> – prüft ob &gt;= 20.19.0 verfügbar ist (mit nvm/npx-Fallback)</li> <li><strong>Abhängigkeiten installieren</strong> – <code>npm install</code> für Root, Backend und Frontend</li> <li><strong>Dienste starten</strong> – Backend und Frontend werden parallel gestartet</li> </ol> <div class="admonition success"> <p class=admonition-title>Erfolgreich gestartet</p> <ul> <li>Backend läuft auf <code>http://localhost:3001</code></li> <li>Frontend läuft auf <code>http://localhost:5173</code></li> </ul> </div> <hr> <h2 id=manuelle-installation>Manuelle Installation<a class=headerlink href=#manuelle-installation title="Permanent link">&para;</a></h2> <p>Falls du mehr Kontrolle benötigst:</p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a><span class=c1># Root-Abhängigkeiten</span>
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a>npm<span class=w> </span>install
</span><span id=__span-2-3><a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a>
</span><span id=__span-2-4><a id=__codelineno-2-4 name=__codelineno-2-4 href=#__codelineno-2-4></a><span class=c1># Backend-Abhängigkeiten</span>
</span><span id=__span-2-5><a id=__codelineno-2-5 name=__codelineno-2-5 href=#__codelineno-2-5></a><span class=nb>cd</span><span class=w> </span>backend<span class=w> </span><span class=o>&amp;&amp;</span><span class=w> </span>npm<span class=w> </span>install<span class=w> </span><span class=o>&amp;&amp;</span><span class=w> </span><span class=nb>cd</span><span class=w> </span>..
</span><span id=__span-2-6><a id=__codelineno-2-6 name=__codelineno-2-6 href=#__codelineno-2-6></a>
</span><span id=__span-2-7><a id=__codelineno-2-7 name=__codelineno-2-7 href=#__codelineno-2-7></a><span class=c1># Frontend-Abhängigkeiten</span>
</span><span id=__span-2-8><a id=__codelineno-2-8 name=__codelineno-2-8 href=#__codelineno-2-8></a><span class=nb>cd</span><span class=w> </span>frontend<span class=w> </span><span class=o>&amp;&amp;</span><span class=w> </span>npm<span class=w> </span>install<span class=w> </span><span class=o>&amp;&amp;</span><span class=w> </span><span class=nb>cd</span><span class=w> </span>..
</span><span id=__span-2-9><a id=__codelineno-2-9 name=__codelineno-2-9 href=#__codelineno-2-9></a>
</span><span id=__span-2-10><a id=__codelineno-2-10 name=__codelineno-2-10 href=#__codelineno-2-10></a><span class=c1># Backend starten (Terminal 1)</span>
</span><span id=__span-2-11><a id=__codelineno-2-11 name=__codelineno-2-11 href=#__codelineno-2-11></a><span class=nb>cd</span><span class=w> </span>backend<span class=w> </span><span class=o>&amp;&amp;</span><span class=w> </span>npm<span class=w> </span>run<span class=w> </span>dev
</span><span id=__span-2-12><a id=__codelineno-2-12 name=__codelineno-2-12 href=#__codelineno-2-12></a>
</span><span id=__span-2-13><a id=__codelineno-2-13 name=__codelineno-2-13 href=#__codelineno-2-13></a><span class=c1># Frontend starten (Terminal 2)</span>
</span><span id=__span-2-14><a id=__codelineno-2-14 name=__codelineno-2-14 href=#__codelineno-2-14></a><span class=nb>cd</span><span class=w> </span>frontend<span class=w> </span><span class=o>&amp;&amp;</span><span class=w> </span>npm<span class=w> </span>run<span class=w> </span>dev
</span></code></pre></div> <hr> <h2 id=umgebungsvariablen-konfigurieren>Umgebungsvariablen konfigurieren<a class=headerlink href=#umgebungsvariablen-konfigurieren title="Permanent link">&para;</a></h2> <h3 id=backend>Backend<a class=headerlink href=#backend title="Permanent link">&para;</a></h3> <div class="language-bash highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a>cp<span class=w> </span>backend/.env.example<span class=w> </span>backend/.env
</span></code></pre></div> <p>Bearbeite <code>backend/.env</code>:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a>PORT=3001
</span><span id=__span-4-2><a id=__codelineno-4-2 name=__codelineno-4-2 href=#__codelineno-4-2></a>DB_PATH=./data/ripster.db
</span><span id=__span-4-3><a id=__codelineno-4-3 name=__codelineno-4-3 href=#__codelineno-4-3></a>CORS_ORIGIN=http://localhost:5173
</span><span id=__span-4-4><a id=__codelineno-4-4 name=__codelineno-4-4 href=#__codelineno-4-4></a>LOG_DIR=./logs
</span><span id=__span-4-5><a id=__codelineno-4-5 name=__codelineno-4-5 href=#__codelineno-4-5></a>LOG_LEVEL=info
</span></code></pre></div> <h3 id=frontend>Frontend<a class=headerlink href=#frontend title="Permanent link">&para;</a></h3> <div class="language-bash highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a>cp<span class=w> </span>frontend/.env.example<span class=w> </span>frontend/.env
</span></code></pre></div> <p>Bearbeite <code>frontend/.env</code>:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-6-1><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a>VITE_API_BASE=http://localhost:3001
</span><span id=__span-6-2><a id=__codelineno-6-2 name=__codelineno-6-2 href=#__codelineno-6-2></a>VITE_WS_URL=ws://localhost:3001
</span></code></pre></div> <div class="admonition tip"> <p class=admonition-title>Alle Umgebungsvariablen</p> <p>Eine vollständige Übersicht aller Umgebungsvariablen findest du unter <a href=../../configuration/environment/ >Umgebungsvariablen</a>.</p> </div> <hr> <h2 id=datenbank-initialisieren>Datenbank initialisieren<a class=headerlink href=#datenbank-initialisieren title="Permanent link">&para;</a></h2> <p>Die SQLite-Datenbank wird <strong>automatisch</strong> beim ersten Start erstellt und mit dem Schema aus <code>db/schema.sql</code> initialisiert. Es sind keine manuellen Datenbankschritte erforderlich.</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-7-1><a id=__codelineno-7-1 name=__codelineno-7-1 href=#__codelineno-7-1></a>backend/data/
</span><span id=__span-7-2><a id=__codelineno-7-2 name=__codelineno-7-2 href=#__codelineno-7-2></a>└── ripster.db ← Wird automatisch angelegt
</span></code></pre></div> <hr> <h2 id=stoppen>Stoppen<a class=headerlink href=#stoppen title="Permanent link">&para;</a></h2> <div class="language-bash highlight"><pre><span></span><code><span id=__span-8-1><a id=__codelineno-8-1 name=__codelineno-8-1 href=#__codelineno-8-1></a>./kill.sh
</span></code></pre></div> <p>Das Skript beendet Backend- und Frontend-Prozesse graceful.</p> <hr> <h2 id=verzeichnisstruktur-nach-installation>Verzeichnisstruktur nach Installation<a class=headerlink href=#verzeichnisstruktur-nach-installation title="Permanent link">&para;</a></h2> <div class="language-text highlight"><pre><span></span><code><span id=__span-9-1><a id=__codelineno-9-1 name=__codelineno-9-1 href=#__codelineno-9-1></a>ripster/
</span><span id=__span-9-2><a id=__codelineno-9-2 name=__codelineno-9-2 href=#__codelineno-9-2></a>├── backend/
</span><span id=__span-9-3><a id=__codelineno-9-3 name=__codelineno-9-3 href=#__codelineno-9-3></a>│ ├── data/ ← SQLite-Datenbank (nach erstem Start)
</span><span id=__span-9-4><a id=__codelineno-9-4 name=__codelineno-9-4 href=#__codelineno-9-4></a>│ ├── logs/ ← Log-Dateien
</span><span id=__span-9-5><a id=__codelineno-9-5 name=__codelineno-9-5 href=#__codelineno-9-5></a>│ ├── node_modules/ ← Backend-Abhängigkeiten
</span><span id=__span-9-6><a id=__codelineno-9-6 name=__codelineno-9-6 href=#__codelineno-9-6></a>│ └── .env ← Backend-Konfiguration
</span><span id=__span-9-7><a id=__codelineno-9-7 name=__codelineno-9-7 href=#__codelineno-9-7></a>├── frontend/
</span><span id=__span-9-8><a id=__codelineno-9-8 name=__codelineno-9-8 href=#__codelineno-9-8></a>│ ├── node_modules/ ← Frontend-Abhängigkeiten
</span><span id=__span-9-9><a id=__codelineno-9-9 name=__codelineno-9-9 href=#__codelineno-9-9></a>│ ├── dist/ ← Production-Build (nach npm run build)
</span><span id=__span-9-10><a id=__codelineno-9-10 name=__codelineno-9-10 href=#__codelineno-9-10></a>│ └── .env ← Frontend-Konfiguration
</span><span id=__span-9-11><a id=__codelineno-9-11 name=__codelineno-9-11 href=#__codelineno-9-11></a>└── node_modules/ ← Root-Abhängigkeiten (concurrently etc.)
</span></code></pre></div> <hr> <h2 id=nachste-schritte>Nächste Schritte<a class=headerlink href=#nachste-schritte title="Permanent link">&para;</a></h2> <p>Nach erfolgreicher Installation:</p> <ol> <li>Öffne <a href=http://localhost:5173>http://localhost:5173</a></li> <li>Navigiere zu <strong>Einstellungen</strong></li> <li>Konfiguriere Pfade, API-Keys und Encoding-Presets</li> </ol> <p><a href=../configuration/ ><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13.22 19.03a.75.75 0 0 1 0-1.06L18.19 13H3.75a.75.75 0 0 1 0-1.5h14.44l-4.97-4.97a.749.749 0 0 1 .326-1.275.75.75 0 0 1 .734.215l6.25 6.25a.75.75 0 0 1 0 1.06l-6.25 6.25a.75.75 0 0 1-1.06 0"/></svg></span> Zur Konfiguration</a></p> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
</span></code></pre></div> <hr> <h2 id=dev-start-empfohlen>Dev-Start (empfohlen)<a class=headerlink href=#dev-start-empfohlen title="Permanent link">&para;</a></h2> <div class="language-bash highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a>./start.sh
</span></code></pre></div> <p><code>start.sh</code>:</p> <ol> <li>prüft Node-Version (<code>&gt;= 20.19.0</code>)</li> <li>installiert Dependencies (Root/Backend/Frontend)</li> <li>startet Backend + Frontend parallel</li> </ol> <p>Danach:</p> <ul> <li>Backend: <code>http://localhost:3001</code></li> <li>Frontend: <code>http://localhost:5173</code></li> </ul> <p>Stoppen: mit <code>Ctrl+C</code> im laufenden Terminal.</p> <hr> <h2 id=manuell-starten>Manuell starten<a class=headerlink href=#manuell-starten title="Permanent link">&para;</a></h2> <div class="language-bash highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a>npm<span class=w> </span>install
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a>npm<span class=w> </span>--prefix<span class=w> </span>backend<span class=w> </span>install
</span><span id=__span-2-3><a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a>npm<span class=w> </span>--prefix<span class=w> </span>frontend<span class=w> </span>install
</span><span id=__span-2-4><a id=__codelineno-2-4 name=__codelineno-2-4 href=#__codelineno-2-4></a>npm<span class=w> </span>run<span class=w> </span>dev
</span></code></pre></div> <p>Oder getrennt:</p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a>npm<span class=w> </span>run<span class=w> </span>dev:backend
</span><span id=__span-3-2><a id=__codelineno-3-2 name=__codelineno-3-2 href=#__codelineno-3-2></a>npm<span class=w> </span>run<span class=w> </span>dev:frontend
</span></code></pre></div> <hr> <h2 id=optional-env-dateien-anlegen>Optional: .env-Dateien anlegen<a class=headerlink href=#optional-env-dateien-anlegen title="Permanent link">&para;</a></h2> <h3 id=backend>Backend<a class=headerlink href=#backend title="Permanent link">&para;</a></h3> <div class="language-bash highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a>cp<span class=w> </span>backend/.env.example<span class=w> </span>backend/.env
</span></code></pre></div> <p>Beispiel:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a>PORT=3001
</span><span id=__span-5-2><a id=__codelineno-5-2 name=__codelineno-5-2 href=#__codelineno-5-2></a>DB_PATH=./data/ripster.db
</span><span id=__span-5-3><a id=__codelineno-5-3 name=__codelineno-5-3 href=#__codelineno-5-3></a>LOG_DIR=./logs
</span><span id=__span-5-4><a id=__codelineno-5-4 name=__codelineno-5-4 href=#__codelineno-5-4></a>CORS_ORIGIN=http://localhost:5173
</span><span id=__span-5-5><a id=__codelineno-5-5 name=__codelineno-5-5 href=#__codelineno-5-5></a>LOG_LEVEL=info
</span></code></pre></div> <h3 id=frontend>Frontend<a class=headerlink href=#frontend title="Permanent link">&para;</a></h3> <div class="language-bash highlight"><pre><span></span><code><span id=__span-6-1><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a>cp<span class=w> </span>frontend/.env.example<span class=w> </span>frontend/.env
</span></code></pre></div> <p>Beispiel:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-7-1><a id=__codelineno-7-1 name=__codelineno-7-1 href=#__codelineno-7-1></a>VITE_API_BASE=/api
</span><span id=__span-7-2><a id=__codelineno-7-2 name=__codelineno-7-2 href=#__codelineno-7-2></a># optional:
</span><span id=__span-7-3><a id=__codelineno-7-3 name=__codelineno-7-3 href=#__codelineno-7-3></a># VITE_WS_URL=ws://localhost:3001/ws
</span></code></pre></div> <hr> <h2 id=datenbank>Datenbank<a class=headerlink href=#datenbank title="Permanent link">&para;</a></h2> <p>SQLite wird automatisch beim Backend-Start initialisiert:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-8-1><a id=__codelineno-8-1 name=__codelineno-8-1 href=#__codelineno-8-1></a>backend/data/ripster.db
</span></code></pre></div> <p>Schema-Quelle: <code>db/schema.sql</code></p> <hr> <h2 id=nachste-schritte>Nächste Schritte<a class=headerlink href=#nachste-schritte title="Permanent link">&para;</a></h2> <ol> <li>Browser öffnen: <code>http://localhost:5173</code></li> <li>In <code>Settings</code> Pfade/Tools/API-Keys prüfen</li> <li>Erste Disc einlegen und Workflow starten</li> </ol> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
@@ -1,4 +1,4 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/getting-started/prerequisites/ rel=canonical><link href=../ rel=prev><link href=../installation/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Voraussetzungen - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#voraussetzungen class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Voraussetzungen </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5Line truncated
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/getting-started/prerequisites/ rel=canonical><link href=../ rel=prev><link href=../installation/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Voraussetzungen - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#voraussetzungen class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Voraussetzungen </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5Line truncated
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a>curl<span class=w> </span>-o-<span class=w> </span>https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh<span class=w> </span><span class=p>|</span><span class=w> </span>bash
</span><span id=__span-0-3><a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a>
</span><span id=__span-0-4><a id=__codelineno-0-4 name=__codelineno-0-4 href=#__codelineno-0-4></a><span class=c1># Node.js 20 installieren</span>
@@ -38,10 +38,13 @@
</span><span id=__span-5-6><a id=__codelineno-5-6 name=__codelineno-5-6 href=#__codelineno-5-6></a>
</span><span id=__span-5-7><a id=__codelineno-5-7 name=__codelineno-5-7 href=#__codelineno-5-7></a><span class=c1># Version prüfen</span>
</span><span id=__span-5-8><a id=__codelineno-5-8 name=__codelineno-5-8 href=#__codelineno-5-8></a>mediainfo<span class=w> </span>--Version
</span></code></pre></div> <hr> <h2 id=disc-laufwerk>Disc-Laufwerk<a class=headerlink href=#disc-laufwerk title="Permanent link">&para;</a></h2> <p>Ripster benötigt ein physisches <strong>DVD- oder Blu-ray-Laufwerk</strong>.</p> <div class="admonition info"> <p class=admonition-title>Blu-ray unter Linux</p> <p>Für Blu-ray-Ripping unter Linux wird zusätzlich <code>libaacs</code> benötigt. MakeMKV bringt jedoch eine eigene Entschlüsselung mit, daher ist dies in den meisten Fällen nicht erforderlich.</p> </div> <div class="language-bash highlight"><pre><span></span><code><span id=__span-6-1><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a><span class=c1># Laufwerk prüfen</span>
</span></code></pre></div> <hr> <h2 id=disc-laufwerk>Disc-Laufwerk<a class=headerlink href=#disc-laufwerk title="Permanent link">&para;</a></h2> <p>Ripster benötigt ein physisches <strong>DVD- oder Blu-ray-Laufwerk</strong>.</p> <div class="admonition danger"> <p class=admonition-title>LibDriveIO-Modus erforderlich</p> <p>Das Laufwerk muss im <strong>LibDriveIO-Modus</strong> betrieben werden – MakeMKV greift direkt auf Rohdaten des Laufwerks zu. Ohne diesen Modus können verschlüsselte Blu-rays (insbesondere UHD) nicht gelesen werden.</p> <p>Nicht alle Laufwerke unterstützen den direkten Zugriff. Eine Anleitung zur Einrichtung und Liste kompatibler Laufwerke findet sich im <a href="https://www.makemkv.com/forum/viewtopic.php?t=18856">MakeMKV-Forum</a>.</p> </div> <div class="language-bash highlight"><pre><span></span><code><span id=__span-6-1><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a><span class=c1># Laufwerk prüfen</span>
</span><span id=__span-6-2><a id=__codelineno-6-2 name=__codelineno-6-2 href=#__codelineno-6-2></a>ls<span class=w> </span>/dev/sr*
</span><span id=__span-6-3><a id=__codelineno-6-3 name=__codelineno-6-3 href=#__codelineno-6-3></a><span class=c1># oder</span>
</span><span id=__span-6-4><a id=__codelineno-6-4 name=__codelineno-6-4 href=#__codelineno-6-4></a>lsblk<span class=w> </span><span class=p>|</span><span class=w> </span>grep<span class=w> </span>rom
</span></code></pre></div> <hr> <h2 id=omdb-api-key>OMDb API-Key<a class=headerlink href=#omdb-api-key title="Permanent link">&para;</a></h2> <p>Ripster verwendet die <a href=https://www.omdbapi.com/ >OMDb API</a> für Filmmetadaten.</p> <ol> <li>Registriere dich kostenlos auf <a href=https://www.omdbapi.com/apikey.aspx>omdbapi.com</a></li> <li>Bestätige deine E-Mail-Adresse</li> <li>Notiere deinen API-Key – du gibst ihn später in den Einstellungen ein</li> </ol> <hr> <h2 id=optionale-voraussetzungen>Optionale Voraussetzungen<a class=headerlink href=#optionale-voraussetzungen title="Permanent link">&para;</a></h2> <h3 id=pushover-benachrichtigungen>PushOver (Benachrichtigungen)<a class=headerlink href=#pushover-benachrichtigungen title="Permanent link">&para;</a></h3> <p>Für mobile Push-Benachrichtigungen bei Fertigstellung oder Fehlern:</p> <ul> <li>App kaufen auf <a href=https://pushover.net>pushover.net</a> (~5 USD einmalig)</li> <li><strong>User Key</strong> und <strong>API Token</strong> notieren</li> </ul> <h3 id=ssh-zugang-deployment>SSH-Zugang (Deployment)<a class=headerlink href=#ssh-zugang-deployment title="Permanent link">&para;</a></h3> <p>Für Remote-Deployment via <code>deploy-ripster.sh</code>:</p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-7-1><a id=__codelineno-7-1 name=__codelineno-7-1 href=#__codelineno-7-1></a><span class=c1># sshpass installieren</span>
</span><span id=__span-6-5><a id=__codelineno-6-5 name=__codelineno-6-5 href=#__codelineno-6-5></a>
</span><span id=__span-6-6><a id=__codelineno-6-6 name=__codelineno-6-6 href=#__codelineno-6-6></a><span class=c1># Laufwerk-Berechtigungen setzen (erforderlich für LibDriveIO)</span>
</span><span id=__span-6-7><a id=__codelineno-6-7 name=__codelineno-6-7 href=#__codelineno-6-7></a>sudo<span class=w> </span>chmod<span class=w> </span>a+rw<span class=w> </span>/dev/sr0
</span></code></pre></div> <div class="admonition info"> <p class=admonition-title>Blu-ray unter Linux</p> <p>MakeMKV bringt mit LibDriveIO eine eigene Entschlüsselung mit – externe Bibliotheken wie <code>libaacs</code> sind in der Regel nicht erforderlich.</p> </div> <hr> <h2 id=omdb-api-key>OMDb API-Key<a class=headerlink href=#omdb-api-key title="Permanent link">&para;</a></h2> <p>Ripster verwendet die <a href=https://www.omdbapi.com/ >OMDb API</a> für Filmmetadaten.</p> <ol> <li>Registriere dich kostenlos auf <a href=https://www.omdbapi.com/apikey.aspx>omdbapi.com</a></li> <li>Bestätige deine E-Mail-Adresse</li> <li>Notiere deinen API-Key – du gibst ihn später in den Einstellungen ein</li> </ol> <hr> <h2 id=optionale-voraussetzungen>Optionale Voraussetzungen<a class=headerlink href=#optionale-voraussetzungen title="Permanent link">&para;</a></h2> <h3 id=pushover-benachrichtigungen>PushOver (Benachrichtigungen)<a class=headerlink href=#pushover-benachrichtigungen title="Permanent link">&para;</a></h3> <p>Für mobile Push-Benachrichtigungen bei Fertigstellung oder Fehlern:</p> <ul> <li>App kaufen auf <a href=https://pushover.net>pushover.net</a> (~5 USD einmalig)</li> <li><strong>User Key</strong> und <strong>API Token</strong> notieren</li> </ul> <h3 id=ssh-zugang-deployment>SSH-Zugang (Deployment)<a class=headerlink href=#ssh-zugang-deployment title="Permanent link">&para;</a></h3> <p>Für Remote-Deployment via <code>deploy-ripster.sh</code>:</p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-7-1><a id=__codelineno-7-1 name=__codelineno-7-1 href=#__codelineno-7-1></a><span class=c1># sshpass installieren</span>
</span><span id=__span-7-2><a id=__codelineno-7-2 name=__codelineno-7-2 href=#__codelineno-7-2></a>sudo<span class=w> </span>apt-get<span class=w> </span>install<span class=w> </span>sshpass
</span></code></pre></div> <hr> <h2 id=checkliste>Checkliste<a class=headerlink href=#checkliste title="Permanent link">&para;</a></h2> <ul> <li>[ ] Node.js &gt;= 20.19.0 installiert (<code>node --version</code>)</li> <li>[ ] <code>makemkvcon</code> installiert (<code>makemkvcon --version</code>)</li> <li>[ ] <code>HandBrakeCLI</code> installiert (<code>HandBrakeCLI --version</code>)</li> <li>[ ] <code>mediainfo</code> installiert (<code>mediainfo --Version</code>)</li> <li>[ ] DVD/Blu-ray Laufwerk vorhanden (<code>ls /dev/sr*</code>)</li> <li>[ ] OMDb API-Key beschafft</li> </ul> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
+11 -94
View File
@@ -1,95 +1,12 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/getting-started/quickstart/ rel=canonical><link href=../configuration/ rel=prev><link href=../../architecture/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Schnellstart - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#schnellstart-vollstandiger-workflow class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Schnellstart </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24Line truncated
START(( )) --&gt; IDLE
IDLE --&gt;|Disc erkannt| DD[DISC_DETECTED]
DD --&gt;|Analyse starten| META[METADATA\nSELECTION]
META --&gt;|Metadaten übernommen| RTS[READY_TO\nSTART]
META --&gt;|vorhandenes RAW +\nPlaylist offen| WUD[WAITING_FOR\nUSER_DECISION]
RTS --&gt;|Auto-Start| RIP[RIPPING]
RTS --&gt;|Auto-Start mit RAW| MIC[MEDIAINFO\nCHECK]
RIP --&gt;|MKV fertig| MIC
RIP --&gt;|Fehler| ERR
MIC --&gt;|Playlist offen (Backup)| WUD
WUD --&gt;|Playlist bestätigt| MIC
WUD --&gt;|Playlist bestätigt,\nnoch kein RAW| RTS
MIC --&gt; RTE[READY_TO\nENCODE]
RTE --&gt;|Encoding starten| ENC[ENCODING]
ENC --&gt;|inkl. Post-Skripte| FIN([FINISHED])
ENC --&gt;|Fehler| ERR
ERR([ERROR]) --&gt;|Retry / Cancel| IDLE
style FIN fill:#e8f5e9,stroke:#66bb6a,color:#2e7d32
style ERR fill:#ffebee,stroke:#ef5350,color:#c62828
style WUD fill:#fff8e1,stroke:#ffa726,color:#e65100
style ENC fill:#f3e5f5,stroke:#ab47bc,color:#6a1b9a</code></pre> </div> </details> <hr> <h2 id=schritt-1-ripster-starten>Schritt 1 – Ripster starten<a class=headerlink href=#schritt-1-ripster-starten title="Permanent link">&para;</a></h2> <div class="language-bash highlight"><pre><span></span><code><span id=__span-0-1><a id=__codelineno-0-1 name=__codelineno-0-1 href=#__codelineno-0-1></a><span class=nb>cd</span><span class=w> </span>ripster
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/getting-started/quickstart/ rel=canonical><link href=../configuration/ rel=prev><link href=../../architecture/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Schnellstart - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#schnellstart-erster-kompletter-job class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Schnellstart </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 Line truncated
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a>./start.sh
</span></code></pre></div> <p>Öffne <a href=http://localhost:5173>http://localhost:5173</a> im Browser. Das Dashboard zeigt <code>IDLE</code>.</p> <hr> <h2 id=schritt-2-disc-einlegen-disc_detected>Schritt 2 – Disc einlegen → <code>DISC_DETECTED</code><a class=headerlink href=#schritt-2-disc-einlegen-disc_detected title="Permanent link">&para;</a></h2> <p>Lege eine DVD oder Blu-ray ein. Der <code>diskDetectionService</code> pollt das Laufwerk alle <code>disc_poll_interval_ms</code> Millisekunden (Standard: 4 Sekunden).</p> <p><strong>Was passiert im Code:</strong></p> <ul> <li><code>diskDetectionService</code> emittiert <code>discInserted</code> mit Geräteinformationen</li> <li><code>pipelineService.onDiscInserted()</code> wird aufgerufen</li> <li>Dashboard-Status-Badge zeigt <strong>"Medium erkannt"</strong></li> <li>Status-Text zeigt <strong>"Neue Disk erkannt"</strong></li> <li>Der <strong>"Analyse starten"</strong>-Button wird aktiv</li> </ul> <div class="admonition tip"> <p class=admonition-title>Manuelle Auslösung</p> <p>Falls die automatische Erkennung nicht greift: <div class="language-bash highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a>curl<span class=w> </span>-X<span class=w> </span>POST<span class=w> </span>http://localhost:3001/api/pipeline/analyze
</span></code></pre></div></p> </div> <hr> <h2 id=schritt-3-analyse-starten-metadata_selection>Schritt 3 – Analyse starten → <code>METADATA_SELECTION</code><a class=headerlink href=#schritt-3-analyse-starten-metadata_selection title="Permanent link">&para;</a></h2> <p>Klicke auf <strong>"Analyse starten"</strong>.</p> <p><strong>Was passiert im Code:</strong></p> <ol> <li>Ein neuer Job-Datensatz wird in der Datenbank angelegt (<code>status: METADATA_SELECTION</code>)</li> <li>Ripster versucht, den Titel automatisch aus dem Disc-Label/Modell zu ermitteln</li> <li>Mit diesem erkannten Titel wird sofort eine <strong>OMDb-Suche</strong> ausgelöst</li> <li>Der <code>MetadataSelectionDialog</code> öffnet sich im Frontend mit den vorgeladenen Suchergebnissen</li> </ol> <p><strong>Erkannter Titel:</strong> Der Disc-Label (z. B. <code>INCEPTION</code>) wird als Suchbegriff verwendet. Falls kein Label vorhanden, bleibt das Suchfeld leer.</p> <hr> <h2 id=schritt-4-metadaten-auswahlen-metadataselectiondialog>Schritt 4 – Metadaten auswählen (<code>MetadataSelectionDialog</code>)<a class=headerlink href=#schritt-4-metadaten-auswahlen-metadataselectiondialog title="Permanent link">&para;</a></h2> <p>Der Dialog zeigt vorgeladene OMDb-Suchergebnisse. Du kannst:</p> <h3 id=4a-omdb-suchergebnis-wahlen>4a) OMDb-Suchergebnis wählen<a class=headerlink href=#4a-omdb-suchergebnis-wahlen title="Permanent link">&para;</a></h3> <div class="language-text highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a>┌─────────────────────────────────────────────────┐
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a>│ Suche: [Inception ] 🔍 │
</span><span id=__span-2-3><a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a>├─────────────────────────────────────────────────┤
</span><span id=__span-2-4><a id=__codelineno-2-4 name=__codelineno-2-4 href=#__codelineno-2-4></a>│ ▶ Inception (2010) · Movie · tt1375666 │
</span><span id=__span-2-5><a id=__codelineno-2-5 name=__codelineno-2-5 href=#__codelineno-2-5></a>│ Inception: ... · Series · ... │
</span><span id=__span-2-6><a id=__codelineno-2-6 name=__codelineno-2-6 href=#__codelineno-2-6></a>├─────────────────────────────────────────────────┤
</span><span id=__span-2-7><a id=__codelineno-2-7 name=__codelineno-2-7 href=#__codelineno-2-7></a>│ [Auswahl übernehmen] │
</span><span id=__span-2-8><a id=__codelineno-2-8 name=__codelineno-2-8 href=#__codelineno-2-8></a>└─────────────────────────────────────────────────┘
</span></code></pre></div> <ul> <li>Suche durch Titel anpassen und Enter drücken</li> <li>Typ-Filter: <code>movie</code> / <code>series</code> umschalten möglich</li> <li>Einen Eintrag anklicken, dann <strong>"Auswahl übernehmen"</strong></li> </ul> <h3 id=4b-manuelle-eingabe-ohne-omdb>4b) Manuelle Eingabe (ohne OMDb)<a class=headerlink href=#4b-manuelle-eingabe-ohne-omdb title="Permanent link">&para;</a></h3> <p>Falls kein passendes Ergebnis gefunden wird: - Titel, Jahr und IMDb-ID manuell eingeben - OMDb-Poster wird übersprungen</p> <p><strong>Was passiert nach Bestätigung:</strong></p> <p>Ripster ruft <code>pipelineService.selectMetadata()</code> auf und startet den nächsten Schritt automatisch:</p> <ul> <li>Job wird auf <code>READY_TO_START</code> gesetzt (kurzer Übergangszustand)</li> <li>Falls bereits RAW vorhanden: direkter Sprung zu <code>MEDIAINFO_CHECK</code></li> <li>Falls kein RAW vorhanden: automatischer Start von <code>RIPPING</code></li> <li>Wenn bereits andere Jobs laufen, landet der Start stattdessen in der Queue</li> </ul> <hr> <h2 id=schritt-5-optional-playlist-auswahl-waiting_for_user_decision>Schritt 5 – Optional: Playlist-Auswahl → <code>WAITING_FOR_USER_DECISION</code><a class=headerlink href=#schritt-5-optional-playlist-auswahl-waiting_for_user_decision title="Permanent link">&para;</a></h2> <p>Dieser Zustand erscheint nur bei mehrdeutigen Blu-ray-Playlists (typisch nach RAW-Analyse im Backup-Modus).</p> <p>Der <strong>Playlist-Auswahl-Dialog</strong> erscheint <strong>zusätzlich</strong> (nach dem Metadaten-Dialog):</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a>┌───────────────────────────────────────────────────────────────┐
</span><span id=__span-3-2><a id=__codelineno-3-2 name=__codelineno-3-2 href=#__codelineno-3-2></a>│ Playlist-Auswahl │
</span><span id=__span-3-3><a id=__codelineno-3-3 name=__codelineno-3-3 href=#__codelineno-3-3></a>│ Es wurden mehrere Titel mit ähnlicher Laufzeit gefunden. │
</span><span id=__span-3-4><a id=__codelineno-3-4 name=__codelineno-3-4 href=#__codelineno-3-4></a>│ Bitte wähle die korrekte Playlist: │
</span><span id=__span-3-5><a id=__codelineno-3-5 name=__codelineno-3-5 href=#__codelineno-3-5></a>├───────────┬──────────┬────────┬──────────────────────────────┤
</span><span id=__span-3-6><a id=__codelineno-3-6 name=__codelineno-3-6 href=#__codelineno-3-6></a>│ Playlist │ Laufzeit │ Score │ Bewertung │
</span><span id=__span-3-7><a id=__codelineno-3-7 name=__codelineno-3-7 href=#__codelineno-3-7></a>├───────────┼──────────┼────────┼──────────────────────────────┤
</span><span id=__span-3-8><a id=__codelineno-3-8 name=__codelineno-3-8 href=#__codelineno-3-8></a>│ ● 00800 │ 2:28:05 │ +18 │ wahrscheinlich korrekt │
</span><span id=__span-3-9><a id=__codelineno-3-9 name=__codelineno-3-9 href=#__codelineno-3-9></a>│ │ │ │ (lineare Segmentfolge) │
</span><span id=__span-3-10><a id=__codelineno-3-10 name=__codelineno-3-10 href=#__codelineno-3-10></a>├───────────┼──────────┼────────┼──────────────────────────────┤
</span><span id=__span-3-11><a id=__codelineno-3-11 name=__codelineno-3-11 href=#__codelineno-3-11></a>│ ○ 00801 │ 2:28:12 │ −4 │ Auffällige Segmentreihenfolge │
</span><span id=__span-3-12><a id=__codelineno-3-12 name=__codelineno-3-12 href=#__codelineno-3-12></a>├───────────┼──────────┼────────┼──────────────────────────────┤
</span><span id=__span-3-13><a id=__codelineno-3-13 name=__codelineno-3-13 href=#__codelineno-3-13></a>│ ○ 00900 │ 2:28:05 │ −32 │ Fake-Struktur │
</span><span id=__span-3-14><a id=__codelineno-3-14 name=__codelineno-3-14 href=#__codelineno-3-14></a>│ │ │ │ (alternierendes Sprungmuster) │
</span><span id=__span-3-15><a id=__codelineno-3-15 name=__codelineno-3-15 href=#__codelineno-3-15></a>└───────────┴──────────┴────────┴──────────────────────────────┘
</span><span id=__span-3-16><a id=__codelineno-3-16 name=__codelineno-3-16 href=#__codelineno-3-16></a> 847 Playlists insgesamt · 3 relevante Kandidaten (≥ 15 min)
</span><span id=__span-3-17><a id=__codelineno-3-17 name=__codelineno-3-17 href=#__codelineno-3-17></a> Empfehlung: 00800 (vorausgewählt)
</span><span id=__span-3-18><a id=__codelineno-3-18 name=__codelineno-3-18 href=#__codelineno-3-18></a> [Playlist übernehmen]
</span></code></pre></div> <ul> <li>Die empfohlene Playlist ist <strong>vorausgewählt</strong> (Checkbox)</li> <li>Score und Bewertungslabel helfen bei der Entscheidung</li> <li>Nach <strong>"Playlist übernehmen"</strong> setzt Ripster automatisch fort:</li> <li>mit vorhandenem RAW in <code>MEDIAINFO_CHECK</code></li> <li>ohne RAW über <code>READY_TO_START</code> weiter Richtung <code>RIPPING</code></li> </ul> <div class="admonition info"> <p class=admonition-title>Scoring-Details</p> <p>Wie die Scores berechnet werden, erklärt die <a href=../../pipeline/playlist-analysis/ >Playlist-Analyse</a>-Seite.</p> </div> <hr> <h2 id=schritt-6-ripping-ripping>Schritt 6 – Ripping → <code>RIPPING</code><a class=headerlink href=#schritt-6-ripping-ripping title="Permanent link">&para;</a></h2> <p><strong>Vorher prüft Ripster:</strong> Existiert bereits eine Raw-Datei für diesen Job?</p> <ul> <li><strong>Ja, Raw-Datei vorhanden</strong> → Direkt zu Schritt 7 (Track-Review), kein erneutes Ripping</li> <li><strong>Nein</strong> → MakeMKV-Ripping startet</li> </ul> <p>Im Standardfall startet Ripster diesen Schritt automatisch nach der Metadaten-Auswahl. Der Button <strong>"Job starten"</strong> ist hauptsächlich für Sonderfälle sichtbar (z. B. Fallback/Queue).</p> <p><strong>Was MakeMKV ausführt (MKV-Modus):</strong></p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a>makemkvcon<span class=w> </span>mkv<span class=w> </span>disc:0<span class=w> </span>all<span class=w> </span>/mnt/raw/Inception-2010/<span class=w> </span><span class=se>\</span>
</span><span id=__span-4-2><a id=__codelineno-4-2 name=__codelineno-4-2 href=#__codelineno-4-2></a><span class=w> </span>--minlength<span class=o>=</span><span class=m>900</span><span class=w> </span>-r
</span></code></pre></div> <p><strong>Was MakeMKV ausführt (Backup-Modus):</strong></p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a>makemkvcon<span class=w> </span>backup<span class=w> </span>disc:0<span class=w> </span>/mnt/raw/Inception-2010-backup/<span class=w> </span><span class=se>\</span>
</span><span id=__span-5-2><a id=__codelineno-5-2 name=__codelineno-5-2 href=#__codelineno-5-2></a><span class=w> </span>--decrypt<span class=w> </span>-r
</span></code></pre></div> <p><strong>Live-Fortschritt</strong> wird aus der MakeMKV-Ausgabe geparst:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-6-1><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a>PRGV:2048,0,65536 → Fortschritt wird berechnet und per WebSocket gesendet
</span><span id=__span-6-2><a id=__codelineno-6-2 name=__codelineno-6-2 href=#__codelineno-6-2></a>PRGT:5011,0,&quot;Sichern...&quot; → Aktueller Task-Name
</span></code></pre></div> <p><strong>Typische Dauer:</strong> - DVD: 20–45 Minuten - Blu-ray: 45–120 Minuten</p> <hr> <h2 id=schritt-7-track-review-ready_to_encode>Schritt 7 – Track-Review → <code>READY_TO_ENCODE</code><a class=headerlink href=#schritt-7-track-review-ready_to_encode title="Permanent link">&para;</a></h2> <p>Nach dem Ripping, nach Playlist-Übernahme oder direkt bei vorhandenem RAW startet der <strong>HandBrake-Scan</strong>:</p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-7-1><a id=__codelineno-7-1 name=__codelineno-7-1 href=#__codelineno-7-1></a>HandBrakeCLI<span class=w> </span>--scan<span class=w> </span>-i<span class=w> </span>&lt;quelle&gt;<span class=w> </span>-t<span class=w> </span><span class=m>0</span>
</span></code></pre></div> <p>Dieser Scan liest alle Tracks aus ohne zu encodieren. Ripster baut daraus den Encode-Plan mit automatischer Vorauswahl:</p> <p><strong>Status: <code>MEDIAINFO_CHECK</code></strong> – läuft automatisch, kein Benutzereingriff</p> <p>Danach öffnet sich das <strong>Encode-Review-Panel</strong> (<code>READY_TO_ENCODE</code>):</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-8-1><a id=__codelineno-8-1 name=__codelineno-8-1 href=#__codelineno-8-1></a>┌─────────────────────────────────────────────────────────────────┐
</span><span id=__span-8-2><a id=__codelineno-8-2 name=__codelineno-8-2 href=#__codelineno-8-2></a>│ Encode-Review │
</span><span id=__span-8-3><a id=__codelineno-8-3 name=__codelineno-8-3 href=#__codelineno-8-3></a>│ Titel: Disc Title 1 · Laufzeit: 2:28:05 · 28 Kapitel │
</span><span id=__span-8-4><a id=__codelineno-8-4 name=__codelineno-8-4 href=#__codelineno-8-4></a>├─────────────────────────────────────────────────────────────────┤
</span><span id=__span-8-5><a id=__codelineno-8-5 name=__codelineno-8-5 href=#__codelineno-8-5></a>│ Audio-Spuren │
</span><span id=__span-8-6><a id=__codelineno-8-6 name=__codelineno-8-6 href=#__codelineno-8-6></a>├──────┬─────────────────────────────┬───────────────────────────┤
</span><span id=__span-8-7><a id=__codelineno-8-7 name=__codelineno-8-7 href=#__codelineno-8-7></a>│ ☑ │ Track 1: English (AC3, 5.1) │ Copy (ac3) │
</span><span id=__span-8-8><a id=__codelineno-8-8 name=__codelineno-8-8 href=#__codelineno-8-8></a>│ ☑ │ Track 2: Deutsch (DTS, 5.1) │ Fallback Transcode (av_aac)│
</span><span id=__span-8-9><a id=__codelineno-8-9 name=__codelineno-8-9 href=#__codelineno-8-9></a>│ ☐ │ Track 3: Français (AC3, 2.0) │ Nicht übernommen │
</span><span id=__span-8-10><a id=__codelineno-8-10 name=__codelineno-8-10 href=#__codelineno-8-10></a>├──────┴─────────────────────────────┴───────────────────────────┤
</span><span id=__span-8-11><a id=__codelineno-8-11 name=__codelineno-8-11 href=#__codelineno-8-11></a>│ Untertitel-Spuren │
</span><span id=__span-8-12><a id=__codelineno-8-12 name=__codelineno-8-12 href=#__codelineno-8-12></a>├──────┬─────────────────────────────┬────────┬──────┬──────────┤
</span><span id=__span-8-13><a id=__codelineno-8-13 name=__codelineno-8-13 href=#__codelineno-8-13></a>│ ☑ │ Track 1: Deutsch │ Einbr.☐ │Forc.☐│Default☑ │
</span><span id=__span-8-14><a id=__codelineno-8-14 name=__codelineno-8-14 href=#__codelineno-8-14></a>│ ☐ │ Track 2: English │ Einbr.☐ │Forc.☐│Default☐ │
</span><span id=__span-8-15><a id=__codelineno-8-15 name=__codelineno-8-15 href=#__codelineno-8-15></a>├──────┴─────────────────────────────┴────────┴──────┴──────────┤
</span><span id=__span-8-16><a id=__codelineno-8-16 name=__codelineno-8-16 href=#__codelineno-8-16></a>│ [Encoding starten] │
</span><span id=__span-8-17><a id=__codelineno-8-17 name=__codelineno-8-17 href=#__codelineno-8-17></a>└─────────────────────────────────────────────────────────────────┘
</span></code></pre></div> <h3 id=audio-track-aktionen-verstehen>Audio-Track-Aktionen verstehen<a class=headerlink href=#audio-track-aktionen-verstehen title="Permanent link">&para;</a></h3> <table> <thead> <tr> <th>Symbol/Text</th> <th>Bedeutung</th> </tr> </thead> <tbody> <tr> <td><code>Copy (ac3)</code></td> <td>Track wird <strong>verlustfrei</strong> direkt übernommen</td> </tr> <tr> <td><code>Copy (truehd)</code></td> <td>TrueHD-Track wird direkt übernommen</td> </tr> <tr> <td><code>Transcode (av_aac)</code></td> <td>Track wird zu AAC umgewandelt</td> </tr> <tr> <td><code>Fallback Transcode (av_aac)</code></td> <td>Copy nicht möglich → automatisch zu AAC</td> </tr> <tr> <td><code>Preset-Default (HandBrake)</code></td> <td>HandBrake-Preset entscheidet</td> </tr> <tr> <td><code>Nicht übernommen</code></td> <td>Track ist nicht ausgewählt</td> </tr> </tbody> </table> <h3 id=untertitel-flags>Untertitel-Flags<a class=headerlink href=#untertitel-flags title="Permanent link">&para;</a></h3> <table> <thead> <tr> <th>Flag</th> <th>Bedeutung</th> </tr> </thead> <tbody> <tr> <td><strong>Einbrennen</strong></td> <td>Untertitel werden fest ins Video gebrannt (nur ein Track möglich)</td> </tr> <tr> <td><strong>Forced</strong></td> <td>Nur erzwungene Untertitel-Einblendungen übernehmen</td> </tr> <tr> <td><strong>Default</strong></td> <td>Diese Spur wird beim Abspielen automatisch aktiviert</td> </tr> </tbody> </table> <h3 id=vorauswahl-regeln>Vorauswahl-Regeln<a class=headerlink href=#vorauswahl-regeln title="Permanent link">&para;</a></h3> <p>Die Tracks mit <code>☑</code> wurden nach der Regel aus den Einstellungen automatisch vorausgewählt (<code>selectedByRule: true</code>). Die Auswahl kann frei geändert werden.</p> <p>Klicke <strong>"Encoding starten"</strong> (bzw. im Pre-Rip-Modus <strong>"Backup + Encoding starten"</strong>), um fortzufahren. Falls die Auswahl noch nicht bestätigt wurde, übernimmt das Frontend die Bestätigung automatisch beim Start.</p> <hr> <h2 id=schritt-8-encoding-encoding>Schritt 8 – Encoding → <code>ENCODING</code><a class=headerlink href=#schritt-8-encoding-encoding title="Permanent link">&para;</a></h2> <p>HandBrake startet mit dem finalisierten Plan:</p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-9-1><a id=__codelineno-9-1 name=__codelineno-9-1 href=#__codelineno-9-1></a>HandBrakeCLI<span class=w> </span><span class=se>\</span>
</span><span id=__span-9-2><a id=__codelineno-9-2 name=__codelineno-9-2 href=#__codelineno-9-2></a><span class=w> </span>-i<span class=w> </span>/dev/sr0<span class=w> </span><span class=se>\</span>
</span><span id=__span-9-3><a id=__codelineno-9-3 name=__codelineno-9-3 href=#__codelineno-9-3></a><span class=w> </span>-o<span class=w> </span><span class=s2>&quot;/mnt/movies/Inception (2010).mkv&quot;</span><span class=w> </span><span class=se>\</span>
</span><span id=__span-9-4><a id=__codelineno-9-4 name=__codelineno-9-4 href=#__codelineno-9-4></a><span class=w> </span>-t<span class=w> </span><span class=m>1</span><span class=w> </span><span class=se>\</span>
</span><span id=__span-9-5><a id=__codelineno-9-5 name=__codelineno-9-5 href=#__codelineno-9-5></a><span class=w> </span>--preset<span class=w> </span><span class=s2>&quot;H.265 MKV 1080p30&quot;</span><span class=w> </span><span class=se>\</span>
</span><span id=__span-9-6><a id=__codelineno-9-6 name=__codelineno-9-6 href=#__codelineno-9-6></a><span class=w> </span>-a<span class=w> </span><span class=m>1</span>,2<span class=w> </span><span class=se>\</span>
</span><span id=__span-9-7><a id=__codelineno-9-7 name=__codelineno-9-7 href=#__codelineno-9-7></a><span class=w> </span>-E<span class=w> </span>copy:ac3,av_aac<span class=w> </span><span class=se>\</span>
</span><span id=__span-9-8><a id=__codelineno-9-8 name=__codelineno-9-8 href=#__codelineno-9-8></a><span class=w> </span>-s<span class=w> </span><span class=m>1</span><span class=w> </span><span class=se>\</span>
</span><span id=__span-9-9><a id=__codelineno-9-9 name=__codelineno-9-9 href=#__codelineno-9-9></a><span class=w> </span>--subtitle-default<span class=w> </span><span class=m>1</span>
</span></code></pre></div> <p><strong>Live-Fortschritt</strong> wird aus HandBrake-stderr geparst:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-10-1><a id=__codelineno-10-1 name=__codelineno-10-1 href=#__codelineno-10-1></a>Encoding: task 1 of 1, 73.50 % (45.23 fps, avg 44.12 fps, ETA 00h12m34s)
</span></code></pre></div> <p>Das Dashboard zeigt: - Fortschrittsbalken (0–100 %) - Aktuelle Encoding-Geschwindigkeit (FPS) - Geschätzte Restzeit (ETA)</p> <p><strong>Typische Dauer (abhängig von CPU/GPU und Preset):</strong> - Schnelles Preset (<code>fast</code>): 0.5× Echtzeit - Standard-Preset: 1–3× Echtzeit - Langsames Preset (<code>slow</code>): 5–10× Echtzeit</p> <hr> <h2 id=schritt-9-fertig-finished>Schritt 9 – Fertig! → <code>FINISHED</code><a class=headerlink href=#schritt-9-fertig-finished title="Permanent link">&para;</a></h2> <div class="language-text highlight"><pre><span></span><code><span id=__span-11-1><a id=__codelineno-11-1 name=__codelineno-11-1 href=#__codelineno-11-1></a>/mnt/nas/movies/
</span><span id=__span-11-2><a id=__codelineno-11-2 name=__codelineno-11-2 href=#__codelineno-11-2></a>└── Inception (2010).mkv ✓ Encodierung abgeschlossen
</span></code></pre></div> <ul> <li>Job-Status in der Datenbank: <code>FINISHED</code></li> <li>PushOver-Benachrichtigung (falls konfiguriert)</li> <li>Eintrag in der <a href=http://localhost:5173/history>History</a> mit vollständigen Logs</li> </ul> <hr> <h2 id=fehlerbehandlung>Fehlerbehandlung<a class=headerlink href=#fehlerbehandlung title="Permanent link">&para;</a></h2> <h3 id=job-im-status-error>Job im Status <code>ERROR</code><a class=headerlink href=#job-im-status-error title="Permanent link">&para;</a></h3> <ol> <li><strong>Dashboard</strong>: Details-Button → Log-Ausgabe prüfen</li> <li><strong>Retry</strong>: Job vom Fehlerzustand neu starten (behält Metadaten)</li> <li><strong>History</strong>: Vollständige Logs und Fehlerdetails</li> </ol> <h3 id=haufige-fehlerursachen>Häufige Fehlerursachen<a class=headerlink href=#haufige-fehlerursachen title="Permanent link">&para;</a></h3> <table> <thead> <tr> <th>Fehler</th> <th>Ursache</th> <th>Lösung</th> </tr> </thead> <tbody> <tr> <td>MakeMKV: Lizenzfehler</td> <td>Abgelaufene Beta-Lizenz</td> <td>Neue Lizenz im <a href="https://www.makemkv.com/forum/viewtopic.php?t=1053">MakeMKV-Forum</a></td> </tr> <tr> <td>HandBrake: Preset nicht gefunden</td> <td>Preset-Name falsch</td> <td><code>HandBrakeCLI --preset-list</code> prüfen</td> </tr> <tr> <td>Keine Disc erkannt</td> <td>Laufwerk-Berechtigungen</td> <td><code>sudo chmod a+rw /dev/sr0</code></td> </tr> <tr> <td>Falsches Video (zerstückelt)</td> <td>Falsche Playlist</td> <td>Job re-encodieren mit anderer Playlist</td> </tr> <tr> <td>OMDb: Keine Ergebnisse</td> <td>API-Key fehlt oder Titel nicht gefunden</td> <td>Einstellungen prüfen; manuell eingeben</td> </tr> </tbody> </table> <hr> <h2 id=kurzubersicht-aller-schritte>Kurzübersicht aller Schritte<a class=headerlink href=#kurzubersicht-aller-schritte title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>#</th> <th>Status</th> <th>Benutzeraktion</th> <th>Was Ripster tut</th> </tr> </thead> <tbody> <tr> <td>1</td> <td><code>IDLE</code></td> <td>Disc einlegen</td> <td>Disc-Polling erkennt Disc</td> </tr> <tr> <td>2</td> <td><code>DISC_DETECTED</code></td> <td>"Analyse starten" klicken</td> <td>Job anlegen, OMDb vorsuchen</td> </tr> <tr> <td>3</td> <td><code>METADATA_SELECTION</code></td> <td>Film im Dialog auswählen</td> <td>Start automatisch einplanen/auslösen</td> </tr> <tr> <td>4</td> <td><code>READY_TO_START</code></td> <td>meist keine</td> <td>Übergangszustand vor Auto-Start</td> </tr> <tr> <td>5</td> <td><code>RIPPING</code></td> <td>Warten</td> <td>MakeMKV rippt, Fortschritt streamen</td> </tr> <tr> <td>6</td> <td><code>MEDIAINFO_CHECK</code></td> <td>Warten</td> <td>HandBrake-Scan, Encode-Plan bauen</td> </tr> <tr> <td>7</td> <td><code>WAITING_FOR_USER_DECISION</code> (optional)</td> <td>Playlist manuell wählen</td> <td>Auf Bestätigung warten</td> </tr> <tr> <td>8</td> <td><code>READY_TO_ENCODE</code></td> <td>Tracks prüfen + "Encoding starten"</td> <td>Auswahl übernehmen, Start auslösen</td> </tr> <tr> <td>9</td> <td><code>ENCODING</code></td> <td>Warten</td> <td>HandBrake encodiert, inkl. Post-Skripte</td> </tr> <tr> <td>10</td> <td><code>FINISHED</code></td> <td>—</td> <td>Datei fertig, Benachrichtigung senden</td> </tr> </tbody> </table> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6Line truncated
</span></code></pre></div> <p>Öffne <code>http://localhost:5173</code>.</p> <hr> <h2 id=2-disc-einlegen>2) Disc einlegen<a class=headerlink href=#2-disc-einlegen title="Permanent link">&para;</a></h2> <p>Pipeline wechselt auf <code>DISC_DETECTED</code>.</p> <p>Falls nötig manuell neu scannen:</p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a>curl<span class=w> </span>-X<span class=w> </span>POST<span class=w> </span>http://localhost:3001/api/pipeline/rescan-disc
</span></code></pre></div> <hr> <h2 id=3-analyse-starten>3) Analyse starten<a class=headerlink href=#3-analyse-starten title="Permanent link">&para;</a></h2> <p>Klicke im Dashboard auf <code>Analyse starten</code>.</p> <p>Intern:</p> <ul> <li>Job wird angelegt</li> <li>MakeMKV-Analyse läuft (<code>ANALYZING</code>)</li> <li>UI wechselt in Metadatenauswahl (<code>METADATA_SELECTION</code>)</li> </ul> <hr> <h2 id=4-metadaten-bestatigen>4) Metadaten bestätigen<a class=headerlink href=#4-metadaten-bestatigen title="Permanent link">&para;</a></h2> <p>Im Dialog:</p> <ul> <li>OMDb-Ergebnis wählen oder manuell eintragen</li> <li>bei Playlist-Abfrage ggf. <code>selectedPlaylist</code> wählen</li> </ul> <p>Nach Bestätigung startet Ripster automatisch weiter.</p> <hr> <h2 id=5-pipeline-pfade>5) Pipeline-Pfade<a class=headerlink href=#5-pipeline-pfade title="Permanent link">&para;</a></h2> <p>Abhängig von Job/RAW-Situation:</p> <ul> <li><strong>kein RAW vorhanden</strong> -&gt; <code>RIPPING</code></li> <li><strong>RAW vorhanden</strong> -&gt; <code>MEDIAINFO_CHECK</code></li> <li><strong>mehrdeutige Playlist</strong> -&gt; <code>WAITING_FOR_USER_DECISION</code></li> </ul> <p>Wenn Parallel-Limit erreicht ist, wird der Job in die Queue eingereiht.</p> <hr> <h2 id=6-review-ready_to_encode>6) Review (<code>READY_TO_ENCODE</code>)<a class=headerlink href=#6-review-ready_to_encode title="Permanent link">&para;</a></h2> <p>Im Review-Panel:</p> <ul> <li>Titel auswählen (falls mehrere)</li> <li>Audio-/Subtitle-Tracks auswählen</li> <li>optional User-Preset anwenden</li> <li>optional Pre-/Post-Skripte und Ketten hinzufügen</li> </ul> <p>Mit <code>Encoding starten</code> wird <code>confirm-encode</code> + Start ausgelöst.</p> <hr> <h2 id=7-encoding-encoding>7) Encoding (<code>ENCODING</code>)<a class=headerlink href=#7-encoding-encoding title="Permanent link">&para;</a></h2> <p>Während Encoding:</p> <ul> <li>Live-Fortschritt/ETA über WebSocket</li> <li>Pre-Encode-Ausführungen laufen vor HandBrake</li> <li>Post-Encode-Ausführungen laufen nach HandBrake</li> </ul> <p>Wichtig:</p> <ul> <li>Pre-Encode-Fehler -&gt; Job endet in <code>ERROR</code></li> <li>Post-Encode-Fehler -&gt; Job kann <code>FINISHED</code> bleiben, aber mit Fehlerhinweis im Status/Log</li> </ul> <hr> <h2 id=8-abschluss-finished>8) Abschluss (<code>FINISHED</code>)<a class=headerlink href=#8-abschluss-finished title="Permanent link">&para;</a></h2> <p>Ergebnis:</p> <ul> <li>Ausgabe in <code>movie_dir</code> (ggf. profilspezifisch)</li> <li>Job in Historie sichtbar</li> <li>Logs im konfigurierten <code>log_dir</code></li> </ul> <hr> <h2 id=nutzliche-api-shortcuts>Nützliche API-Shortcuts<a class=headerlink href=#nutzliche-api-shortcuts title="Permanent link">&para;</a></h2> <div class="language-bash highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a><span class=c1># Pipeline-Snapshot</span>
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a>curl<span class=w> </span>http://localhost:3001/api/pipeline/state
</span><span id=__span-2-3><a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a>
</span><span id=__span-2-4><a id=__codelineno-2-4 name=__codelineno-2-4 href=#__codelineno-2-4></a><span class=c1># Queue-Snapshot</span>
</span><span id=__span-2-5><a id=__codelineno-2-5 name=__codelineno-2-5 href=#__codelineno-2-5></a>curl<span class=w> </span>http://localhost:3001/api/pipeline/queue
</span><span id=__span-2-6><a id=__codelineno-2-6 name=__codelineno-2-6 href=#__codelineno-2-6></a>
</span><span id=__span-2-7><a id=__codelineno-2-7 name=__codelineno-2-7 href=#__codelineno-2-7></a><span class=c1># Jobs</span>
</span><span id=__span-2-8><a id=__codelineno-2-8 name=__codelineno-2-8 href=#__codelineno-2-8></a>curl<span class=w> </span>http://localhost:3001/api/history
</span></code></pre></div> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
+1 -1
View File
@@ -1,4 +1,4 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/ rel=canonical><link href=getting-started/ rel=next><link rel=icon href=assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Ripster</title><link rel=stylesheet href=assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=stylesheets/extra.css><script>__md_scope=new URL(".",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#ripster class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Home </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5Line truncated
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/ rel=canonical><link href=getting-started/ rel=next><link rel=icon href=assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Ripster</title><link rel=stylesheet href=assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=stylesheets/extra.css><script>__md_scope=new URL(".",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#ripster class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Home </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5Line truncated
</span></code></pre></div> <h3 id=kernfunktionen>Kernfunktionen<a class=headerlink href=#kernfunktionen title="Permanent link">&para;</a></h3> <table> <thead> <tr> <th>Feature</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><strong>Echtzeit-Updates</strong></td> <td>WebSocket-basierte Live-Statusanzeige ohne Reload</td> </tr> <tr> <td><strong>Intelligente Playlist-Analyse</strong></td> <td>Erkennt Blu-ray Playlist-Verschleierung (Fake-Playlists)</td> </tr> <tr> <td><strong>Track-Auswahl</strong></td> <td>Individuelle Auswahl von Audio- und Untertitelspuren</td> </tr> <tr> <td><strong>Orphan-Recovery</strong></td> <td>Import von bereits gerippten Dateien als Jobs</td> </tr> <tr> <td><strong>PushOver-Benachrichtigungen</strong></td> <td>Mobile Alerts bei Fertigstellung oder Fehlern</td> </tr> <tr> <td><strong>DB-Korruptions-Recovery</strong></td> <td>Automatische Quarantäne bei korrupten SQLite-Dateien</td> </tr> <tr> <td><strong>Re-Encoding</strong></td> <td>Erneutes Encodieren ohne neu rippen</td> </tr> </tbody> </table> <hr> <h2 id=technologie-stack>Technologie-Stack<a class=headerlink href=#technologie-stack title="Permanent link">&para;</a></h2> <div class="tabbed-set tabbed-alternate" data-tabs=1:3><input checked=checked id=__tabbed_1_1 name=__tabbed_1 type=radio><input id=__tabbed_1_2 name=__tabbed_1 type=radio><input id=__tabbed_1_3 name=__tabbed_1 type=radio><div class=tabbed-labels><label for=__tabbed_1_1>Backend</label><label for=__tabbed_1_2>Frontend</label><label for=__tabbed_1_3>Externe Tools</label></div> <div class=tabbed-content> <div class=tabbed-block> <ul> <li><strong>Node.js</strong> &gt;= 20.19.0 mit Express.js</li> <li><strong>SQLite3</strong> mit automatischen Schema-Migrationen</li> <li><strong>WebSocket</strong> (<code>ws</code>) für Echtzeit-Kommunikation</li> <li>Externe CLI-Tools: <code>makemkvcon</code>, <code>HandBrakeCLI</code>, <code>mediainfo</code></li> </ul> </div> <div class=tabbed-block> <ul> <li><strong>React</strong> 18.3.1 mit React Router</li> <li><strong>Vite</strong> 5.4.12 als Build-Tool</li> <li><strong>PrimeReact</strong> 10.9.2 als UI-Bibliothek</li> <li>WebSocket-Client für Live-Updates</li> </ul> </div> <div class=tabbed-block> <table> <thead> <tr> <th>Tool</th> <th>Zweck</th> </tr> </thead> <tbody> <tr> <td><code>makemkvcon</code></td> <td>Disc-Analyse &amp; MKV/Backup-Ripping</td> </tr> <tr> <td><code>HandBrakeCLI</code></td> <td>Video-Encoding</td> </tr> <tr> <td><code>mediainfo</code></td> <td>Track-Informationen aus gerippten Dateien</td> </tr> <tr> <td>OMDb API</td> <td>Filmmetadaten (Titel, Poster, IMDb-ID)</td> </tr> </tbody> </table> </div> </div> </div> <hr> <h2 id=schnellstart>Schnellstart<a class=headerlink href=#schnellstart title="Permanent link">&para;</a></h2> <div class="language-bash highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a><span class=c1># 1. Repository klonen</span>
</span><span id=__span-1-2><a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a>git<span class=w> </span>clone<span class=w> </span>https://github.com/YOUR_GITHUB_USERNAME/ripster.git
</span><span id=__span-1-3><a id=__codelineno-1-3 name=__codelineno-1-3 href=#__codelineno-1-3></a><span class=nb>cd</span><span class=w> </span>ripster
+29 -139
View File
@@ -1,139 +1,29 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/pipeline/encoding/ rel=canonical><link href=../workflow/ rel=prev><link href=../playlist-analysis/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Encode-Planung & Track-Auswahl - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#encode-planung-track-auswahl class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Encode-Planung & Track-Auswahl </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svLine truncated
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a> ↓
</span><span id=__span-0-3><a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a>HandBrake --scan (alle Titel &amp; Tracks einlesen)
</span><span id=__span-0-4><a id=__codelineno-0-4 name=__codelineno-0-4 href=#__codelineno-0-4></a> ↓
</span><span id=__span-0-5><a id=__codelineno-0-5 name=__codelineno-0-5 href=#__codelineno-0-5></a>buildTrackSelectors() ← Regeln aus Einstellungen ableiten
</span><span id=__span-0-6><a id=__codelineno-0-6 name=__codelineno-0-6 href=#__codelineno-0-6></a> ↓
</span><span id=__span-0-7><a id=__codelineno-0-7 name=__codelineno-0-7 href=#__codelineno-0-7></a>selectTrackIds() ← Tracks anhand Regeln vorauswählen
</span><span id=__span-0-8><a id=__codelineno-0-8 name=__codelineno-0-8 href=#__codelineno-0-8></a> ↓
</span><span id=__span-0-9><a id=__codelineno-0-9 name=__codelineno-0-9 href=#__codelineno-0-9></a>resolveAudioEncoderAction() ← Encoder-Aktion pro Track bestimmen
</span><span id=__span-0-10><a id=__codelineno-0-10 name=__codelineno-0-10 href=#__codelineno-0-10></a> ↓
</span><span id=__span-0-11><a id=__codelineno-0-11 name=__codelineno-0-11 href=#__codelineno-0-11></a>buildDiscScanReview() ← Vollständigen Encode-Plan erstellen
</span><span id=__span-0-12><a id=__codelineno-0-12 name=__codelineno-0-12 href=#__codelineno-0-12></a> ↓
</span><span id=__span-0-13><a id=__codelineno-0-13 name=__codelineno-0-13 href=#__codelineno-0-13></a>READY_TO_ENCODE ← Benutzer-Review im Frontend
</span><span id=__span-0-14><a id=__codelineno-0-14 name=__codelineno-0-14 href=#__codelineno-0-14></a> ↓
</span><span id=__span-0-15><a id=__codelineno-0-15 name=__codelineno-0-15 href=#__codelineno-0-15></a>applyManualTrackSelectionToPlan() ← Benutzer-Auswahl anwenden
</span><span id=__span-0-16><a id=__codelineno-0-16 name=__codelineno-0-16 href=#__codelineno-0-16></a> ↓
</span><span id=__span-0-17><a id=__codelineno-0-17 name=__codelineno-0-17 href=#__codelineno-0-17></a>ENCODING ← HandBrake-CLI mit finalem Plan starten
</span></code></pre></div> <hr> <h2 id=phase-1-pre-rip-track-scan>Phase 1: Pre-Rip Track-Scan<a class=headerlink href=#phase-1-pre-rip-track-scan title="Permanent link">&para;</a></h2> <p>Ripster führt einen <strong>HandBrake-Scan</strong> bereits <strong>vor dem eigentlichen Ripping</strong> durch:</p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a>HandBrakeCLI<span class=w> </span>--scan<span class=w> </span>-i<span class=w> </span>/dev/sr0<span class=w> </span>-t<span class=w> </span><span class=m>0</span>
</span></code></pre></div> <p>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.</p> <div class="admonition info"> <p class=admonition-title>Pre-Rip vs. Post-Rip</p> <p>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 <code>.mkv</code>-Datei gescannt.</p> </div> <hr> <h2 id=phase-2-track-selektor-regeln-buildtrackselectors>Phase 2: Track-Selektor-Regeln (<code>buildTrackSelectors</code>)<a class=headerlink href=#phase-2-track-selektor-regeln-buildtrackselectors title="Permanent link">&para;</a></h2> <p>Die Regeln werden aus den HandBrake-Einstellungen abgeleitet. Es gibt fünf <strong>Selektionsmodi</strong>:</p> <table> <thead> <tr> <th>Modus</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>none</code></td> <td>Keine Tracks dieser Art übernehmen</td> </tr> <tr> <td><code>first</code></td> <td>Nur den ersten Track übernehmen</td> </tr> <tr> <td><code>all</code></td> <td>Alle Tracks übernehmen</td> </tr> <tr> <td><code>language</code></td> <td>Nur Tracks in bestimmten Sprachen</td> </tr> <tr> <td><code>explicit</code></td> <td>Bestimmte Track-IDs explizit angeben</td> </tr> </tbody> </table> <p>Der aktive Modus wird aus den <code>handbrake_*</code>-Einstellungen und <code>handbrake_extra_args</code> abgeleitet. Explizite CLI-Argumente (<code>--audio</code>, <code>--audio-lang-list</code>) überschreiben die Basis-Konfiguration.</p> <hr> <h2 id=phase-3-automatische-vorauswahl-selecttrackids>Phase 3: Automatische Vorauswahl (<code>selectTrackIds</code>)<a class=headerlink href=#phase-3-automatische-vorauswahl-selecttrackids title="Permanent link">&para;</a></h2> <h3 id=audio-tracks>Audio-Tracks<a class=headerlink href=#audio-tracks title="Permanent link">&para;</a></h3> <div class="language-text highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a>Modus &#39;none&#39; → Keine Audio-Tracks
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a>Modus &#39;all&#39; → Alle Tracks (oder nur erster, wenn firstOnly)
</span><span id=__span-2-3><a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a>Modus &#39;language&#39; → Alle Tracks in den konfigurierten Sprachen
</span><span id=__span-2-4><a id=__codelineno-2-4 name=__codelineno-2-4 href=#__codelineno-2-4></a>Modus &#39;explicit&#39; → Nur die angegebenen Track-IDs
</span><span id=__span-2-5><a id=__codelineno-2-5 name=__codelineno-2-5 href=#__codelineno-2-5></a>Modus &#39;first&#39; → Nur Track 1
</span></code></pre></div> <p>Jeder Audio-Track erhält das Feld <code>selectedByRule: true/false</code> – dieses zeigt dem Benutzer, welche Tracks automatisch vorausgewählt wurden.</p> <p><strong>Sprach-Normalisierung (<code>normalizeLanguage</code>):</strong></p> <p>Alle Sprachcodes werden auf <strong>ISO 639-2</strong> (3-Buchstaben) normalisiert:</p> <table> <thead> <tr> <th>Eingabe</th> <th>Normalisiert</th> </tr> </thead> <tbody> <tr> <td><code>de</code>, <code>ger</code></td> <td><code>deu</code></td> </tr> <tr> <td><code>German</code></td> <td><code>deu</code></td> </tr> <tr> <td><code>en</code>, <code>eng</code></td> <td><code>eng</code></td> </tr> <tr> <td><code>English</code></td> <td><code>eng</code></td> </tr> <tr> <td><code>fr</code>, <code>fre</code></td> <td><code>fra</code></td> </tr> <tr> <td><code>ja</code>, <code>jpn</code></td> <td><code>jpn</code></td> </tr> <tr> <td>Unbekannt</td> <td><code>und</code></td> </tr> </tbody> </table> <h3 id=untertitel-tracks>Untertitel-Tracks<a class=headerlink href=#untertitel-tracks title="Permanent link">&para;</a></h3> <p>Gleiche Modus-Logik wie Audio, aber mit <strong>zusätzlichen Flags</strong> pro Track:</p> <table> <thead> <tr> <th>Flag</th> <th>Bedeutung</th> </tr> </thead> <tbody> <tr> <td><code>burnIn</code></td> <td>Untertitel in Video einbrennen (<code>--subtitle-burned</code>)</td> </tr> <tr> <td><code>forced</code></td> <td>Nur erzwungene Untertitel übernehmen (<code>--subtitle-forced</code>)</td> </tr> <tr> <td><code>defaultTrack</code></td> <td>Als Standard-Untertitelspur markieren (<code>--subtitle-default</code>)</td> </tr> </tbody> </table> <p>Diese Flags werden im Encode-Review als Checkboxen angezeigt.</p> <hr> <h2 id=phase-4-encoder-aktion-bestimmen-resolveaudioencoderaction>Phase 4: Encoder-Aktion bestimmen (<code>resolveAudioEncoderAction</code>)<a class=headerlink href=#phase-4-encoder-aktion-bestimmen-resolveaudioencoderaction title="Permanent link">&para;</a></h2> <p>Für jeden vorausgewählten Audio-Track bestimmt Ripster die Encoder-Aktion:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a>Encoder-Einstellung Codec-Support in Copy-Mask? Aktion
</span><span id=__span-3-2><a id=__codelineno-3-2 name=__codelineno-3-2 href=#__codelineno-3-2></a>─────────────────────────────────────────────────────────────────────
</span><span id=__span-3-3><a id=__codelineno-3-3 name=__codelineno-3-3 href=#__codelineno-3-3></a>Kein Encoder / &#39;preset-default&#39; → preset-default HandBrake-Preset entscheidet
</span><span id=__span-3-4><a id=__codelineno-3-4 name=__codelineno-3-4 href=#__codelineno-3-4></a>encoder.startsWith(&#39;copy&#39;)
</span><span id=__span-3-5><a id=__codelineno-3-5 name=__codelineno-3-5 href=#__codelineno-3-5></a> UND Codec in audioCopyMask → copy Direktkopie (verlustfrei)
</span><span id=__span-3-6><a id=__codelineno-3-6 name=__codelineno-3-6 href=#__codelineno-3-6></a> UND Codec NICHT in audioCopyMask→ fallback Transcode mit Fallback-Encoder
</span><span id=__span-3-7><a id=__codelineno-3-7 name=__codelineno-3-7 href=#__codelineno-3-7></a>sonstiger Encoder → transcode Transcode mit explizitem Encoder
</span></code></pre></div> <p><strong>Encoder-Aktionstypen:</strong></p> <table> <thead> <tr> <th>Typ</th> <th>Label (UI)</th> <th>Qualität</th> </tr> </thead> <tbody> <tr> <td><code>preset-default</code></td> <td><code>Preset-Default (HandBrake)</code></td> <td>HandBrake entscheidet</td> </tr> <tr> <td><code>copy</code></td> <td><code>Copy (ac3)</code></td> <td>Verlustfrei</td> </tr> <tr> <td><code>fallback</code></td> <td><code>Fallback Transcode (av_aac)</code></td> <td>Mit Qualitätsverlust</td> </tr> <tr> <td><code>transcode</code></td> <td><code>Transcode (av_aac)</code></td> <td>Mit Qualitätsverlust</td> </tr> </tbody> </table> <p><strong>Copy-kompatible Codecs (Standard Copy-Mask):</strong></p> <table> <thead> <tr> <th>Codec</th> <th>Encoder-String</th> </tr> </thead> <tbody> <tr> <td>AC-3</td> <td><code>copy:ac3</code></td> </tr> <tr> <td>E-AC-3</td> <td><code>copy:eac3</code></td> </tr> <tr> <td>AAC</td> <td><code>copy:aac</code></td> </tr> <tr> <td>MP3</td> <td><code>copy:mp3</code></td> </tr> <tr> <td>TrueHD</td> <td><code>copy:truehd</code></td> </tr> <tr> <td>DTS</td> <td><code>copy:dts</code> <em>(nur mit spez. HandBrake-Build)</em></td> </tr> <tr> <td>DTS-HD</td> <td><code>copy:dtshd</code> <em>(nur mit spez. HandBrake-Build)</em></td> </tr> </tbody> </table> <div class="admonition warning"> <p class=admonition-title>DTS im Standard-HandBrake</p> <p>Standard-HandBrake-Builds unterstützen kein DTS-Passthrough. DTS-Tracks werden dann automatisch auf den Fallback-Encoder umgestellt (Standard: <code>av_aac</code>).</p> </div> <hr> <h2 id=phase-5-encode-plan-struktur>Phase 5: Encode-Plan-Struktur<a class=headerlink href=#phase-5-encode-plan-struktur title="Permanent link">&para;</a></h2> <p>Der vollständige Plan wird im Job-Datensatz als <code>encode_plan_json</code> gespeichert:</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a><span class=p>{</span>
</span><span id=__span-4-2><a id=__codelineno-4-2 name=__codelineno-4-2 href=#__codelineno-4-2></a><span class=w> </span><span class=nt>&quot;mode&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;pre_rip&quot;</span><span class=p>,</span>
</span><span id=__span-4-3><a id=__codelineno-4-3 name=__codelineno-4-3 href=#__codelineno-4-3></a><span class=w> </span><span class=nt>&quot;preRip&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-4-4><a id=__codelineno-4-4 name=__codelineno-4-4 href=#__codelineno-4-4></a><span class=w> </span><span class=nt>&quot;encodeInputTitleId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-4-5><a id=__codelineno-4-5 name=__codelineno-4-5 href=#__codelineno-4-5></a><span class=w> </span><span class=nt>&quot;encodeInputPath&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;disc-track-scan://title-1&quot;</span><span class=p>,</span>
</span><span id=__span-4-6><a id=__codelineno-4-6 name=__codelineno-4-6 href=#__codelineno-4-6></a><span class=w> </span><span class=nt>&quot;selectors&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-4-7><a id=__codelineno-4-7 name=__codelineno-4-7 href=#__codelineno-4-7></a><span class=w> </span><span class=nt>&quot;audio&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;mode&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;language&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;languages&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=s2>&quot;deu&quot;</span><span class=p>,</span><span class=w> </span><span class=s2>&quot;eng&quot;</span><span class=p>],</span><span class=w> </span><span class=nt>&quot;copyMask&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=s2>&quot;copy:ac3&quot;</span><span class=p>,</span><span class=w> </span><span class=s2>&quot;copy:eac3&quot;</span><span class=p>]</span><span class=w> </span><span class=p>},</span>
</span><span id=__span-4-8><a id=__codelineno-4-8 name=__codelineno-4-8 href=#__codelineno-4-8></a><span class=w> </span><span class=nt>&quot;subtitle&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;mode&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;none&quot;</span><span class=w> </span><span class=p>}</span>
</span><span id=__span-4-9><a id=__codelineno-4-9 name=__codelineno-4-9 href=#__codelineno-4-9></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-4-10><a id=__codelineno-4-10 name=__codelineno-4-10 href=#__codelineno-4-10></a><span class=w> </span><span class=nt>&quot;titles&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-4-11><a id=__codelineno-4-11 name=__codelineno-4-11 href=#__codelineno-4-11></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-4-12><a id=__codelineno-4-12 name=__codelineno-4-12 href=#__codelineno-4-12></a><span class=w> </span><span class=nt>&quot;id&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-4-13><a id=__codelineno-4-13 name=__codelineno-4-13 href=#__codelineno-4-13></a><span class=w> </span><span class=nt>&quot;fileName&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Disc Title 1&quot;</span><span class=p>,</span>
</span><span id=__span-4-14><a id=__codelineno-4-14 name=__codelineno-4-14 href=#__codelineno-4-14></a><span class=w> </span><span class=nt>&quot;durationSeconds&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>8885</span><span class=p>,</span>
</span><span id=__span-4-15><a id=__codelineno-4-15 name=__codelineno-4-15 href=#__codelineno-4-15></a><span class=w> </span><span class=nt>&quot;selectedByMinLength&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-4-16><a id=__codelineno-4-16 name=__codelineno-4-16 href=#__codelineno-4-16></a><span class=w> </span><span class=nt>&quot;isEncodeInput&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-4-17><a id=__codelineno-4-17 name=__codelineno-4-17 href=#__codelineno-4-17></a><span class=w> </span><span class=nt>&quot;audioTracks&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-4-18><a id=__codelineno-4-18 name=__codelineno-4-18 href=#__codelineno-4-18></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-4-19><a id=__codelineno-4-19 name=__codelineno-4-19 href=#__codelineno-4-19></a><span class=w> </span><span class=nt>&quot;id&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-4-20><a id=__codelineno-4-20 name=__codelineno-4-20 href=#__codelineno-4-20></a><span class=w> </span><span class=nt>&quot;sourceTrackId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-4-21><a id=__codelineno-4-21 name=__codelineno-4-21 href=#__codelineno-4-21></a><span class=w> </span><span class=nt>&quot;language&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;eng&quot;</span><span class=p>,</span>
</span><span id=__span-4-22><a id=__codelineno-4-22 name=__codelineno-4-22 href=#__codelineno-4-22></a><span class=w> </span><span class=nt>&quot;languageLabel&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;English&quot;</span><span class=p>,</span>
</span><span id=__span-4-23><a id=__codelineno-4-23 name=__codelineno-4-23 href=#__codelineno-4-23></a><span class=w> </span><span class=nt>&quot;title&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;5.1 Surround&quot;</span><span class=p>,</span>
</span><span id=__span-4-24><a id=__codelineno-4-24 name=__codelineno-4-24 href=#__codelineno-4-24></a><span class=w> </span><span class=nt>&quot;format&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;AC3&quot;</span><span class=p>,</span>
</span><span id=__span-4-25><a id=__codelineno-4-25 name=__codelineno-4-25 href=#__codelineno-4-25></a><span class=w> </span><span class=nt>&quot;codecToken&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;ac3&quot;</span><span class=p>,</span>
</span><span id=__span-4-26><a id=__codelineno-4-26 name=__codelineno-4-26 href=#__codelineno-4-26></a><span class=w> </span><span class=nt>&quot;channels&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;6&quot;</span><span class=p>,</span>
</span><span id=__span-4-27><a id=__codelineno-4-27 name=__codelineno-4-27 href=#__codelineno-4-27></a><span class=w> </span><span class=nt>&quot;selectedByRule&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-4-28><a id=__codelineno-4-28 name=__codelineno-4-28 href=#__codelineno-4-28></a><span class=w> </span><span class=nt>&quot;selectedForEncode&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-4-29><a id=__codelineno-4-29 name=__codelineno-4-29 href=#__codelineno-4-29></a><span class=w> </span><span class=nt>&quot;encodePreviewActions&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-4-30><a id=__codelineno-4-30 name=__codelineno-4-30 href=#__codelineno-4-30></a><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;copy&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;encoder&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;copy:ac3&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;label&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Copy (ac3)&quot;</span><span class=w> </span><span class=p>}</span>
</span><span id=__span-4-31><a id=__codelineno-4-31 name=__codelineno-4-31 href=#__codelineno-4-31></a><span class=w> </span><span class=p>],</span>
</span><span id=__span-4-32><a id=__codelineno-4-32 name=__codelineno-4-32 href=#__codelineno-4-32></a><span class=w> </span><span class=nt>&quot;encodePreviewSummary&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Copy (ac3)&quot;</span>
</span><span id=__span-4-33><a id=__codelineno-4-33 name=__codelineno-4-33 href=#__codelineno-4-33></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-4-34><a id=__codelineno-4-34 name=__codelineno-4-34 href=#__codelineno-4-34></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-4-35><a id=__codelineno-4-35 name=__codelineno-4-35 href=#__codelineno-4-35></a><span class=w> </span><span class=nt>&quot;id&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>2</span><span class=p>,</span>
</span><span id=__span-4-36><a id=__codelineno-4-36 name=__codelineno-4-36 href=#__codelineno-4-36></a><span class=w> </span><span class=nt>&quot;sourceTrackId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>2</span><span class=p>,</span>
</span><span id=__span-4-37><a id=__codelineno-4-37 name=__codelineno-4-37 href=#__codelineno-4-37></a><span class=w> </span><span class=nt>&quot;language&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;deu&quot;</span><span class=p>,</span>
</span><span id=__span-4-38><a id=__codelineno-4-38 name=__codelineno-4-38 href=#__codelineno-4-38></a><span class=w> </span><span class=nt>&quot;languageLabel&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Deutsch&quot;</span><span class=p>,</span>
</span><span id=__span-4-39><a id=__codelineno-4-39 name=__codelineno-4-39 href=#__codelineno-4-39></a><span class=w> </span><span class=nt>&quot;format&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;DTS&quot;</span><span class=p>,</span>
</span><span id=__span-4-40><a id=__codelineno-4-40 name=__codelineno-4-40 href=#__codelineno-4-40></a><span class=w> </span><span class=nt>&quot;codecToken&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;dts&quot;</span><span class=p>,</span>
</span><span id=__span-4-41><a id=__codelineno-4-41 name=__codelineno-4-41 href=#__codelineno-4-41></a><span class=w> </span><span class=nt>&quot;channels&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;6&quot;</span><span class=p>,</span>
</span><span id=__span-4-42><a id=__codelineno-4-42 name=__codelineno-4-42 href=#__codelineno-4-42></a><span class=w> </span><span class=nt>&quot;selectedByRule&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-4-43><a id=__codelineno-4-43 name=__codelineno-4-43 href=#__codelineno-4-43></a><span class=w> </span><span class=nt>&quot;selectedForEncode&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-4-44><a id=__codelineno-4-44 name=__codelineno-4-44 href=#__codelineno-4-44></a><span class=w> </span><span class=nt>&quot;encodePreviewActions&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-4-45><a id=__codelineno-4-45 name=__codelineno-4-45 href=#__codelineno-4-45></a><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;fallback&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;encoder&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;av_aac&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;label&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Fallback Transcode (av_aac)&quot;</span><span class=w> </span><span class=p>}</span>
</span><span id=__span-4-46><a id=__codelineno-4-46 name=__codelineno-4-46 href=#__codelineno-4-46></a><span class=w> </span><span class=p>],</span>
</span><span id=__span-4-47><a id=__codelineno-4-47 name=__codelineno-4-47 href=#__codelineno-4-47></a><span class=w> </span><span class=nt>&quot;encodePreviewSummary&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Fallback Transcode (av_aac)&quot;</span>
</span><span id=__span-4-48><a id=__codelineno-4-48 name=__codelineno-4-48 href=#__codelineno-4-48></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-4-49><a id=__codelineno-4-49 name=__codelineno-4-49 href=#__codelineno-4-49></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-4-50><a id=__codelineno-4-50 name=__codelineno-4-50 href=#__codelineno-4-50></a><span class=w> </span><span class=nt>&quot;id&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>3</span><span class=p>,</span>
</span><span id=__span-4-51><a id=__codelineno-4-51 name=__codelineno-4-51 href=#__codelineno-4-51></a><span class=w> </span><span class=nt>&quot;language&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;fra&quot;</span><span class=p>,</span>
</span><span id=__span-4-52><a id=__codelineno-4-52 name=__codelineno-4-52 href=#__codelineno-4-52></a><span class=w> </span><span class=nt>&quot;languageLabel&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Français&quot;</span><span class=p>,</span>
</span><span id=__span-4-53><a id=__codelineno-4-53 name=__codelineno-4-53 href=#__codelineno-4-53></a><span class=w> </span><span class=nt>&quot;selectedByRule&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>false</span><span class=p>,</span>
</span><span id=__span-4-54><a id=__codelineno-4-54 name=__codelineno-4-54 href=#__codelineno-4-54></a><span class=w> </span><span class=nt>&quot;selectedForEncode&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>false</span><span class=p>,</span>
</span><span id=__span-4-55><a id=__codelineno-4-55 name=__codelineno-4-55 href=#__codelineno-4-55></a><span class=w> </span><span class=nt>&quot;encodePreviewSummary&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Nicht übernommen&quot;</span>
</span><span id=__span-4-56><a id=__codelineno-4-56 name=__codelineno-4-56 href=#__codelineno-4-56></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-4-57><a id=__codelineno-4-57 name=__codelineno-4-57 href=#__codelineno-4-57></a><span class=w> </span><span class=p>],</span>
</span><span id=__span-4-58><a id=__codelineno-4-58 name=__codelineno-4-58 href=#__codelineno-4-58></a><span class=w> </span><span class=nt>&quot;subtitleTracks&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-4-59><a id=__codelineno-4-59 name=__codelineno-4-59 href=#__codelineno-4-59></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-4-60><a id=__codelineno-4-60 name=__codelineno-4-60 href=#__codelineno-4-60></a><span class=w> </span><span class=nt>&quot;id&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-4-61><a id=__codelineno-4-61 name=__codelineno-4-61 href=#__codelineno-4-61></a><span class=w> </span><span class=nt>&quot;language&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;deu&quot;</span><span class=p>,</span>
</span><span id=__span-4-62><a id=__codelineno-4-62 name=__codelineno-4-62 href=#__codelineno-4-62></a><span class=w> </span><span class=nt>&quot;selectedByRule&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-4-63><a id=__codelineno-4-63 name=__codelineno-4-63 href=#__codelineno-4-63></a><span class=w> </span><span class=nt>&quot;selectedForEncode&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-4-64><a id=__codelineno-4-64 name=__codelineno-4-64 href=#__codelineno-4-64></a><span class=w> </span><span class=nt>&quot;burnIn&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>false</span><span class=p>,</span>
</span><span id=__span-4-65><a id=__codelineno-4-65 name=__codelineno-4-65 href=#__codelineno-4-65></a><span class=w> </span><span class=nt>&quot;forced&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>false</span><span class=p>,</span>
</span><span id=__span-4-66><a id=__codelineno-4-66 name=__codelineno-4-66 href=#__codelineno-4-66></a><span class=w> </span><span class=nt>&quot;defaultTrack&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-4-67><a id=__codelineno-4-67 name=__codelineno-4-67 href=#__codelineno-4-67></a><span class=w> </span><span class=nt>&quot;subtitlePreviewSummary&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Übernehmen&quot;</span><span class=p>,</span>
</span><span id=__span-4-68><a id=__codelineno-4-68 name=__codelineno-4-68 href=#__codelineno-4-68></a><span class=w> </span><span class=nt>&quot;subtitlePreviewFlags&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=s2>&quot;default&quot;</span><span class=p>]</span>
</span><span id=__span-4-69><a id=__codelineno-4-69 name=__codelineno-4-69 href=#__codelineno-4-69></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-4-70><a id=__codelineno-4-70 name=__codelineno-4-70 href=#__codelineno-4-70></a><span class=w> </span><span class=p>]</span>
</span><span id=__span-4-71><a id=__codelineno-4-71 name=__codelineno-4-71 href=#__codelineno-4-71></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-4-72><a id=__codelineno-4-72 name=__codelineno-4-72 href=#__codelineno-4-72></a><span class=w> </span><span class=p>]</span>
</span><span id=__span-4-73><a id=__codelineno-4-73 name=__codelineno-4-73 href=#__codelineno-4-73></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=phase-6-benutzer-review-im-frontend-mediainforeviewpanel>Phase 6: Benutzer-Review im Frontend (<code>MediaInfoReviewPanel</code>)<a class=headerlink href=#phase-6-benutzer-review-im-frontend-mediainforeviewpanel title="Permanent link">&para;</a></h2> <p>Das Review-Panel zeigt:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a>┌─────────────────────────────────────────────────────────────────┐
</span><span id=__span-5-2><a id=__codelineno-5-2 name=__codelineno-5-2 href=#__codelineno-5-2></a>│ Encode-Review Titel: Disc Title 1 │
</span><span id=__span-5-3><a id=__codelineno-5-3 name=__codelineno-5-3 href=#__codelineno-5-3></a>│ Laufzeit: 2:28:05 │
</span><span id=__span-5-4><a id=__codelineno-5-4 name=__codelineno-5-4 href=#__codelineno-5-4></a>├─────────────────────────────────────────────────────────────────┤
</span><span id=__span-5-5><a id=__codelineno-5-5 name=__codelineno-5-5 href=#__codelineno-5-5></a>│ Audio-Spuren │
</span><span id=__span-5-6><a id=__codelineno-5-6 name=__codelineno-5-6 href=#__codelineno-5-6></a>├──────┬──────────────────────────┬──────────────────────────────┤
</span><span id=__span-5-7><a id=__codelineno-5-7 name=__codelineno-5-7 href=#__codelineno-5-7></a>│ [✓] │ Track 1: English (AC3) │ Copy (ac3) │
</span><span id=__span-5-8><a id=__codelineno-5-8 name=__codelineno-5-8 href=#__codelineno-5-8></a>│ [✓] │ Track 2: Deutsch (DTS) │ Fallback Transcode (av_aac) │
</span><span id=__span-5-9><a id=__codelineno-5-9 name=__codelineno-5-9 href=#__codelineno-5-9></a>│ [ ] │ Track 3: Français (DTS) │ Nicht übernommen │
</span><span id=__span-5-10><a id=__codelineno-5-10 name=__codelineno-5-10 href=#__codelineno-5-10></a>├──────┴──────────────────────────┴──────────────────────────────┤
</span><span id=__span-5-11><a id=__codelineno-5-11 name=__codelineno-5-11 href=#__codelineno-5-11></a>│ Untertitel-Spuren │
</span><span id=__span-5-12><a id=__codelineno-5-12 name=__codelineno-5-12 href=#__codelineno-5-12></a>├──────┬──────────────────────────┬────────┬────────┬────────────┤
</span><span id=__span-5-13><a id=__codelineno-5-13 name=__codelineno-5-13 href=#__codelineno-5-13></a>│ [✓] │ Track 1: Deutsch │Einbr.[ ]│Forced[ ]│Default[✓]│
</span><span id=__span-5-14><a id=__codelineno-5-14 name=__codelineno-5-14 href=#__codelineno-5-14></a>│ [ ] │ Track 2: English │Einbr.[ ]│Forced[ ]│Default[ ]│
</span><span id=__span-5-15><a id=__codelineno-5-15 name=__codelineno-5-15 href=#__codelineno-5-15></a>├──────┴──────────────────────────┴────────┴────────┴────────────┤
</span><span id=__span-5-16><a id=__codelineno-5-16 name=__codelineno-5-16 href=#__codelineno-5-16></a>│ [Encoding starten] │
</span><span id=__span-5-17><a id=__codelineno-5-17 name=__codelineno-5-17 href=#__codelineno-5-17></a>└─────────────────────────────────────────────────────────────────┘
</span></code></pre></div> <p>Der Benutzer kann: - <strong>Audio-Tracks</strong> per Checkbox aktivieren/deaktivieren - <strong>Untertitel-Flags</strong> (Einbrennen, Forced, Default) setzen - <strong>Mehrere Titel</strong> bei der Titleauswahl wechseln (für Discs mit mehreren Haupttiteln)</p> <hr> <h2 id=phase-7-benutzer-auswahl-anwenden-applymanualtrackselectiontoplan>Phase 7: Benutzer-Auswahl anwenden (<code>applyManualTrackSelectionToPlan</code>)<a class=headerlink href=#phase-7-benutzer-auswahl-anwenden-applymanualtrackselectiontoplan title="Permanent link">&para;</a></h2> <p>Im Frontend wird die Benutzer-Auswahl beim Klick auf <strong>"Encoding starten"</strong> (ggf. automatisch) bestätigt und dann auf den Plan angewendet:</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-6-1><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a><span class=err>Payload</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-6-2><a id=__codelineno-6-2 name=__codelineno-6-2 href=#__codelineno-6-2></a><span class=w> </span><span class=nt>&quot;selectedEncodeTitleId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-6-3><a id=__codelineno-6-3 name=__codelineno-6-3 href=#__codelineno-6-3></a><span class=w> </span><span class=nt>&quot;selectedTrackSelection&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-6-4><a id=__codelineno-6-4 name=__codelineno-6-4 href=#__codelineno-6-4></a><span class=w> </span><span class=nt>&quot;1&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-6-5><a id=__codelineno-6-5 name=__codelineno-6-5 href=#__codelineno-6-5></a><span class=w> </span><span class=nt>&quot;audioTrackIds&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=mi>1</span><span class=p>,</span><span class=w> </span><span class=mi>2</span><span class=p>],</span>
</span><span id=__span-6-6><a id=__codelineno-6-6 name=__codelineno-6-6 href=#__codelineno-6-6></a><span class=w> </span><span class=nt>&quot;subtitleTrackIds&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=mi>1</span><span class=p>]</span>
</span><span id=__span-6-7><a id=__codelineno-6-7 name=__codelineno-6-7 href=#__codelineno-6-7></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-6-8><a id=__codelineno-6-8 name=__codelineno-6-8 href=#__codelineno-6-8></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-6-9><a id=__codelineno-6-9 name=__codelineno-6-9 href=#__codelineno-6-9></a><span class=p>}</span>
</span></code></pre></div> <p>Jeder Track erhält <code>selectedForEncode: true/false</code> entsprechend der Auswahl. Die Encoder-Aktionen (<code>encodeActions</code>) der nicht gewählten Tracks werden geleert.</p> <hr> <h2 id=phase-8-handbrake-cli-befehl>Phase 8: HandBrake-CLI-Befehl<a class=headerlink href=#phase-8-handbrake-cli-befehl title="Permanent link">&para;</a></h2> <p>Aus dem finalisierten Plan baut Ripster den HandBrake-Aufruf:</p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-7-1><a id=__codelineno-7-1 name=__codelineno-7-1 href=#__codelineno-7-1></a>HandBrakeCLI<span class=w> </span><span class=se>\</span>
</span><span id=__span-7-2><a id=__codelineno-7-2 name=__codelineno-7-2 href=#__codelineno-7-2></a><span class=w> </span>-i<span class=w> </span>/dev/sr0<span class=w> </span><span class=se>\</span>
</span><span id=__span-7-3><a id=__codelineno-7-3 name=__codelineno-7-3 href=#__codelineno-7-3></a><span class=w> </span>-o<span class=w> </span><span class=s2>&quot;/mnt/movies/Inception (2010).mkv&quot;</span><span class=w> </span><span class=se>\</span>
</span><span id=__span-7-4><a id=__codelineno-7-4 name=__codelineno-7-4 href=#__codelineno-7-4></a><span class=w> </span>-t<span class=w> </span><span class=m>1</span><span class=w> </span><span class=se>\</span>
</span><span id=__span-7-5><a id=__codelineno-7-5 name=__codelineno-7-5 href=#__codelineno-7-5></a><span class=w> </span>--preset<span class=w> </span><span class=s2>&quot;H.265 MKV 1080p30&quot;</span><span class=w> </span><span class=se>\</span>
</span><span id=__span-7-6><a id=__codelineno-7-6 name=__codelineno-7-6 href=#__codelineno-7-6></a><span class=w> </span>-a<span class=w> </span><span class=m>1</span>,2<span class=w> </span><span class=se>\</span>
</span><span id=__span-7-7><a id=__codelineno-7-7 name=__codelineno-7-7 href=#__codelineno-7-7></a><span class=w> </span>-E<span class=w> </span>copy:ac3,av_aac<span class=w> </span><span class=se>\</span>
</span><span id=__span-7-8><a id=__codelineno-7-8 name=__codelineno-7-8 href=#__codelineno-7-8></a><span class=w> </span>-s<span class=w> </span><span class=m>1</span><span class=w> </span><span class=se>\</span>
</span><span id=__span-7-9><a id=__codelineno-7-9 name=__codelineno-7-9 href=#__codelineno-7-9></a><span class=w> </span>--subtitle-default<span class=w> </span><span class=m>1</span>
</span></code></pre></div> <table> <thead> <tr> <th>Argument</th> <th>Quelle</th> </tr> </thead> <tbody> <tr> <td><code>-i</code></td> <td><code>encode_input_path</code> aus Job</td> </tr> <tr> <td><code>-o</code></td> <td>Ausgabepfad aus <code>filename_template</code> + <code>movie_dir</code></td> </tr> <tr> <td><code>-t</code></td> <td>Gewählter Titel-Index</td> </tr> <tr> <td><code>-a</code></td> <td>Kommagetrennte Audio-Track-IDs der ausgewählten Tracks</td> </tr> <tr> <td><code>-E</code></td> <td>Kommagetrennte Encoder-Aktionen (eine pro Track, gleiche Reihenfolge wie <code>-a</code>)</td> </tr> <tr> <td><code>-s</code></td> <td>Kommagetrennte Untertitel-Track-IDs</td> </tr> <tr> <td><code>--subtitle-default</code></td> <td>Track-ID der als Default markierten Untertitelspur</td> </tr> <tr> <td><code>--preset</code></td> <td><code>handbrake_preset</code>-Einstellung</td> </tr> <tr> <td>Extras</td> <td><code>handbrake_extra_args</code>-Einstellung</td> </tr> </tbody> </table> <hr> <h2 id=dateiname-template>Dateiname-Template<a class=headerlink href=#dateiname-template title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>Platzhalter</th> <th>Wert</th> <th>Beispiel</th> </tr> </thead> <tbody> <tr> <td><code>{title}</code></td> <td>Filmtitel von OMDb</td> <td><code>Inception</code></td> </tr> <tr> <td><code>{year}</code></td> <td>Erscheinungsjahr</td> <td><code>2010</code></td> </tr> <tr> <td><code>{imdb_id}</code></td> <td>IMDb-ID</td> <td><code>tt1375666</code></td> </tr> <tr> <td><code>{type}</code></td> <td><code>movie</code> oder <code>series</code></td> <td><code>movie</code></td> </tr> </tbody> </table> <p>Sonderzeichen (<code>:</code>, <code>/</code>, <code>?</code>, <code>*</code> etc.) werden automatisch aus dem Dateinamen entfernt.</p> <hr> <h2 id=re-encoding>Re-Encoding<a class=headerlink href=#re-encoding title="Permanent link">&para;</a></h2> <p>Ein abgeschlossener Job kann ohne erneutes Ripping neu encodiert werden:</p> <ol> <li>Job in der <strong>History</strong> öffnen</li> <li><strong>"Re-Encode"</strong> klicken</li> <li>Track-Auswahl anpassen (oder bestehende übernehmen)</li> <li>Encoding startet mit den aktuellen <code>handbrake_*</code>-Einstellungen</li> </ol> <p>Nützlich bei geänderten Presets, anderen Sprach-Präferenzen oder nach einem Einstellungs-Update.</p> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2Line truncated
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/pipeline/encoding/ rel=canonical><link href=../workflow/ rel=prev><link href=../playlist-analysis/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Encode-Planung & Track-Auswahl - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#encode-planung-track-auswahl class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Encode-Planung & Track-Auswahl </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svLine truncated
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a> -&gt; HandBrake-Scan (--scan --json)
</span><span id=__span-0-3><a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a> -&gt; Plan erstellen (Titel, Audio, Untertitel)
</span><span id=__span-0-4><a id=__codelineno-0-4 name=__codelineno-0-4 href=#__codelineno-0-4></a> -&gt; READY_TO_ENCODE
</span><span id=__span-0-5><a id=__codelineno-0-5 name=__codelineno-0-5 href=#__codelineno-0-5></a> -&gt; Benutzer bestätigt Auswahl
</span><span id=__span-0-6><a id=__codelineno-0-6 name=__codelineno-0-6 href=#__codelineno-0-6></a> -&gt; finaler HandBrake-Aufruf
</span></code></pre></div> <hr> <h2 id=review-inhalt-ready_to_encode>Review-Inhalt (<code>READY_TO_ENCODE</code>)<a class=headerlink href=#review-inhalt-ready_to_encode title="Permanent link">&para;</a></h2> <ul> <li>auswählbarer Encode-Titel</li> <li>Audio-Track-Selektion</li> <li>Untertitel-Track-Selektion inkl. Flags</li> <li><code>burnIn</code></li> <li><code>forced</code></li> <li><code>defaultTrack</code></li> <li>optionale User-Presets (HandBrake-Preset + Extra-Args)</li> <li>optionale Pre-/Post-Skripte und Ketten</li> </ul> <hr> <h2 id=bestatigung-confirm-encode>Bestätigung (<code>confirm-encode</code>)<a class=headerlink href=#bestatigung-confirm-encode title="Permanent link">&para;</a></h2> <p>Typischer Payload:</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a><span class=p>{</span>
</span><span id=__span-1-2><a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a><span class=w> </span><span class=nt>&quot;selectedEncodeTitleId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-1-3><a id=__codelineno-1-3 name=__codelineno-1-3 href=#__codelineno-1-3></a><span class=w> </span><span class=nt>&quot;selectedTrackSelection&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-1-4><a id=__codelineno-1-4 name=__codelineno-1-4 href=#__codelineno-1-4></a><span class=w> </span><span class=nt>&quot;1&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-1-5><a id=__codelineno-1-5 name=__codelineno-1-5 href=#__codelineno-1-5></a><span class=w> </span><span class=nt>&quot;audioTrackIds&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=mi>1</span><span class=p>,</span><span class=w> </span><span class=mi>2</span><span class=p>],</span>
</span><span id=__span-1-6><a id=__codelineno-1-6 name=__codelineno-1-6 href=#__codelineno-1-6></a><span class=w> </span><span class=nt>&quot;subtitleTrackIds&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=mi>3</span><span class=p>]</span>
</span><span id=__span-1-7><a id=__codelineno-1-7 name=__codelineno-1-7 href=#__codelineno-1-7></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-1-8><a id=__codelineno-1-8 name=__codelineno-1-8 href=#__codelineno-1-8></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-1-9><a id=__codelineno-1-9 name=__codelineno-1-9 href=#__codelineno-1-9></a><span class=w> </span><span class=nt>&quot;selectedPreEncodeScriptIds&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=mi>1</span><span class=p>],</span>
</span><span id=__span-1-10><a id=__codelineno-1-10 name=__codelineno-1-10 href=#__codelineno-1-10></a><span class=w> </span><span class=nt>&quot;selectedPostEncodeScriptIds&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=mi>2</span><span class=p>],</span>
</span><span id=__span-1-11><a id=__codelineno-1-11 name=__codelineno-1-11 href=#__codelineno-1-11></a><span class=w> </span><span class=nt>&quot;selectedPreEncodeChainIds&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=mi>3</span><span class=p>],</span>
</span><span id=__span-1-12><a id=__codelineno-1-12 name=__codelineno-1-12 href=#__codelineno-1-12></a><span class=w> </span><span class=nt>&quot;selectedPostEncodeChainIds&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=mi>4</span><span class=p>],</span>
</span><span id=__span-1-13><a id=__codelineno-1-13 name=__codelineno-1-13 href=#__codelineno-1-13></a><span class=w> </span><span class=nt>&quot;selectedUserPresetId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>5</span>
</span><span id=__span-1-14><a id=__codelineno-1-14 name=__codelineno-1-14 href=#__codelineno-1-14></a><span class=p>}</span>
</span></code></pre></div> <p>Ripster speichert die bestätigte Auswahl in <code>jobs.encode_plan_json</code> und markiert <code>encode_review_confirmed = 1</code>.</p> <hr> <h2 id=handbrake-aufruf>HandBrake-Aufruf<a class=headerlink href=#handbrake-aufruf title="Permanent link">&para;</a></h2> <p>Grundstruktur:</p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a>HandBrakeCLI<span class=w> </span><span class=se>\</span>
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a><span class=w> </span>-i<span class=w> </span>&lt;input&gt;<span class=w> </span><span class=se>\</span>
</span><span id=__span-2-3><a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a><span class=w> </span>-o<span class=w> </span>&lt;output&gt;<span class=w> </span><span class=se>\</span>
</span><span id=__span-2-4><a id=__codelineno-2-4 name=__codelineno-2-4 href=#__codelineno-2-4></a><span class=w> </span>-t<span class=w> </span>&lt;titleId&gt;<span class=w> </span><span class=se>\</span>
</span><span id=__span-2-5><a id=__codelineno-2-5 name=__codelineno-2-5 href=#__codelineno-2-5></a><span class=w> </span>-Z<span class=w> </span><span class=s2>&quot;&lt;preset&gt;&quot;</span><span class=w> </span><span class=se>\</span>
</span><span id=__span-2-6><a id=__codelineno-2-6 name=__codelineno-2-6 href=#__codelineno-2-6></a><span class=w> </span>&lt;extra-args&gt;<span class=w> </span><span class=se>\</span>
</span><span id=__span-2-7><a id=__codelineno-2-7 name=__codelineno-2-7 href=#__codelineno-2-7></a><span class=w> </span>-a<span class=w> </span>&lt;audioTrackIds<span class=p>|</span>none&gt;<span class=w> </span><span class=se>\</span>
</span><span id=__span-2-8><a id=__codelineno-2-8 name=__codelineno-2-8 href=#__codelineno-2-8></a><span class=w> </span>-s<span class=w> </span>&lt;subtitleTrackIds<span class=p>|</span>none&gt;
</span></code></pre></div> <p>Untertitel-Flags werden bei Bedarf ergänzt:</p> <ul> <li><code>--subtitle-burned=&lt;id&gt;</code></li> <li><code>--subtitle-default=&lt;id&gt;</code></li> <li><code>--subtitle-forced=&lt;id&gt;</code> oder <code>--subtitle-forced</code></li> </ul> <hr> <h2 id=pre-post-encode-ausfuhrungen>Pre-/Post-Encode-Ausführungen<a class=headerlink href=#pre-post-encode-ausfuhrungen title="Permanent link">&para;</a></h2> <ul> <li>Pre-Encode läuft vor HandBrake</li> <li>Post-Encode läuft nach HandBrake</li> </ul> <p>Verhalten bei Fehlern:</p> <ul> <li>Pre-Encode-Fehler: Job wird als <code>ERROR</code> beendet (Encode startet nicht)</li> <li>Post-Encode-Fehler: Job kann <code>FINISHED</code> bleiben, enthält aber Fehlerhinweis/Script-Summary</li> </ul> <hr> <h2 id=dateinamenordner>Dateinamen/Ordner<a class=headerlink href=#dateinamenordner title="Permanent link">&para;</a></h2> <p>Der finale Outputpfad wird aus Settings-Templates aufgebaut.</p> <p>Platzhalter:</p> <ul> <li><code>${title}</code></li> <li><code>${year}</code></li> <li><code>${imdbId}</code></li> </ul> <p>Ungültige Dateizeichen werden sanitisiert.</p> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "VerLine truncated
+1 -1
View File
@@ -1 +1 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/pipeline/ rel=canonical><link href=../architecture/database/ rel=prev><link href=workflow/ rel=next><link rel=icon href=../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Pipeline - Ripster</title><link rel=stylesheet href=../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../stylesheets/extra.css><script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#pipeline class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Pipeline </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.2Line truncated
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/pipeline/ rel=canonical><link href=../architecture/database/ rel=prev><link href=workflow/ rel=next><link rel=icon href=../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Pipeline - Ripster</title><link rel=stylesheet href=../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../stylesheets/extra.css><script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#pipeline class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Pipeline </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.2Line truncated
+1 -74
View File
@@ -1,74 +1 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/pipeline/playlist-analysis/ rel=canonical><link href=../encoding/ rel=prev><link href=../post-encode-scripts/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Playlist-Analyse - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#playlist-analyse class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Playlist-Analyse </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path dLine truncated
</span></code></pre></div> <p>Feld <strong>26</strong> enthält die kommagetrennte Liste der Segment-Nummern in der Abspielreihenfolge des Titels.</p> <hr> <h2 id=algorithmus-im-detail-playlistanalysisjs>Algorithmus im Detail (<code>playlistAnalysis.js</code>)<a class=headerlink href=#algorithmus-im-detail-playlistanalysisjs title="Permanent link">&para;</a></h2> <h3 id=schritt-1-segment-nummern-parsen>Schritt 1 – Segment-Nummern parsen<a class=headerlink href=#schritt-1-segment-nummern-parsen title="Permanent link">&para;</a></h3> <div class="language-text highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a>TINFO:1,26,&quot;00000,00001,00002,00003&quot; → [0, 1, 2, 3] linearer Film
</span><span id=__span-1-2><a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a>TINFO:2,26,&quot;00100,00050,00100,00051&quot; → [100, 50, 100, 51] Fake-Playlist
</span></code></pre></div> <h3 id=schritt-2-metriken-berechnen-computesegmentmetrics>Schritt 2 – Metriken berechnen (<code>computeSegmentMetrics</code>)<a class=headerlink href=#schritt-2-metriken-berechnen-computesegmentmetrics title="Permanent link">&para;</a></h3> <p>Für jedes aufeinanderfolgende Segment-Paar <code>[a, b]</code> wird <code>diff = b − a</code> berechnet:</p> <table> <thead> <tr> <th>Metrik</th> <th>Bedingung</th> <th>Bedeutung</th> </tr> </thead> <tbody> <tr> <td><code>directSequenceSteps</code></td> <td><code>diff == 1</code></td> <td>Aufeinanderfolgende Segmente → linearer Film</td> </tr> <tr> <td><code>backwardJumps</code></td> <td><code>b &lt; a</code></td> <td>Rückwärtssprünge → verdächtig</td> </tr> <tr> <td><code>largeJumps</code></td> <td><code>\|diff\| &gt; 20</code></td> <td>Große Sprünge → verdächtig</td> </tr> <tr> <td><code>alternatingPairs</code></td> <td>Große Sprünge mit <strong>wechselndem Vorzeichen</strong></td> <td>Hin-und-her-Muster → starker Fake-Indikator</td> </tr> </tbody> </table> <p><strong>Score-Formel:</strong></p> <div class="language-text highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a>score = (directSequenceSteps × 2) − (backwardJumps × 3) − (largeJumps × 2)
</span></code></pre></div> <p><strong>Konkrete Beispiele:</strong></p> <table> <thead> <tr> <th>Segmentfolge</th> <th>directSeq</th> <th>backward</th> <th>large</th> <th>score</th> <th>Ergebnis</th> </tr> </thead> <tbody> <tr> <td><code>0,1,2,3,4,5</code></td> <td>5</td> <td>0</td> <td>0</td> <td>+10</td> <td>Echter Film</td> </tr> <tr> <td><code>0,1,100,2,101,3</code></td> <td>2</td> <td>0</td> <td>4</td> <td>-4</td> <td>Verdächtig</td> </tr> <tr> <td><code>50,10,60,11,70,12</code></td> <td>0</td> <td>3</td> <td>3</td> <td>-15</td> <td>Fake</td> </tr> </tbody> </table> <h3 id=schritt-3-bewertungslabel-vergeben-buildevaluationlabel>Schritt 3 – Bewertungslabel vergeben (<code>buildEvaluationLabel</code>)<a class=headerlink href=#schritt-3-bewertungslabel-vergeben-buildevaluationlabel title="Permanent link">&para;</a></h3> <div class="language-text highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a>alternatingRatio = alternatingPairs / largeJumps
</span><span id=__span-3-2><a id=__codelineno-3-2 name=__codelineno-3-2 href=#__codelineno-3-2></a>
</span><span id=__span-3-3><a id=__codelineno-3-3 name=__codelineno-3-3 href=#__codelineno-3-3></a>if alternatingRatio &gt;= 0.55 AND alternatingPairs &gt;= 3:
</span><span id=__span-3-4><a id=__codelineno-3-4 name=__codelineno-3-4 href=#__codelineno-3-4></a> → &quot;Fake-Struktur (alternierendes Sprungmuster)&quot;
</span><span id=__span-3-5><a id=__codelineno-3-5 name=__codelineno-3-5 href=#__codelineno-3-5></a>
</span><span id=__span-3-6><a id=__codelineno-3-6 name=__codelineno-3-6 href=#__codelineno-3-6></a>else if backwardJumps &gt; 0 OR largeJumps &gt; 0:
</span><span id=__span-3-7><a id=__codelineno-3-7 name=__codelineno-3-7 href=#__codelineno-3-7></a> → &quot;Auffällige Segmentreihenfolge&quot;
</span><span id=__span-3-8><a id=__codelineno-3-8 name=__codelineno-3-8 href=#__codelineno-3-8></a>
</span><span id=__span-3-9><a id=__codelineno-3-9 name=__codelineno-3-9 href=#__codelineno-3-9></a>else:
</span><span id=__span-3-10><a id=__codelineno-3-10 name=__codelineno-3-10 href=#__codelineno-3-10></a> → &quot;wahrscheinlich korrekt (lineare Segmentfolge)&quot;
</span></code></pre></div> <h3 id=schritt-4-duplikat-gruppen-bilden-buildsimilaritygroups>Schritt 4 – Duplikat-Gruppen bilden (<code>buildSimilarityGroups</code>)<a class=headerlink href=#schritt-4-duplikat-gruppen-bilden-buildsimilaritygroups title="Permanent link">&para;</a></h3> <p>Alle Titel werden nach <strong>ähnlicher Laufzeit</strong> gruppiert (±90 Sekunden Toleranz). Gibt es mehrere Kandidaten mit ähnlicher Laufzeit, ist das ein klares Zeichen für Obfuskierung:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a>8 Titel mit ~148 Minuten Laufzeit → Duplikat-Gruppe
</span><span id=__span-4-2><a id=__codelineno-4-2 name=__codelineno-4-2 href=#__codelineno-4-2></a>→ obfuscationDetected = true
</span></code></pre></div> <h3 id=schritt-5-besten-kandidaten-empfehlen-scorecandidates>Schritt 5 – Besten Kandidaten empfehlen (<code>scoreCandidates</code>)<a class=headerlink href=#schritt-5-besten-kandidaten-empfehlen-scorecandidates title="Permanent link">&para;</a></h3> <p>Innerhalb der größten Duplikat-Gruppe werden alle Kandidaten sortiert nach:</p> <ol> <li><code>score</code> (höher = besser)</li> <li><code>sequenceCoherence</code> (Anteil linearer Segmentschritte)</li> <li>Laufzeit (länger = besser)</li> <li>Dateigröße (größer = besser als Tiebreaker)</li> </ol> <p>Der <strong>erste Kandidat</strong> der sortierten Liste ist die Empfehlung.</p> <h3 id=schritt-6-entscheidung-erzwingen-bei-mehreren-kandidaten>Schritt 6 – Entscheidung erzwingen bei mehreren Kandidaten<a class=headerlink href=#schritt-6-entscheidung-erzwingen-bei-mehreren-kandidaten title="Permanent link">&para;</a></h3> <p>Sobald nach <code>MIN_LENGTH_MINUTES</code> <strong>mehr als eine</strong> Playlist übrig bleibt, wird immer eine manuelle Auswahl verlangt:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a>candidateCount &gt; 1 → manualDecisionRequired = true
</span><span id=__span-5-2><a id=__codelineno-5-2 name=__codelineno-5-2 href=#__codelineno-5-2></a>candidateCount &lt;= 1 → manualDecisionRequired = false
</span></code></pre></div> <hr> <h2 id=wann-greift-der-benutzer-ein>Wann greift der Benutzer ein?<a class=headerlink href=#wann-greift-der-benutzer-ein title="Permanent link">&para;</a></h2> <div class="language-text highlight"><pre><span></span><code><span id=__span-6-1><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a>obfuscationDetected = duplicateDurationGroups.length &gt; 0
</span><span id=__span-6-2><a id=__codelineno-6-2 name=__codelineno-6-2 href=#__codelineno-6-2></a>manualDecisionRequired = candidates.length &gt; 1
</span></code></pre></div> <table> <thead> <tr> <th>Ergebnis</th> <th>Nächster Pipeline-Zustand</th> <th>Aktion</th> </tr> </thead> <tbody> <tr> <td>Nur ein Kandidat nach Mindestlänge</td> <td><code>READY_TO_START</code></td> <td>Automatische Übernahme möglich</td> </tr> <tr> <td>Mehrere Kandidaten nach Mindestlänge</td> <td><code>WAITING_FOR_USER_DECISION</code></td> <td>Benutzer muss Playlist auswählen</td> </tr> </tbody> </table> <hr> <h2 id=benutzeroberflache-playlist-auswahl-dialog>Benutzeroberfläche: Playlist-Auswahl-Dialog<a class=headerlink href=#benutzeroberflache-playlist-auswahl-dialog title="Permanent link">&para;</a></h2> <p>Wenn <code>manualDecisionRequired = true</code>, öffnet sich der Playlist-Dialog <strong>nach</strong> dem Metadaten-Dialog:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-7-1><a id=__codelineno-7-1 name=__codelineno-7-1 href=#__codelineno-7-1></a>┌───────────────────────────────────────────────────────────────────┐
</span><span id=__span-7-2><a id=__codelineno-7-2 name=__codelineno-7-2 href=#__codelineno-7-2></a>│ Playlist-Auswahl │
</span><span id=__span-7-3><a id=__codelineno-7-3 name=__codelineno-7-3 href=#__codelineno-7-3></a>├──────────┬──────────┬──────────┬────────────────────────────────┤
</span><span id=__span-7-4><a id=__codelineno-7-4 name=__codelineno-7-4 href=#__codelineno-7-4></a>│ Playlist │ Laufzeit │ Score │ Bewertung │
</span><span id=__span-7-5><a id=__codelineno-7-5 name=__codelineno-7-5 href=#__codelineno-7-5></a>├──────────┼──────────┼──────────┼────────────────────────────────┤
</span><span id=__span-7-6><a id=__codelineno-7-6 name=__codelineno-7-6 href=#__codelineno-7-6></a>│ ★ 00800 │ 2:28:05 │ +18 │ wahrscheinlich korrekt │
</span><span id=__span-7-7><a id=__codelineno-7-7 name=__codelineno-7-7 href=#__codelineno-7-7></a>│ │ │ │ (lineare Segmentfolge) │
</span><span id=__span-7-8><a id=__codelineno-7-8 name=__codelineno-7-8 href=#__codelineno-7-8></a>├──────────┼──────────┼──────────┼────────────────────────────────┤
</span><span id=__span-7-9><a id=__codelineno-7-9 name=__codelineno-7-9 href=#__codelineno-7-9></a>│ 00801 │ 2:28:12 │ −4 │ Auffällige Segmentreihenfolge │
</span><span id=__span-7-10><a id=__codelineno-7-10 name=__codelineno-7-10 href=#__codelineno-7-10></a>├──────────┼──────────┼──────────┼────────────────────────────────┤
</span><span id=__span-7-11><a id=__codelineno-7-11 name=__codelineno-7-11 href=#__codelineno-7-11></a>│ 00900 │ 2:28:05 │ −32 │ Fake-Struktur │
</span><span id=__span-7-12><a id=__codelineno-7-12 name=__codelineno-7-12 href=#__codelineno-7-12></a>│ │ │ │ (alternierendes Sprungmuster) │
</span><span id=__span-7-13><a id=__codelineno-7-13 name=__codelineno-7-13 href=#__codelineno-7-13></a>└──────────┴──────────┴──────────┴────────────────────────────────┘
</span><span id=__span-7-14><a id=__codelineno-7-14 name=__codelineno-7-14 href=#__codelineno-7-14></a> Hinweis: 847 Playlists insgesamt. 3 relevante Kandidaten (≥ 15 min).
</span><span id=__span-7-15><a id=__codelineno-7-15 name=__codelineno-7-15 href=#__codelineno-7-15></a> Empfehlung: 00800 (★)
</span></code></pre></div> <ul> <li><strong>★</strong> markiert die empfohlene Playlist (vorausgewählt)</li> <li>Nur Titel ≥ <code>makemkv_min_length_minutes</code> erscheinen in der Liste</li> <li>Der Benutzer wählt per Radio-Button und klickt "Bestätigen"</li> <li>Erst nach dieser Bestätigung wechselt die Pipeline zu <code>READY_TO_START</code></li> </ul> <hr> <h2 id=vollstandige-datenstruktur-analyzecontextplaylistanalysis>Vollständige Datenstruktur (<code>analyzeContext.playlistAnalysis</code>)<a class=headerlink href=#vollstandige-datenstruktur-analyzecontextplaylistanalysis title="Permanent link">&para;</a></h2> <div class="language-json highlight"><pre><span></span><code><span id=__span-8-1><a id=__codelineno-8-1 name=__codelineno-8-1 href=#__codelineno-8-1></a><span class=p>{</span>
</span><span id=__span-8-2><a id=__codelineno-8-2 name=__codelineno-8-2 href=#__codelineno-8-2></a><span class=w> </span><span class=nt>&quot;titles&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-8-3><a id=__codelineno-8-3 name=__codelineno-8-3 href=#__codelineno-8-3></a><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;titleId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;playlistId&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;00800&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;durationSeconds&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>8885</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;durationLabel&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2:28:05&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;chapters&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>28</span><span class=w> </span><span class=p>}</span>
</span><span id=__span-8-4><a id=__codelineno-8-4 name=__codelineno-8-4 href=#__codelineno-8-4></a><span class=w> </span><span class=p>],</span>
</span><span id=__span-8-5><a id=__codelineno-8-5 name=__codelineno-8-5 href=#__codelineno-8-5></a><span class=w> </span><span class=nt>&quot;candidates&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-8-6><a id=__codelineno-8-6 name=__codelineno-8-6 href=#__codelineno-8-6></a><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;titleId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;playlistId&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;00800&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;durationSeconds&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>8885</span><span class=w> </span><span class=p>},</span>
</span><span id=__span-8-7><a id=__codelineno-8-7 name=__codelineno-8-7 href=#__codelineno-8-7></a><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;titleId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>2</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;playlistId&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;00801&quot;</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;durationSeconds&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>8892</span><span class=w> </span><span class=p>}</span>
</span><span id=__span-8-8><a id=__codelineno-8-8 name=__codelineno-8-8 href=#__codelineno-8-8></a><span class=w> </span><span class=p>],</span>
</span><span id=__span-8-9><a id=__codelineno-8-9 name=__codelineno-8-9 href=#__codelineno-8-9></a><span class=w> </span><span class=nt>&quot;evaluatedCandidates&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-8-10><a id=__codelineno-8-10 name=__codelineno-8-10 href=#__codelineno-8-10></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-8-11><a id=__codelineno-8-11 name=__codelineno-8-11 href=#__codelineno-8-11></a><span class=w> </span><span class=nt>&quot;titleId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-8-12><a id=__codelineno-8-12 name=__codelineno-8-12 href=#__codelineno-8-12></a><span class=w> </span><span class=nt>&quot;playlistId&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;00800&quot;</span><span class=p>,</span>
</span><span id=__span-8-13><a id=__codelineno-8-13 name=__codelineno-8-13 href=#__codelineno-8-13></a><span class=w> </span><span class=nt>&quot;score&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>18</span><span class=p>,</span>
</span><span id=__span-8-14><a id=__codelineno-8-14 name=__codelineno-8-14 href=#__codelineno-8-14></a><span class=w> </span><span class=nt>&quot;sequenceCoherence&quot;</span><span class=p>:</span><span class=w> </span><span class=mf>0.95</span><span class=p>,</span>
</span><span id=__span-8-15><a id=__codelineno-8-15 name=__codelineno-8-15 href=#__codelineno-8-15></a><span class=w> </span><span class=nt>&quot;evaluationLabel&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;wahrscheinlich korrekt (lineare Segmentfolge)&quot;</span><span class=p>,</span>
</span><span id=__span-8-16><a id=__codelineno-8-16 name=__codelineno-8-16 href=#__codelineno-8-16></a><span class=w> </span><span class=nt>&quot;metrics&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-8-17><a id=__codelineno-8-17 name=__codelineno-8-17 href=#__codelineno-8-17></a><span class=w> </span><span class=nt>&quot;directSequenceSteps&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>12</span><span class=p>,</span>
</span><span id=__span-8-18><a id=__codelineno-8-18 name=__codelineno-8-18 href=#__codelineno-8-18></a><span class=w> </span><span class=nt>&quot;backwardJumps&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>0</span><span class=p>,</span>
</span><span id=__span-8-19><a id=__codelineno-8-19 name=__codelineno-8-19 href=#__codelineno-8-19></a><span class=w> </span><span class=nt>&quot;largeJumps&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-8-20><a id=__codelineno-8-20 name=__codelineno-8-20 href=#__codelineno-8-20></a><span class=w> </span><span class=nt>&quot;alternatingPairs&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>0</span>
</span><span id=__span-8-21><a id=__codelineno-8-21 name=__codelineno-8-21 href=#__codelineno-8-21></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-8-22><a id=__codelineno-8-22 name=__codelineno-8-22 href=#__codelineno-8-22></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-8-23><a id=__codelineno-8-23 name=__codelineno-8-23 href=#__codelineno-8-23></a><span class=w> </span><span class=p>],</span>
</span><span id=__span-8-24><a id=__codelineno-8-24 name=__codelineno-8-24 href=#__codelineno-8-24></a><span class=w> </span><span class=nt>&quot;duplicateDurationGroups&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-8-25><a id=__codelineno-8-25 name=__codelineno-8-25 href=#__codelineno-8-25></a><span class=w> </span><span class=p>[</span>
</span><span id=__span-8-26><a id=__codelineno-8-26 name=__codelineno-8-26 href=#__codelineno-8-26></a><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;titleId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;playlistId&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;00800&quot;</span><span class=w> </span><span class=p>},</span>
</span><span id=__span-8-27><a id=__codelineno-8-27 name=__codelineno-8-27 href=#__codelineno-8-27></a><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>&quot;titleId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>2</span><span class=p>,</span><span class=w> </span><span class=nt>&quot;playlistId&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;00801&quot;</span><span class=w> </span><span class=p>}</span>
</span><span id=__span-8-28><a id=__codelineno-8-28 name=__codelineno-8-28 href=#__codelineno-8-28></a><span class=w> </span><span class=p>]</span>
</span><span id=__span-8-29><a id=__codelineno-8-29 name=__codelineno-8-29 href=#__codelineno-8-29></a><span class=w> </span><span class=p>],</span>
</span><span id=__span-8-30><a id=__codelineno-8-30 name=__codelineno-8-30 href=#__codelineno-8-30></a><span class=w> </span><span class=nt>&quot;recommendation&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-8-31><a id=__codelineno-8-31 name=__codelineno-8-31 href=#__codelineno-8-31></a><span class=w> </span><span class=nt>&quot;titleId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-8-32><a id=__codelineno-8-32 name=__codelineno-8-32 href=#__codelineno-8-32></a><span class=w> </span><span class=nt>&quot;playlistId&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;00800&quot;</span><span class=p>,</span>
</span><span id=__span-8-33><a id=__codelineno-8-33 name=__codelineno-8-33 href=#__codelineno-8-33></a><span class=w> </span><span class=nt>&quot;score&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>18</span><span class=p>,</span>
</span><span id=__span-8-34><a id=__codelineno-8-34 name=__codelineno-8-34 href=#__codelineno-8-34></a><span class=w> </span><span class=nt>&quot;reason&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Höchster Segment-Score in der größten Laufzeit-Gruppe&quot;</span>
</span><span id=__span-8-35><a id=__codelineno-8-35 name=__codelineno-8-35 href=#__codelineno-8-35></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-8-36><a id=__codelineno-8-36 name=__codelineno-8-36 href=#__codelineno-8-36></a><span class=w> </span><span class=nt>&quot;obfuscationDetected&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span><span class=p>,</span>
</span><span id=__span-8-37><a id=__codelineno-8-37 name=__codelineno-8-37 href=#__codelineno-8-37></a><span class=w> </span><span class=nt>&quot;manualDecisionRequired&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>true</span>
</span><span id=__span-8-38><a id=__codelineno-8-38 name=__codelineno-8-38 href=#__codelineno-8-38></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=konfiguration>Konfiguration<a class=headerlink href=#konfiguration title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>Einstellung</th> <th>Standard</th> <th>Wirkung</th> </tr> </thead> <tbody> <tr> <td><code>makemkv_min_length_minutes</code></td> <td><code>15</code></td> <td>Titel kürzer als dieser Wert werden als Kandidaten ignoriert</td> </tr> </tbody> </table> <hr> <h2 id=tipps-bei-fehlempfehlung>Tipps bei Fehlempfehlung<a class=headerlink href=#tipps-bei-fehlempfehlung title="Permanent link">&para;</a></h2> <div class="admonition tip"> <p class=admonition-title>Falsche Playlist gewählt?</p> <p>Wenn das resultierende Video zerstückelt ist:</p> <ol> <li>Job in der <strong>History</strong> öffnen</li> <li><strong>Re-Encode</strong> starten – diesmal eine andere Playlist wählen</li> <li>Alternativ: Korrekte Playlist im <a href=https://www.makemkv.com/forum/ >MakeMKV-Forum</a> recherchieren</li> </ol> </div> <div class="admonition info"> <p class=admonition-title>Keine Segment-Daten verfügbar</p> <p>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.</p> </div> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other":Line truncated
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/pipeline/playlist-analysis/ rel=canonical><link href=../encoding/ rel=prev><link href=../post-encode-scripts/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Playlist-Analyse - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#playlist-analyse class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Playlist-Analyse </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path dLine truncated
+6 -55
View File
@@ -1,55 +1,6 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/pipeline/post-encode-scripts/ rel=canonical><link href=../playlist-analysis/ rel=prev><link href=../../api/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Post-Encode-Skripte - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#post-encode-skripte class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Post-Encode-Skripte </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24">Line truncated
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a> ↓
</span><span id=__span-0-3><a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a>Skript 1 ausführen ← Fehler? → Abbruch
</span><span id=__span-0-4><a id=__codelineno-0-4 name=__codelineno-0-4 href=#__codelineno-0-4></a> ↓
</span><span id=__span-0-5><a id=__codelineno-0-5 name=__codelineno-0-5 href=#__codelineno-0-5></a>Skript 2 ausführen ← Fehler? → Abbruch
</span><span id=__span-0-6><a id=__codelineno-0-6 name=__codelineno-0-6 href=#__codelineno-0-6></a> ↓
</span><span id=__span-0-7><a id=__codelineno-0-7 name=__codelineno-0-7 href=#__codelineno-0-7></a> ...
</span><span id=__span-0-8><a id=__codelineno-0-8 name=__codelineno-0-8 href=#__codelineno-0-8></a> ↓
</span><span id=__span-0-9><a id=__codelineno-0-9 name=__codelineno-0-9 href=#__codelineno-0-9></a>FINISHED
</span></code></pre></div> <div class="admonition warning"> <p class=admonition-title>Abbruch bei Fehler</p> <p>Schlägt ein Skript fehl (Exit-Code ≠ 0), werden alle nachfolgenden Skripte <strong>nicht mehr ausgeführt</strong>. Der Job bleibt im Abschlusszustand <code>FINISHED</code>; der Fehler wird in Log/Status-Text und im <code>postEncodeScripts</code>-Summary festgehalten.</p> </div> <hr> <h2 id=skript-verwaltung>Skript-Verwaltung<a class=headerlink href=#skript-verwaltung title="Permanent link">&para;</a></h2> <p>Skripte werden über die <strong>Einstellungen-Seite</strong> angelegt und verwaltet. Sie stehen danach in jedem Encode-Review zur Auswahl.</p> <h3 id=skript-anlegen>Skript anlegen<a class=headerlink href=#skript-anlegen title="Permanent link">&para;</a></h3> <p>Navigiere zu <strong>Einstellungen → Skripte</strong> und klicke <strong>"Neues Skript"</strong>:</p> <table> <thead> <tr> <th>Feld</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><strong>Name</strong></td> <td>Anzeigename des Skripts (z. B. <code>Zu Plex verschieben</code>)</td> </tr> <tr> <td><strong>Befehl</strong></td> <td>Shell-Befehl oder Skriptpfad (z. B. <code>/home/michael/scripts/move-to-plex.sh</code>)</td> </tr> <tr> <td><strong>Beschreibung</strong></td> <td>Optionale Erklärung</td> </tr> </tbody> </table> <h3 id=verfugbare-umgebungsvariablen>Verfügbare Umgebungsvariablen<a class=headerlink href=#verfugbare-umgebungsvariablen title="Permanent link">&para;</a></h3> <p>Jedes Skript wird mit folgenden Umgebungsvariablen aufgerufen:</p> <table> <thead> <tr> <th>Variable</th> <th>Inhalt</th> <th>Beispiel</th> </tr> </thead> <tbody> <tr> <td><code>RIPSTER_OUTPUT_PATH</code></td> <td>Absoluter Pfad der encodierten Datei</td> <td><code>/mnt/movies/Inception (2010).mkv</code></td> </tr> <tr> <td><code>RIPSTER_JOB_ID</code></td> <td>Job-ID in der Datenbank</td> <td><code>42</code></td> </tr> <tr> <td><code>RIPSTER_TITLE</code></td> <td>Filmtitel</td> <td><code>Inception</code></td> </tr> <tr> <td><code>RIPSTER_YEAR</code></td> <td>Erscheinungsjahr</td> <td><code>2010</code></td> </tr> <tr> <td><code>RIPSTER_IMDB_ID</code></td> <td>IMDb-ID</td> <td><code>tt1375666</code></td> </tr> <tr> <td><code>RIPSTER_RAW_PATH</code></td> <td>Pfad zur Raw-MKV-Datei</td> <td><code>/mnt/raw/Inception-2010/t00.mkv</code></td> </tr> </tbody> </table> <h3 id=beispiel-skript-datei-nach-jellyfin-verschieben>Beispiel-Skript: Datei nach Jellyfin verschieben<a class=headerlink href=#beispiel-skript-datei-nach-jellyfin-verschieben title="Permanent link">&para;</a></h3> <div class="language-bash highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a><span class=ch>#!/bin/bash</span>
</span><span id=__span-1-2><a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a><span class=c1># /home/michael/scripts/move-to-jellyfin.sh</span>
</span><span id=__span-1-3><a id=__codelineno-1-3 name=__codelineno-1-3 href=#__codelineno-1-3></a>
</span><span id=__span-1-4><a id=__codelineno-1-4 name=__codelineno-1-4 href=#__codelineno-1-4></a><span class=nv>TARGET_DIR</span><span class=o>=</span><span class=s2>&quot;/mnt/media/movies&quot;</span>
</span><span id=__span-1-5><a id=__codelineno-1-5 name=__codelineno-1-5 href=#__codelineno-1-5></a>mkdir<span class=w> </span>-p<span class=w> </span><span class=s2>&quot;</span><span class=nv>$TARGET_DIR</span><span class=s2>&quot;</span>
</span><span id=__span-1-6><a id=__codelineno-1-6 name=__codelineno-1-6 href=#__codelineno-1-6></a>mv<span class=w> </span><span class=s2>&quot;</span><span class=nv>$RIPSTER_OUTPUT_PATH</span><span class=s2>&quot;</span><span class=w> </span><span class=s2>&quot;</span><span class=nv>$TARGET_DIR</span><span class=s2>/&quot;</span>
</span><span id=__span-1-7><a id=__codelineno-1-7 name=__codelineno-1-7 href=#__codelineno-1-7></a><span class=nb>echo</span><span class=w> </span><span class=s2>&quot;Verschoben: </span><span class=nv>$RIPSTER_TITLE</span><span class=s2> nach </span><span class=nv>$TARGET_DIR</span><span class=s2>&quot;</span>
</span></code></pre></div> <h3 id=beispiel-skript-webhook-auslosen>Beispiel-Skript: Webhook auslösen<a class=headerlink href=#beispiel-skript-webhook-auslosen title="Permanent link">&para;</a></h3> <div class="language-bash highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a><span class=ch>#!/bin/bash</span>
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a><span class=c1># /home/michael/scripts/notify-webhook.sh</span>
</span><span id=__span-2-3><a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a>
</span><span id=__span-2-4><a id=__codelineno-2-4 name=__codelineno-2-4 href=#__codelineno-2-4></a>curl<span class=w> </span>-s<span class=w> </span>-X<span class=w> </span>POST<span class=w> </span>https://mein-webhook.example.com/ripster<span class=w> </span><span class=se>\</span>
</span><span id=__span-2-5><a id=__codelineno-2-5 name=__codelineno-2-5 href=#__codelineno-2-5></a><span class=w> </span>-H<span class=w> </span><span class=s2>&quot;Content-Type: application/json&quot;</span><span class=w> </span><span class=se>\</span>
</span><span id=__span-2-6><a id=__codelineno-2-6 name=__codelineno-2-6 href=#__codelineno-2-6></a><span class=w> </span>-d<span class=w> </span><span class=s2>&quot;{\&quot;title\&quot;: \&quot;</span><span class=nv>$RIPSTER_TITLE</span><span class=s2>\&quot;, \&quot;year\&quot;: \&quot;</span><span class=nv>$RIPSTER_YEAR</span><span class=s2>\&quot;, \&quot;path\&quot;: \&quot;</span><span class=nv>$RIPSTER_OUTPUT_PATH</span><span class=s2>\&quot;}&quot;</span>
</span></code></pre></div> <hr> <h2 id=skript-im-encode-review-auswahlen>Skript im Encode-Review auswählen<a class=headerlink href=#skript-im-encode-review-auswahlen title="Permanent link">&para;</a></h2> <p>Im <code>READY_TO_ENCODE</code>-Zustand zeigt das <strong>MediaInfoReviewPanel</strong> einen Skript-Abschnitt:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a>┌──────────────────────────────────────────────────────────┐
</span><span id=__span-3-2><a id=__codelineno-3-2 name=__codelineno-3-2 href=#__codelineno-3-2></a>│ Post-Encode-Skripte │
</span><span id=__span-3-3><a id=__codelineno-3-3 name=__codelineno-3-3 href=#__codelineno-3-3></a>├──────────────────────────────────────────────────────────┤
</span><span id=__span-3-4><a id=__codelineno-3-4 name=__codelineno-3-4 href=#__codelineno-3-4></a>│ Ausgewählte Skripte (Reihenfolge per Drag &amp; Drop): │
</span><span id=__span-3-5><a id=__codelineno-3-5 name=__codelineno-3-5 href=#__codelineno-3-5></a>│ ≡ 1. Zu Plex verschieben [Entfernen]│
</span><span id=__span-3-6><a id=__codelineno-3-6 name=__codelineno-3-6 href=#__codelineno-3-6></a>│ ≡ 2. Webhook auslösen [Entfernen]│
</span><span id=__span-3-7><a id=__codelineno-3-7 name=__codelineno-3-7 href=#__codelineno-3-7></a>├──────────────────────────────────────────────────────────┤
</span><span id=__span-3-8><a id=__codelineno-3-8 name=__codelineno-3-8 href=#__codelineno-3-8></a>│ Skript hinzufügen: [Zu Jellyfin verschieben ▾] [+ Hinzuf.]│
</span><span id=__span-3-9><a id=__codelineno-3-9 name=__codelineno-3-9 href=#__codelineno-3-9></a>└──────────────────────────────────────────────────────────┘
</span></code></pre></div> <ul> <li><strong>Reihenfolge</strong> per Drag &amp; Drop ändern</li> <li><strong>Hinzufügen</strong> aus der Dropdown-Liste aller konfigurierten Skripte</li> <li><strong>Entfernen</strong> einzelner Skripte aus der aktuellen Auswahl</li> <li>Skripte können pro Job unterschiedlich gewählt werden</li> </ul> <hr> <h2 id=skript-testen>Skript testen<a class=headerlink href=#skript-testen title="Permanent link">&para;</a></h2> <p>Über die Einstellungen kann jedes Skript mit einem Test-Job ausgeführt werden:</p> <div class="language-http highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a><span class=err>POST /api/settings/scripts/:scriptId/test</span>
</span></code></pre></div> <p>Der Test-Aufruf befüllt die Umgebungsvariablen mit Platzhalter-Werten.</p> <hr> <h2 id=ausfuhrungs-ergebnis>Ausführungs-Ergebnis<a class=headerlink href=#ausfuhrungs-ergebnis title="Permanent link">&para;</a></h2> <p>Das Ergebnis der Skript-Ausführung wird im Job-Datensatz gespeichert und in der History angezeigt:</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a><span class=p>{</span>
</span><span id=__span-5-2><a id=__codelineno-5-2 name=__codelineno-5-2 href=#__codelineno-5-2></a><span class=w> </span><span class=nt>&quot;postEncodeScripts&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-5-3><a id=__codelineno-5-3 name=__codelineno-5-3 href=#__codelineno-5-3></a><span class=w> </span><span class=nt>&quot;configured&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>2</span><span class=p>,</span>
</span><span id=__span-5-4><a id=__codelineno-5-4 name=__codelineno-5-4 href=#__codelineno-5-4></a><span class=w> </span><span class=nt>&quot;attempted&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>2</span><span class=p>,</span>
</span><span id=__span-5-5><a id=__codelineno-5-5 name=__codelineno-5-5 href=#__codelineno-5-5></a><span class=w> </span><span class=nt>&quot;succeeded&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>2</span><span class=p>,</span>
</span><span id=__span-5-6><a id=__codelineno-5-6 name=__codelineno-5-6 href=#__codelineno-5-6></a><span class=w> </span><span class=nt>&quot;failed&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>0</span><span class=p>,</span>
</span><span id=__span-5-7><a id=__codelineno-5-7 name=__codelineno-5-7 href=#__codelineno-5-7></a><span class=w> </span><span class=nt>&quot;skipped&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>0</span><span class=p>,</span>
</span><span id=__span-5-8><a id=__codelineno-5-8 name=__codelineno-5-8 href=#__codelineno-5-8></a><span class=w> </span><span class=nt>&quot;aborted&quot;</span><span class=p>:</span><span class=w> </span><span class=kc>false</span><span class=p>,</span>
</span><span id=__span-5-9><a id=__codelineno-5-9 name=__codelineno-5-9 href=#__codelineno-5-9></a><span class=w> </span><span class=nt>&quot;results&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-5-10><a id=__codelineno-5-10 name=__codelineno-5-10 href=#__codelineno-5-10></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-5-11><a id=__codelineno-5-11 name=__codelineno-5-11 href=#__codelineno-5-11></a><span class=w> </span><span class=nt>&quot;scriptId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>1</span><span class=p>,</span>
</span><span id=__span-5-12><a id=__codelineno-5-12 name=__codelineno-5-12 href=#__codelineno-5-12></a><span class=w> </span><span class=nt>&quot;scriptName&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Zu Plex verschieben&quot;</span><span class=p>,</span>
</span><span id=__span-5-13><a id=__codelineno-5-13 name=__codelineno-5-13 href=#__codelineno-5-13></a><span class=w> </span><span class=nt>&quot;status&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;SUCCESS&quot;</span>
</span><span id=__span-5-14><a id=__codelineno-5-14 name=__codelineno-5-14 href=#__codelineno-5-14></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-5-15><a id=__codelineno-5-15 name=__codelineno-5-15 href=#__codelineno-5-15></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-5-16><a id=__codelineno-5-16 name=__codelineno-5-16 href=#__codelineno-5-16></a><span class=w> </span><span class=nt>&quot;scriptId&quot;</span><span class=p>:</span><span class=w> </span><span class=mi>2</span><span class=p>,</span>
</span><span id=__span-5-17><a id=__codelineno-5-17 name=__codelineno-5-17 href=#__codelineno-5-17></a><span class=w> </span><span class=nt>&quot;scriptName&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Webhook auslösen&quot;</span><span class=p>,</span>
</span><span id=__span-5-18><a id=__codelineno-5-18 name=__codelineno-5-18 href=#__codelineno-5-18></a><span class=w> </span><span class=nt>&quot;status&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;SUCCESS&quot;</span>
</span><span id=__span-5-19><a id=__codelineno-5-19 name=__codelineno-5-19 href=#__codelineno-5-19></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-5-20><a id=__codelineno-5-20 name=__codelineno-5-20 href=#__codelineno-5-20></a><span class=w> </span><span class=p>]</span>
</span><span id=__span-5-21><a id=__codelineno-5-21 name=__codelineno-5-21 href=#__codelineno-5-21></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-5-22><a id=__codelineno-5-22 name=__codelineno-5-22 href=#__codelineno-5-22></a><span class=p>}</span>
</span></code></pre></div> <table> <thead> <tr> <th>Feld</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>configured</code></td> <td>Anzahl ausgewählter Skripte</td> </tr> <tr> <td><code>attempted</code></td> <td>Anzahl tatsächlich gestarteter Skripte</td> </tr> <tr> <td><code>succeeded</code></td> <td>Erfolgreich ausgeführt (Exit-Code 0)</td> </tr> <tr> <td><code>failed</code></td> <td>Fehlgeschlagen</td> </tr> <tr> <td><code>skipped</code></td> <td>Nicht ausgeführt (wegen vorherigem Fehler)</td> </tr> <tr> <td><code>aborted</code></td> <td><code>true</code>, wenn die Kette abgebrochen wurde</td> </tr> </tbody> </table> <hr> <h2 id=api-referenz>API-Referenz<a class=headerlink href=#api-referenz title="Permanent link">&para;</a></h2> <p>Eine vollständige API-Dokumentation der Skript-Endpunkte findest du unter:</p> <p><a href=../../api/settings/#skript-verwaltung><span class=twemoji><svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13.22 19.03a.75.75 0 0 1 0-1.06L18.19 13H3.75a.75.75 0 0 1 0-1.5h14.44l-4.97-4.97a.749.749 0 0 1 .326-1.275.75.75 0 0 1 .734.215l6.25 6.25a.75.75 0 0 1 0 1.06l-6.25 6.25a.75.75 0 0 1-1.06 0"/></svg></span> Settings API – Skripte</a></p> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingebeLine truncated
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/pipeline/post-encode-scripts/ rel=canonical><link href=../playlist-analysis/ rel=prev><link href=../../api/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Encode-Skripte (Pre & Post) - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#encode-skripte-pre-post class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Encode-Skripte (Pre & Post) </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg Line truncated
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a> -&gt; Pre-Encode Skripte/Ketten
</span><span id=__span-0-3><a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a> -&gt; HandBrake Encoding
</span><span id=__span-0-4><a id=__codelineno-0-4 name=__codelineno-0-4 href=#__codelineno-0-4></a> -&gt; Post-Encode Skripte/Ketten
</span><span id=__span-0-5><a id=__codelineno-0-5 name=__codelineno-0-5 href=#__codelineno-0-5></a> -&gt; FINISHED oder ERROR
</span></code></pre></div> <hr> <h2 id=auswahl-im-review>Auswahl im Review<a class=headerlink href=#auswahl-im-review title="Permanent link">&para;</a></h2> <p>Im Review-Panel kannst du getrennt wählen:</p> <ul> <li><code>selectedPreEncodeScriptIds</code></li> <li><code>selectedPostEncodeScriptIds</code></li> <li><code>selectedPreEncodeChainIds</code></li> <li><code>selectedPostEncodeChainIds</code></li> </ul> <hr> <h2 id=fehlerverhalten>Fehlerverhalten<a class=headerlink href=#fehlerverhalten title="Permanent link">&para;</a></h2> <ul> <li>Pre-Encode-Fehler stoppen die Kette und führen zu <code>ERROR</code>.</li> <li>Post-Encode-Fehler stoppen die restlichen Post-Schritte; Job kann dennoch <code>FINISHED</code> sein (mit Fehlerzusatz im Status/Log).</li> </ul> <hr> <h2 id=verfugbare-umgebungsvariablen>Verfügbare Umgebungsvariablen<a class=headerlink href=#verfugbare-umgebungsvariablen title="Permanent link">&para;</a></h2> <p>Beim Script-Run werden gesetzt:</p> <ul> <li><code>RIPSTER_SCRIPT_RUN_AT</code></li> <li><code>RIPSTER_JOB_ID</code></li> <li><code>RIPSTER_JOB_TITLE</code></li> <li><code>RIPSTER_MODE</code></li> <li><code>RIPSTER_INPUT_PATH</code></li> <li><code>RIPSTER_OUTPUT_PATH</code></li> <li><code>RIPSTER_RAW_PATH</code></li> <li><code>RIPSTER_SCRIPT_ID</code></li> <li><code>RIPSTER_SCRIPT_NAME</code></li> <li><code>RIPSTER_SCRIPT_SOURCE</code></li> </ul> <hr> <h2 id=skript-ketten>Skript-Ketten<a class=headerlink href=#skript-ketten title="Permanent link">&para;</a></h2> <p>Ketten unterstützen zwei Step-Typen:</p> <ul> <li><code>script</code> (führt ein hinterlegtes Skript aus)</li> <li><code>wait</code> (wartet <code>waitSeconds</code>)</li> </ul> <p>Bei Fehler in einem Script-Step wird die Kette abgebrochen.</p> <hr> <h2 id=testlaufe>Testläufe<a class=headerlink href=#testlaufe title="Permanent link">&para;</a></h2> <ul> <li>Skript testen: <code>POST /api/settings/scripts/:id/test</code></li> <li>Kette testen: <code>POST /api/settings/script-chains/:id/test</code></li> </ul> <p>Ergebnisse enthalten Erfolg/Exit-Code, Laufzeit und stdout/stderr.</p> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-Line truncated
+15 -61
View File
@@ -1,61 +1,15 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/pipeline/workflow/ rel=canonical><link href=../ rel=prev><link href=../encoding/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Workflow & Zustände - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#workflow-zustande class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Workflow & Zustände </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0Line truncated
START(( )) --&gt; IDLE
IDLE --&gt;|Disc erkannt| DD[DISC_DETECTED]
DD --&gt;|Analyse starten| META[METADATA\nSELECTION]
META --&gt;|Metadaten übernommen| RTS[READY_TO\nSTART]
META --&gt;|vorhandenes RAW +\nPlaylist offen| WUD[WAITING_FOR\nUSER_DECISION]
RTS --&gt;|Auto-Start| RIP[RIPPING]
RTS --&gt;|Auto-Start mit RAW| MIC[MEDIAINFO\nCHECK]
RIP --&gt;|MKV fertig| MIC
RIP --&gt;|Fehler| ERR
RIP --&gt;|Abbruch| CAN([CANCELLED])
MIC --&gt;|Playlist offen (Backup)| WUD
WUD --&gt;|Playlist bestätigt| MIC
WUD --&gt;|Playlist bestätigt,\nnoch kein RAW| RTS
MIC --&gt; RTE[READY_TO\nENCODE]
RTE --&gt;|Encoding starten\n(bestätigt bei Bedarf automatisch)| ENC[ENCODING]
ENC --&gt;|inkl. Post-Skripte| FIN([FINISHED])
ENC --&gt;|Fehler| ERR
ENC --&gt;|Abbruch| CAN
ERR([ERROR]) --&gt;|Retry / Cancel| IDLE
CAN --&gt;|Retry / Neu-Analyse| IDLE
FIN --&gt;|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</code></pre> </div> <hr> <h2 id=ui-badge-bezeichnungen>UI-Badge-Bezeichnungen<a class=headerlink href=#ui-badge-bezeichnungen title="Permanent link">&para;</a></h2> <p>Die Status-Badges im Dashboard verwenden diese Labels:</p> <table> <thead> <tr> <th>State</th> <th>Badge-Label</th> </tr> </thead> <tbody> <tr> <td><code>IDLE</code></td> <td><code>Bereit</code></td> </tr> <tr> <td><code>DISC_DETECTED</code></td> <td><code>Medium erkannt</code></td> </tr> <tr> <td><code>METADATA_SELECTION</code></td> <td><code>Metadatenauswahl</code></td> </tr> <tr> <td><code>WAITING_FOR_USER_DECISION</code></td> <td><code>Warte auf Auswahl</code></td> </tr> <tr> <td><code>READY_TO_START</code></td> <td><code>Startbereit</code></td> </tr> <tr> <td><code>RIPPING</code></td> <td><code>Rippen</code></td> </tr> <tr> <td><code>MEDIAINFO_CHECK</code></td> <td><code>Mediainfo-Pruefung</code></td> </tr> <tr> <td><code>READY_TO_ENCODE</code></td> <td><code>Bereit zum Encodieren</code></td> </tr> <tr> <td><code>ENCODING</code></td> <td><code>Encodieren</code></td> </tr> <tr> <td><code>FINISHED</code></td> <td><code>Fertig</code></td> </tr> <tr> <td><code>CANCELLED</code></td> <td><code>Abgebrochen</code></td> </tr> <tr> <td><code>ERROR</code></td> <td><code>Fehler</code></td> </tr> <tr> <td>Queue (kein eigener State)</td> <td><code>In der Queue</code></td> </tr> </tbody> </table> <hr> <h2 id=zustandsbeschreibungen>Zustandsbeschreibungen<a class=headerlink href=#zustandsbeschreibungen title="Permanent link">&para;</a></h2> <h3 id=idle>IDLE<a class=headerlink href=#idle title="Permanent link">&para;</a></h3> <p><strong>Ausgangszustand.</strong> Ripster wartet auf eine Disc.</p> <ul> <li><code>diskDetectionService</code> pollt das Laufwerk im konfigurierten Intervall</li> <li>Bei Disc-Erkennung: automatischer Übergang zu <code>DISC_DETECTED</code></li> <li>WebSocket-Event: <code>DISC_DETECTED</code></li> </ul> <hr> <h3 id=disc_detected>DISC_DETECTED<a class=headerlink href=#disc_detected title="Permanent link">&para;</a></h3> <p><strong>Disc erkannt, wartet auf Benutzeraktion.</strong></p> <ul> <li>Dashboard-Badge: <strong>"Medium erkannt"</strong></li> <li>Status-Text: <strong>"Neue Disk erkannt"</strong></li> <li><strong>"Analyse starten"</strong>-Button wird aktiv</li> <li>Kein Prozess läuft noch</li> </ul> <p><strong>Übergang:</strong> Benutzer klickt "Analyse starten" → <code>METADATA_SELECTION</code></p> <hr> <h3 id=metadata_selection>METADATA_SELECTION<a class=headerlink href=#metadata_selection title="Permanent link">&para;</a></h3> <p><strong>Metadaten-Auswahl läuft.</strong></p> <ol> <li>Job wird erstellt (<code>status = METADATA_SELECTION</code>)</li> <li>OMDb-Vorsuche mit erkanntem Disc-Label</li> <li><code>MetadataSelectionDialog</code> öffnet sich mit vorgeladenen Ergebnissen</li> <li>Benutzer wählt Filmtitel (oder gibt manuell ein)</li> <li>Nach Bestätigung wird der Job automatisch für Start/Queue vorbereitet (<code>selectMetadata</code> + <code>startPreparedJob</code>)</li> </ol> <p><strong>Übergang (automatisch nach Metadaten-Bestätigung):</strong></p> <table> <thead> <tr> <th>Ergebnis</th> <th>Nächster Zustand</th> </tr> </thead> <tbody> <tr> <td>Kein verwertbares RAW vorhanden</td> <td><code>READY_TO_START</code> → automatisch <code>RIPPING</code> (oder Queue)</td> </tr> <tr> <td>Verwertbares RAW vorhanden</td> <td><code>READY_TO_START</code> → automatisch <code>MEDIAINFO_CHECK</code> (oder Queue)</td> </tr> <tr> <td>Vorhandenes RAW + offene Playlist-Entscheidung</td> <td><code>WAITING_FOR_USER_DECISION</code></td> </tr> </tbody> </table> <hr> <h3 id=waiting_for_user_decision>WAITING_FOR_USER_DECISION<a class=headerlink href=#waiting_for_user_decision title="Permanent link">&para;</a></h3> <p><strong>Playlist-Obfuskierung erkannt – manuelle Auswahl erforderlich.</strong></p> <div class="admonition info"> <p class=admonition-title>Neu seit „Skript Integration + UI Anpassungen"</p> <p>Dieser Zustand wurde eingeführt, um Blu-rays mit mehreren Playlists ähnlicher Länge korrekt zu behandeln.</p> </div> <ul> <li>Playlist-Auswahl-Dialog wird im Dashboard angezeigt</li> <li>Alle Kandidaten mit Score, Laufzeit und Bewertungslabel</li> <li>Empfohlene Playlist ist vorausgewählt</li> <li>Benutzer bestätigt mit <strong>"Playlist übernehmen"</strong></li> <li>Tritt häufig nach <code>MEDIAINFO_CHECK</code> auf (Backup-Analyse), seltener direkt nach <code>METADATA_SELECTION</code> bei vorhandenem RAW</li> </ul> <p><strong>Darstellung im Dashboard:</strong></p> <div class="language-text highlight"><pre><span></span><code><span id=__span-0-1><a id=__codelineno-0-1 name=__codelineno-0-1 href=#__codelineno-0-1></a>┌──────────────────────────────────────────────────────────┐
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a>│ Playlist-Auswahl erforderlich │
</span><span id=__span-0-3><a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a>│ Es wurden mehrere Titel mit ähnlicher Laufzeit gefunden. │
</span><span id=__span-0-4><a id=__codelineno-0-4 name=__codelineno-0-4 href=#__codelineno-0-4></a>├──────────┬──────────┬────────┬──────────────────────────┤
</span><span id=__span-0-5><a id=__codelineno-0-5 name=__codelineno-0-5 href=#__codelineno-0-5></a>│ Playlist │ Laufzeit │ Score │ Bewertung │
</span><span id=__span-0-6><a id=__codelineno-0-6 name=__codelineno-0-6 href=#__codelineno-0-6></a>├──────────┼──────────┼────────┼──────────────────────────┤
</span><span id=__span-0-7><a id=__codelineno-0-7 name=__codelineno-0-7 href=#__codelineno-0-7></a>│ ● 00800 │ 2:28:05 │ +18 │ wahrscheinlich korrekt │
</span><span id=__span-0-8><a id=__codelineno-0-8 name=__codelineno-0-8 href=#__codelineno-0-8></a>│ ○ 00801 │ 2:28:12 │ −4 │ Auffällige Segmentfolge │
</span><span id=__span-0-9><a id=__codelineno-0-9 name=__codelineno-0-9 href=#__codelineno-0-9></a>│ ○ 00900 │ 2:28:05 │ −32 │ Fake-Struktur │
</span><span id=__span-0-10><a id=__codelineno-0-10 name=__codelineno-0-10 href=#__codelineno-0-10></a>└──────────┴──────────┴────────┴──────────────────────────┘
</span><span id=__span-0-11><a id=__codelineno-0-11 name=__codelineno-0-11 href=#__codelineno-0-11></a> [Playlist übernehmen]
</span></code></pre></div> <p><strong>Übergang:</strong> <code>selectMetadata(jobId, { selectedPlaylist })</code> setzt die Pipeline automatisch fort:</p> <ul> <li>mit vorhandenem RAW nach <code>MEDIAINFO_CHECK</code></li> <li>ohne RAW über <code>READY_TO_START</code> weiter Richtung <code>RIPPING</code></li> </ul> <p>Mehr Details: <a href=../playlist-analysis/ >Playlist-Analyse</a></p> <hr> <h3 id=ready_to_start>READY_TO_START<a class=headerlink href=#ready_to_start title="Permanent link">&para;</a></h3> <p><strong>Übergangs-/Fallback-Zustand vor dem eigentlichen Start.</strong></p> <ul> <li>Wird nach Metadaten-Bestätigung kurz gesetzt</li> <li><code>startPreparedJob()</code> wird danach automatisch ausgeführt</li> <li>Wenn Parallel-Limit erreicht ist, wird der Start stattdessen in die Queue eingereiht</li> <li><strong>"Job starten"</strong> ist primär für Sonderfälle/Fallback sichtbar</li> </ul> <p><strong>Sonderfall – RAW-Datei bereits vorhanden:</strong> Wenn für diesen Job bereits ein verwertbares RAW unter <code>raw_dir</code> existiert, wird Ripping übersprungen und direkt <code>MEDIAINFO_CHECK</code> gestartet.</p> <p><strong>Übergang:</strong> <code>startPreparedJob(jobId)</code> → <code>RIPPING</code> oder direkt <code>MEDIAINFO_CHECK</code></p> <hr> <h3 id=ripping>RIPPING<a class=headerlink href=#ripping title="Permanent link">&para;</a></h3> <p><strong>MakeMKV rippt die Disc.</strong></p> <div class="tabbed-set tabbed-alternate" data-tabs=1:2><input checked=checked id=__tabbed_1_1 name=__tabbed_1 type=radio><input id=__tabbed_1_2 name=__tabbed_1 type=radio><div class=tabbed-labels><label for=__tabbed_1_1>MKV-Modus (Standard)</label><label for=__tabbed_1_2>Backup-Modus</label></div> <div class=tabbed-content> <div class=tabbed-block> <div class="language-bash highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a>makemkvcon<span class=w> </span>mkv<span class=w> </span>disc:0<span class=w> </span>all<span class=w> </span>/path/to/raw/<span class=w> </span>--minlength<span class=o>=</span><span class=m>900</span><span class=w> </span>-r
</span></code></pre></div> <p>Erstellt MKV-Datei(en) direkt aus den gewählten Titeln.</p> </div> <div class=tabbed-block> <div class="language-bash highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a>makemkvcon<span class=w> </span>backup<span class=w> </span>disc:0<span class=w> </span>/path/to/raw/backup/<span class=w> </span>--decrypt<span class=w> </span>-r
</span></code></pre></div> <p>Erstellt vollständiges Disc-Backup inkl. Menüs.</p> </div> </div> </div> <p><strong>Live-Updates</strong> aus MakeMKV-Ausgabe:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a>PRGV:2048,0,65536 → Fortschritt-Berechnung
</span><span id=__span-3-2><a id=__codelineno-3-2 name=__codelineno-3-2 href=#__codelineno-3-2></a>PRGT:5011,0,&quot;...&quot; → Aktueller Task-Name
</span></code></pre></div> <p><strong>Typische Dauer:</strong> DVD 20–45 min · Blu-ray 45–120 min</p> <hr> <h3 id=mediainfo_check>MEDIAINFO_CHECK<a class=headerlink href=#mediainfo_check title="Permanent link">&para;</a></h3> <p><strong>HandBrake-Scan und Encode-Plan-Erstellung.</strong></p> <p>Dieser Zustand umfasst je nach Quelle mehrere Phasen:</p> <ol> <li>Optional: Playlist-Auflösung bei Blu-ray-Backup (inkl. MakeMKV/HandBrake-Zuordnung)</li> <li><strong>HandBrake-Scan</strong> (<code>HandBrakeCLI --scan</code>) auf RAW-Input</li> <li><strong>Encode-Plan-Erstellung</strong> mit automatischer Track-Vorauswahl</li> </ol> <p>Kein Benutzereingriff – läuft automatisch durch.</p> <p><strong>Übergänge:</strong></p> <ul> <li>Eindeutige Quelle/Titelwahl möglich → <code>READY_TO_ENCODE</code></li> <li>Mehrdeutige Playlist erkannt → <code>WAITING_FOR_USER_DECISION</code></li> </ul> <hr> <h3 id=ready_to_encode>READY_TO_ENCODE<a class=headerlink href=#ready_to_encode title="Permanent link">&para;</a></h3> <p><strong>Encode-Plan bereit.</strong></p> <p>Das <code>MediaInfoReviewPanel</code> zeigt:</p> <ul> <li><strong>Titel-Auswahl</strong> (bei Discs mit mehreren langen Titeln)</li> <li><strong>Audio-Tracks</strong> mit Encoder-Vorschau (Copy/Transcode/Fallback)</li> <li><strong>Untertitel-Tracks</strong> mit Flags (Einbrennen, Forced, Default)</li> <li><strong>Post-Encode-Skripte</strong> – Auswahl und Reihenfolge der auszuführenden Skripte</li> </ul> <p>Im Frontend startet <strong>"Encoding starten"</strong> (bzw. <strong>"Backup + Encoding starten"</strong> im Pre-Rip-Modus) den nächsten Schritt. Falls die Review noch nicht bestätigt wurde, wird <code>confirmEncodeReview(...)</code> automatisch vor dem Start aufgerufen.</p> <p><strong>Übergang:</strong> <code>startPreparedJob(jobId)</code> → <code>ENCODING</code> (oder im Pre-Rip-Fall zuerst <code>RIPPING</code>)</p> <hr> <h3 id=encoding>ENCODING<a class=headerlink href=#encoding title="Permanent link">&para;</a></h3> <p><strong>HandBrake encodiert die Datei.</strong></p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a>HandBrakeCLI<span class=w> </span><span class=se>\</span>
</span><span id=__span-4-2><a id=__codelineno-4-2 name=__codelineno-4-2 href=#__codelineno-4-2></a><span class=w> </span>-i<span class=w> </span>&lt;quelle&gt;<span class=w> </span>-o<span class=w> </span>&lt;ziel&gt;<span class=w> </span><span class=se>\</span>
</span><span id=__span-4-3><a id=__codelineno-4-3 name=__codelineno-4-3 href=#__codelineno-4-3></a><span class=w> </span>-t<span class=w> </span>&lt;titelId&gt;<span class=w> </span><span class=se>\</span>
</span><span id=__span-4-4><a id=__codelineno-4-4 name=__codelineno-4-4 href=#__codelineno-4-4></a><span class=w> </span>--preset<span class=w> </span><span class=s2>&quot;H.265 MKV 1080p30&quot;</span><span class=w> </span><span class=se>\</span>
</span><span id=__span-4-5><a id=__codelineno-4-5 name=__codelineno-4-5 href=#__codelineno-4-5></a><span class=w> </span>-a<span class=w> </span><span class=m>1</span>,2<span class=w> </span>-E<span class=w> </span>copy:ac3,av_aac<span class=w> </span><span class=se>\</span>
</span><span id=__span-4-6><a id=__codelineno-4-6 name=__codelineno-4-6 href=#__codelineno-4-6></a><span class=w> </span>-s<span class=w> </span><span class=m>1</span><span class=w> </span>--subtitle-default<span class=w> </span><span class=m>1</span>
</span></code></pre></div> <p><strong>Live-Updates</strong> aus HandBrake-stderr:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a>Encoding: task 1 of 1, 73.50 % (45.23 fps, avg 44.12 fps, ETA 00h12m34s)
</span></code></pre></div> <p>Post-Encode-Skripte werden innerhalb dieses Zustands sequenziell ausgeführt (kein separater Pipeline-State).</p> <div class="admonition note"> <p class=admonition-title>Skriptfehler</p> <p>Skriptfehler führen zum Abbruch der Skriptkette, der Job bleibt jedoch im Abschlusszustand <code>FINISHED</code> mit entsprechendem Hinweis im Status-Text/Log.</p> </div> <hr> <h3 id=finished>FINISHED<a class=headerlink href=#finished title="Permanent link">&para;</a></h3> <p><strong>Job erfolgreich abgeschlossen.</strong></p> <ul> <li>Ausgabedatei liegt im konfigurierten <code>movie_dir</code></li> <li>Job-Status in Datenbank: <code>FINISHED</code></li> <li>PushOver-Benachrichtigung (falls konfiguriert)</li> <li>WebSocket-Event: <code>PIPELINE_STATE_CHANGED</code> (State <code>FINISHED</code>)</li> </ul> <hr> <h3 id=cancelled>CANCELLED<a class=headerlink href=#cancelled title="Permanent link">&para;</a></h3> <p><strong>Job wurde vom Benutzer abgebrochen.</strong></p> <ul> <li>Entsteht bei aktivem Abbruch (<code>/api/pipeline/cancel</code>) während laufender Phase</li> <li>Job-Status in Datenbank: <code>CANCELLED</code></li> <li>Im Dashboard stehen danach u. a. <code>Retry Rippen</code>, <code>Review neu starten</code> oder <code>Encode neu starten</code> (kontextabhängig) zur Verfügung</li> </ul> <hr> <h3 id=error>ERROR<a class=headerlink href=#error title="Permanent link">&para;</a></h3> <p><strong>Fehler aufgetreten.</strong></p> <ul> <li>Fehlerdetails im Job-Datensatz gespeichert</li> <li>Fehler-Logs in History abrufbar</li> <li><strong>Retry</strong>: Neustart vom Fehlerzustand</li> <li><strong>Neu analysieren</strong>: Disc erneut als neuer Job starten</li> </ul> <hr> <h2 id=abbrechen-retry>Abbrechen &amp; Retry<a class=headerlink href=#abbrechen-retry title="Permanent link">&para;</a></h2> <h3 id=pipeline-abbrechen>Pipeline abbrechen<a class=headerlink href=#pipeline-abbrechen title="Permanent link">&para;</a></h3> <div class="language-http highlight"><pre><span></span><code><span id=__span-6-1><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a><span class=err>POST /api/pipeline/cancel</span>
</span></code></pre></div> <ul> <li>SIGINT → graceful exit (Timeout: 10 s) → SIGKILL</li> <li>Laufender Job landet in <code>CANCELLED</code> (oder Queue-Eintrag wird entfernt, falls noch nicht gestartet)</li> </ul> <h3 id=job-wiederholen>Job wiederholen<a class=headerlink href=#job-wiederholen title="Permanent link">&para;</a></h3> <div class="language-http highlight"><pre><span></span><code><span id=__span-7-1><a id=__codelineno-7-1 name=__codelineno-7-1 href=#__codelineno-7-1></a><span class=err>POST /api/pipeline/retry/:jobId</span>
</span></code></pre></div> <ul> <li>Startet den Job neu in <code>RIPPING</code> (oder reiht den Retry in die Queue ein)</li> <li>Metadaten bleiben erhalten; Encode-/Scan-Daten werden neu erzeugt</li> </ul> <h3 id=re-encode>Re-Encode<a class=headerlink href=#re-encode title="Permanent link">&para;</a></h3> <div class="language-http highlight"><pre><span></span><code><span id=__span-8-1><a id=__codelineno-8-1 name=__codelineno-8-1 href=#__codelineno-8-1></a><span class=err>POST /api/pipeline/reencode/:jobId</span>
</span></code></pre></div> <ul> <li>Encodiert bestehende Raw-MKV neu</li> <li>Ermöglicht neue Track-Auswahl und andere Skripte</li> <li>Kein Ripping erforderlich</li> </ul> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/pipeline/workflow/ rel=canonical><link href=../ rel=prev><link href=../encoding/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Workflow & Zustände - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#workflow-zustande class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Workflow & Zustände </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0Line truncated
IDLE --&gt; DISC_DETECTED
DISC_DETECTED --&gt; ANALYZING
ANALYZING --&gt; METADATA_SELECTION
METADATA_SELECTION --&gt; READY_TO_START
READY_TO_START --&gt; RIPPING
READY_TO_START --&gt; MEDIAINFO_CHECK
MEDIAINFO_CHECK --&gt; WAITING_FOR_USER_DECISION
WAITING_FOR_USER_DECISION --&gt; MEDIAINFO_CHECK
MEDIAINFO_CHECK --&gt; READY_TO_ENCODE
READY_TO_ENCODE --&gt; ENCODING
ENCODING --&gt; FINISHED
ENCODING --&gt; ERROR
RIPPING --&gt; ERROR
RIPPING --&gt; CANCELLED</code></pre> <hr> <h2 id=state-liste>State-Liste<a class=headerlink href=#state-liste title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>State</th> <th>Bedeutung</th> </tr> </thead> <tbody> <tr> <td><code>IDLE</code></td> <td>Wartet auf Disc</td> </tr> <tr> <td><code>DISC_DETECTED</code></td> <td>Disc erkannt</td> </tr> <tr> <td><code>ANALYZING</code></td> <td>MakeMKV-Analyse läuft</td> </tr> <tr> <td><code>METADATA_SELECTION</code></td> <td>Benutzer wählt Metadaten</td> </tr> <tr> <td><code>WAITING_FOR_USER_DECISION</code></td> <td>Playlist-Auswahl nötig</td> </tr> <tr> <td><code>READY_TO_START</code></td> <td>Übergangszustand vor Start</td> </tr> <tr> <td><code>RIPPING</code></td> <td>MakeMKV-Rip läuft</td> </tr> <tr> <td><code>MEDIAINFO_CHECK</code></td> <td>Quelle/Tracks werden ausgewertet</td> </tr> <tr> <td><code>READY_TO_ENCODE</code></td> <td>Review ist bereit</td> </tr> <tr> <td><code>ENCODING</code></td> <td>HandBrake läuft</td> </tr> <tr> <td><code>FINISHED</code></td> <td>erfolgreich abgeschlossen</td> </tr> <tr> <td><code>CANCELLED</code></td> <td>abgebrochen</td> </tr> <tr> <td><code>ERROR</code></td> <td>fehlgeschlagen</td> </tr> </tbody> </table> <hr> <h2 id=typische-pfade>Typische Pfade<a class=headerlink href=#typische-pfade title="Permanent link">&para;</a></h2> <h3 id=standardfall-kein-vorhandenes-raw>Standardfall (kein vorhandenes RAW)<a class=headerlink href=#standardfall-kein-vorhandenes-raw title="Permanent link">&para;</a></h3> <ol> <li>Disc erkannt</li> <li>Analyse + Metadaten</li> <li><code>RIPPING</code></li> <li><code>MEDIAINFO_CHECK</code></li> <li><code>READY_TO_ENCODE</code></li> <li><code>ENCODING</code></li> <li><code>FINISHED</code></li> </ol> <h3 id=vorhandenes-raw>Vorhandenes RAW<a class=headerlink href=#vorhandenes-raw title="Permanent link">&para;</a></h3> <p><code>READY_TO_START</code> springt direkt zu <code>MEDIAINFO_CHECK</code> (kein neuer Rip).</p> <h3 id=mehrdeutige-blu-ray-playlist>Mehrdeutige Blu-ray-Playlist<a class=headerlink href=#mehrdeutige-blu-ray-playlist title="Permanent link">&para;</a></h3> <p><code>MEDIAINFO_CHECK</code> -&gt; <code>WAITING_FOR_USER_DECISION</code> bis Benutzer Playlist bestätigt.</p> <hr> <h2 id=queue-verhalten>Queue-Verhalten<a class=headerlink href=#queue-verhalten title="Permanent link">&para;</a></h2> <p>Wenn <code>pipeline_max_parallel_jobs</code> erreicht ist:</p> <ul> <li>Job-Aktionen werden als Queue-Einträge abgelegt</li> <li>Queue kann zusätzlich Nicht-Job-Einträge enthalten (<code>script</code>, <code>chain</code>, <code>wait</code>)</li> <li>Reihenfolge ist per API/UI änderbar</li> </ul> <hr> <h2 id=abbruch-retry-restart>Abbruch, Retry, Restart<a class=headerlink href=#abbruch-retry-restart title="Permanent link">&para;</a></h2> <ul> <li><code>cancel</code>: laufenden Job abbrechen oder Queue-Eintrag entfernen</li> <li><code>retry</code>: Fehler-/Abbruch-Job neu starten</li> <li><code>reencode</code>: aus vorhandenem RAW neu encodieren</li> <li><code>restart-review</code>: Review aus RAW neu aufbauen</li> <li><code>restart-encode</code>: Encoding mit letzter bestätigter Auswahl neu starten</li> </ul> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.Line truncated
+1 -1
View File
@@ -1 +1 @@
{"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":"<p>Halbautomatische Disc-Ripping-Plattform f\u00fcr DVDs und Blu-rays</p> <ul> <li> <p> Automatisiertes Ripping</p> <p>Disc einlegen \u2013 Ripster erkennt sie automatisch und startet den Analyse-Workflow mit MakeMKV.</p> <p> Workflow verstehen</p> </li> <li> <p> Metadata-Integration</p> <p>Automatische Suche in der OMDb-Datenbank f\u00fcr Filmtitel, Poster und IMDb-IDs.</p> <p> Konfiguration</p> </li> <li> <p> Flexibles Encoding</p> <p>HandBrake-Encoding mit individueller Track-Auswahl f\u00fcr Audio- und Untertitelspuren.</p> <p> Encode-Planung</p> </li> <li> <p> Job-Historie</p> <p>Vollst\u00e4ndiges Audit-Trail aller Ripping-Jobs mit Logs und Re-Encode-Funktion.</p> <p> History API</p> </li> </ul>"},{"location":"#was-ist-ripster","title":"Was ist Ripster?","text":"<p>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:</p> <pre><code>Disc einlegen \u2192 Erkennung \u2192 Analyse \u2192 Metadaten w\u00e4hlen \u2192 Rippen \u2192 Encodieren \u2192 Fertig\n</code></pre>"},{"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 <ul> <li>Node.js &gt;= 20.19.0 mit Express.js</li> <li>SQLite3 mit automatischen Schema-Migrationen</li> <li>WebSocket (<code>ws</code>) f\u00fcr Echtzeit-Kommunikation</li> <li>Externe CLI-Tools: <code>makemkvcon</code>, <code>HandBrakeCLI</code>, <code>mediainfo</code></li> </ul> <ul> <li>React 18.3.1 mit React Router</li> <li>Vite 5.4.12 als Build-Tool</li> <li>PrimeReact 10.9.2 als UI-Bibliothek</li> <li>WebSocket-Client f\u00fcr Live-Updates</li> </ul> Tool Zweck <code>makemkvcon</code> Disc-Analyse &amp; MKV/Backup-Ripping <code>HandBrakeCLI</code> Video-Encoding <code>mediainfo</code> Track-Informationen aus gerippten Dateien OMDb API Filmmetadaten (Titel, Poster, IMDb-ID)"},{"location":"#schnellstart","title":"Schnellstart","text":"<pre><code># 1. Repository klonen\ngit clone https://github.com/YOUR_GITHUB_USERNAME/ripster.git\ncd ripster\n\n# 2. Starten (Node.js &gt;= 20 erforderlich)\n./start.sh\n\n# 3. Browser \u00f6ffnen\nopen http://localhost:5173\n</code></pre> <p>Erste Schritte</p> <p>Die vollst\u00e4ndige Installationsanleitung mit allen Voraussetzungen findest du unter Erste Schritte.</p>"},{"location":"#pipeline-uberblick","title":"Pipeline-\u00dcberblick","text":"<pre><code>flowchart LR\n IDLE --&gt; DD[DISC_DETECTED]\n DD --&gt; META[METADATA\\nSELECTION]\n META --&gt; RTS[READY_TO\\nSTART]\n RTS --&gt;|Auto-Start| RIP[RIPPING]\n RTS --&gt;|Auto-Start mit RAW| MIC\n RIP --&gt; MIC[MEDIAINFO\\nCHECK]\n MIC --&gt;|Playlist offen (Backup)| WUD[WAITING_FOR\\nUSER_DECISION]\n WUD --&gt; MIC\n MIC --&gt; RTE[READY_TO\\nENCODE]\n RTE --&gt; ENC[ENCODING]\n ENC --&gt;|inkl. Post-Skripte| FIN([FINISHED])\n ENC --&gt; ERR([ERROR])\n RIP --&gt; ERR\n\n style FIN fill:#e8f5e9,stroke:#66bb6a,color:#2e7d32\n style ERR fill:#ffebee,stroke:#ef5350,color:#c62828\n style WUD fill:#fff8e1,stroke:#ffa726,color:#e65100\n style ENC fill:#f3e5f5,stroke:#ab47bc,color:#6a1b9a</code></pre> <p><code>READY_TO_START</code> ist in der Praxis meist ein kurzer \u00dcbergangszustand: der Job wird nach Metadaten-Auswahl automatisch gestartet oder in die Queue eingeplant.</p>"},{"location":"api/","title":"API-Referenz","text":"<p>Ripster bietet eine REST-API f\u00fcr alle Operationen sowie einen WebSocket-Endpunkt f\u00fcr Echtzeit-Updates.</p>"},{"location":"api/#basis-url","title":"Basis-URL","text":"<pre><code>http://localhost:3001\n</code></pre> <p>Konfigurierbar \u00fcber die Umgebungsvariable <code>PORT</code>.</p>"},{"location":"api/#api-gruppen","title":"API-Gruppen","text":"<ul> <li> <p> Pipeline API</p> <p>Pipeline-Steuerung: Analyse starten, Metadaten setzen, Ripping und Encoding steuern.</p> <p> Pipeline API</p> </li> <li> <p> Settings API</p> <p>Einstellungen lesen und schreiben.</p> <p> Settings API</p> </li> <li> <p> History API</p> <p>Job-Geschichte abfragen, Jobs l\u00f6schen, Orphan-Ordner importieren.</p> <p> History API</p> </li> <li> <p> WebSocket Events</p> <p>Line truncated
{"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":"<p>Halbautomatische Disc-Ripping-Plattform f\u00fcr DVDs und Blu-rays</p> <ul> <li> <p> Automatisiertes Ripping</p> <p>Disc einlegen \u2013 Ripster erkennt sie automatisch und startet den Analyse-Workflow mit MakeMKV.</p> <p> Workflow verstehen</p> </li> <li> <p> Metadata-Integration</p> <p>Automatische Suche in der OMDb-Datenbank f\u00fcr Filmtitel, Poster und IMDb-IDs.</p> <p> Konfiguration</p> </li> <li> <p> Flexibles Encoding</p> <p>HandBrake-Encoding mit individueller Track-Auswahl f\u00fcr Audio- und Untertitelspuren.</p> <p> Encode-Planung</p> </li> <li> <p> Job-Historie</p> <p>Vollst\u00e4ndiges Audit-Trail aller Ripping-Jobs mit Logs und Re-Encode-Funktion.</p> <p> History API</p> </li> </ul>"},{"location":"#was-ist-ripster","title":"Was ist Ripster?","text":"<p>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:</p> <pre><code>Disc einlegen \u2192 Erkennung \u2192 Analyse \u2192 Metadaten w\u00e4hlen \u2192 Rippen \u2192 Encodieren \u2192 Fertig\n</code></pre>"},{"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 <ul> <li>Node.js &gt;= 20.19.0 mit Express.js</li> <li>SQLite3 mit automatischen Schema-Migrationen</li> <li>WebSocket (<code>ws</code>) f\u00fcr Echtzeit-Kommunikation</li> <li>Externe CLI-Tools: <code>makemkvcon</code>, <code>HandBrakeCLI</code>, <code>mediainfo</code></li> </ul> <ul> <li>React 18.3.1 mit React Router</li> <li>Vite 5.4.12 als Build-Tool</li> <li>PrimeReact 10.9.2 als UI-Bibliothek</li> <li>WebSocket-Client f\u00fcr Live-Updates</li> </ul> Tool Zweck <code>makemkvcon</code> Disc-Analyse &amp; MKV/Backup-Ripping <code>HandBrakeCLI</code> Video-Encoding <code>mediainfo</code> Track-Informationen aus gerippten Dateien OMDb API Filmmetadaten (Titel, Poster, IMDb-ID)"},{"location":"#schnellstart","title":"Schnellstart","text":"<pre><code># 1. Repository klonen\ngit clone https://github.com/YOUR_GITHUB_USERNAME/ripster.git\ncd ripster\n\n# 2. Starten (Node.js &gt;= 20 erforderlich)\n./start.sh\n\n# 3. Browser \u00f6ffnen\nopen http://localhost:5173\n</code></pre> <p>Erste Schritte</p> <p>Die vollst\u00e4ndige Installationsanleitung mit allen Voraussetzungen findest du unter Erste Schritte.</p>"},{"location":"#pipeline-uberblick","title":"Pipeline-\u00dcberblick","text":"<pre><code>flowchart LR\n IDLE --&gt; DD[DISC_DETECTED]\n DD --&gt; META[METADATA\\nSELECTION]\n META --&gt; RTS[READY_TO\\nSTART]\n RTS --&gt;|Auto-Start| RIP[RIPPING]\n RTS --&gt;|Auto-Start mit RAW| MIC\n RIP --&gt; MIC[MEDIAINFO\\nCHECK]\n MIC --&gt;|Playlist offen (Backup)| WUD[WAITING_FOR\\nUSER_DECISION]\n WUD --&gt; MIC\n MIC --&gt; RTE[READY_TO\\nENCODE]\n RTE --&gt; ENC[ENCODING]\n ENC --&gt;|inkl. Post-Skripte| FIN([FINISHED])\n ENC --&gt; ERR([ERROR])\n RIP --&gt; ERR\n\n style FIN fill:#e8f5e9,stroke:#66bb6a,color:#2e7d32\n style ERR fill:#ffebee,stroke:#ef5350,color:#c62828\n style WUD fill:#fff8e1,stroke:#ffa726,color:#e65100\n style ENC fill:#f3e5f5,stroke:#ab47bc,color:#6a1b9a</code></pre> <p><code>READY_TO_START</code> ist in der Praxis meist ein kurzer \u00dcbergangszustand: der Job wird nach Metadaten-Auswahl automatisch gestartet oder in die Queue eingeplant.</p>"},{"location":"api/","title":"API-Referenz","text":"<p>Ripster bietet eine REST-API f\u00fcr Steuerung/Verwaltung sowie einen WebSocket-Endpunkt f\u00fcr Echtzeit-Updates.</p>"},{"location":"api/#basis-url","title":"Basis-URL","text":"<pre><code>http://localhost:3001\n</code></pre> <p>API-Prefix: <code>/api</code></p> <p>Beispiele:</p> <ul> <li><code>GET /api/health</code></li> <li><code>GET /api/pipeline/state</code></li> </ul>"},{"location":"api/#api-gruppen","title":"API-Gruppen","text":"<ul> <li> <p> Health</p> <p>Service-Liveness.</p> <p><code>GET /api/health</code></p> </li> <li> <p> Pipeline API</p> <p>Analyse, Start/Retry/Cancel, Queue, Re-Encode.</p> <p> Pipeline API</p> </li> <li> <p> Settings API</p> <p>Einstellungen, Skripte/Ketten, User-Presets.</p> <p> Settings API</p> </li> <li> <p> History API</Line truncated
+35 -31
View File
@@ -2,126 +2,130 @@
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://mboehmlaender.github.io/ripster/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/api/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/api/crons/</loc>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/api/history/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/api/pipeline/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/api/settings/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/api/websocket/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/architecture/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/architecture/backend/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/architecture/database/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/architecture/frontend/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/architecture/overview/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/configuration/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/configuration/environment/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/configuration/settings-reference/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/deployment/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/deployment/development/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/deployment/production/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/getting-started/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/getting-started/configuration/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/getting-started/installation/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/getting-started/prerequisites/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/getting-started/quickstart/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/pipeline/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/pipeline/encoding/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/pipeline/playlist-analysis/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/pipeline/post-encode-scripts/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/pipeline/workflow/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/tools/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/tools/handbrake/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/tools/makemkv/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
<url>
<loc>https://mboehmlaender.github.io/ripster/tools/mediainfo/</loc>
<lastmod>2026-03-05</lastmod>
<lastmod>2026-03-10</lastmod>
</url>
</urlset>
Binary file not shown.
+11 -24
View File
@@ -1,24 +1,11 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/tools/handbrake/ rel=canonical><link href=../makemkv/ rel=prev><link href=../mediainfo/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>HandBrake - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#handbrake class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> HandBrake </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.Line truncated
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a><span class=w> </span>--input<span class=w> </span><span class=s2>&quot;/mnt/raw/Film_t00.mkv&quot;</span><span class=w> </span><span class=se>\</span>
</span><span id=__span-0-3><a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a><span class=w> </span>--output<span class=w> </span><span class=s2>&quot;/mnt/movies/Film (2010).mkv&quot;</span><span class=w> </span><span class=se>\</span>
</span><span id=__span-0-4><a id=__codelineno-0-4 name=__codelineno-0-4 href=#__codelineno-0-4></a><span class=w> </span>--preset<span class=w> </span><span class=s2>&quot;H.265 MKV 1080p30&quot;</span><span class=w> </span><span class=se>\</span>
</span><span id=__span-0-5><a id=__codelineno-0-5 name=__codelineno-0-5 href=#__codelineno-0-5></a><span class=w> </span>--audio<span class=w> </span><span class=m>1</span>,2<span class=w> </span><span class=se>\</span>
</span><span id=__span-0-6><a id=__codelineno-0-6 name=__codelineno-0-6 href=#__codelineno-0-6></a><span class=w> </span>--aencoder<span class=w> </span>copy:ac3,ffaac<span class=w> </span><span class=se>\</span>
</span><span id=__span-0-7><a id=__codelineno-0-7 name=__codelineno-0-7 href=#__codelineno-0-7></a><span class=w> </span>--subtitle<span class=w> </span><span class=m>1</span><span class=w> </span><span class=se>\</span>
</span><span id=__span-0-8><a id=__codelineno-0-8 name=__codelineno-0-8 href=#__codelineno-0-8></a><span class=w> </span>--subtitle-default<span class=w> </span><span class=m>1</span>
</span></code></pre></div> <hr> <h2 id=presets>Presets<a class=headerlink href=#presets title="Permanent link">&para;</a></h2> <p>HandBrake verwendet <strong>Presets</strong> für vorkonfigurierte Encoding-Einstellungen.</p> <h3 id=empfohlene-presets>Empfohlene Presets<a class=headerlink href=#empfohlene-presets title="Permanent link">&para;</a></h3> <table> <thead> <tr> <th>Preset</th> <th>Codec</th> <th>Auflösung</th> <th>Für</th> </tr> </thead> <tbody> <tr> <td><code>H.265 MKV 1080p30</code></td> <td>HEVC/H.265</td> <td>1080p</td> <td>Beste Qualität/Größe</td> </tr> <tr> <td><code>H.265 MKV 720p30</code></td> <td>HEVC/H.265</td> <td>720p</td> <td>Kleinere Dateien</td> </tr> <tr> <td><code>H.264 MKV 1080p30</code></td> <td>AVC/H.264</td> <td>1080p</td> <td>Breiteste Kompatibilität</td> </tr> <tr> <td><code>HQ 1080p30 Surround</code></td> <td>HEVC/H.265</td> <td>1080p</td> <td>Hohe Qualität mit Surround</td> </tr> </tbody> </table> <h3 id=alle-presets-anzeigen>Alle Presets anzeigen<a class=headerlink href=#alle-presets-anzeigen title="Permanent link">&para;</a></h3> <div class="language-bash highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a>HandBrakeCLI<span class=w> </span>--preset-list
</span></code></pre></div> <hr> <h2 id=audio-encoding>Audio-Encoding<a class=headerlink href=#audio-encoding title="Permanent link">&para;</a></h2> <h3 id=copy-kompatible-codecs>Copy-kompatible Codecs<a class=headerlink href=#copy-kompatible-codecs title="Permanent link">&para;</a></h3> <p>HandBrake kann folgende Codecs direkt kopieren (kein Qualitätsverlust):</p> <table> <thead> <tr> <th>Codec</th> <th><code>--aencoder</code> Wert</th> </tr> </thead> <tbody> <tr> <td>AC-3</td> <td><code>copy:ac3</code></td> </tr> <tr> <td>AAC</td> <td><code>copy:aac</code></td> </tr> <tr> <td>MP3</td> <td><code>copy:mp3</code></td> </tr> <tr> <td>TrueHD</td> <td><code>copy:truehd</code></td> </tr> <tr> <td>E-AC-3</td> <td><code>copy:eac3</code></td> </tr> </tbody> </table> <h3 id=transcoding>Transcoding<a class=headerlink href=#transcoding title="Permanent link">&para;</a></h3> <p>Codecs die nicht kopiert werden können, werden zu AAC transcodiert:</p> <table> <thead> <tr> <th>Original</th> <th>Transcodiert zu</th> </tr> </thead> <tbody> <tr> <td>DTS</td> <td>AAC (<code>ffaac</code>)</td> </tr> <tr> <td>DTS-HD</td> <td>AAC (<code>ffaac</code>)</td> </tr> </tbody> </table> <hr> <h2 id=extra-argumente>Extra-Argumente<a class=headerlink href=#extra-argumente title="Permanent link">&para;</a></h2> <p>Über die Einstellung <code>handbrake_extra_args</code> können beliebige HandBrake-Argumente hinzugefügt werden:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a># Cropping deaktivieren
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a>--crop 0:0:0:0
</span><span id=__span-2-3><a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a>
</span><span id=__span-2-4><a id=__codelineno-2-4 name=__codelineno-2-4 href=#__codelineno-2-4></a># Loose Anamorphic
</span><span id=__span-2-5><a id=__codelineno-2-5 name=__codelineno-2-5 href=#__codelineno-2-5></a>--loose-anamorphic
</span><span id=__span-2-6><a id=__codelineno-2-6 name=__codelineno-2-6 href=#__codelineno-2-6></a>
</span><span id=__span-2-7><a id=__codelineno-2-7 name=__codelineno-2-7 href=#__codelineno-2-7></a># Bestimmte Qualität setzen
</span><span id=__span-2-8><a id=__codelineno-2-8 name=__codelineno-2-8 href=#__codelineno-2-8></a>--quality 20
</span></code></pre></div> <hr> <h2 id=fortschritts-parsing>Fortschritts-Parsing<a class=headerlink href=#fortschritts-parsing title="Permanent link">&para;</a></h2> <p>Ripster parst die HandBrake-Ausgabe auf stderr für die Fortschrittsanzeige:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a>Encoding: task 1 of 1, 73.50 % (45.23 fps, avg 44.12 fps, ETA 00h12m34s)
</span></code></pre></div> <p><code>progressParsers.js</code> extrahiert: - Prozentzahl - Aktuelle FPS - ETA</p> <hr> <h2 id=konfiguration-in-ripster>Konfiguration in Ripster<a class=headerlink href=#konfiguration-in-ripster title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>Einstellung</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>handbrake_command</code></td> <td>Pfad/Befehl für <code>HandBrakeCLI</code></td> </tr> <tr> <td><code>handbrake_preset</code></td> <td>Preset-Name</td> </tr> <tr> <td><code>handbrake_extra_args</code></td> <td>Zusätzliche CLI-Argumente</td> </tr> <tr> <td><code>output_extension</code></td> <td>Dateiendung der Ausgabe</td> </tr> </tbody> </table> <hr> <h2 id=troubleshooting>Troubleshooting<a class=headerlink href=#troubleshooting title="Permanent link">&para;</a></h2> <h3 id=handbrake-findet-preset-nicht>HandBrake findet Preset nicht<a class=headerlink href=#handbrake-findet-preset-nicht title="Permanent link">&para;</a></h3> <div class="language-bash highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a><span class=c1># Preset-Liste anzeigen</span>
</span><span id=__span-4-2><a id=__codelineno-4-2 name=__codelineno-4-2 href=#__codelineno-4-2></a>HandBrakeCLI<span class=w> </span>--preset-list<span class=w> </span><span class=m>2</span>&gt;<span class=p>&amp;</span><span class=m>1</span><span class=w> </span><span class=p>|</span><span class=w> </span>grep<span class=w> </span>-i<span class=w> </span><span class=s2>&quot;h.265&quot;</span>
</span></code></pre></div> <p>Preset-Namen sind case-sensitive!</p> <h3 id=encoding-sehr-langsam>Encoding sehr langsam<a class=headerlink href=#encoding-sehr-langsam title="Permanent link">&para;</a></h3> <div class="language-bash highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a><span class=c1># CPU-Encoding-Preset anpassen (schneller = schlechtere Qualität)</span>
</span><span id=__span-5-2><a id=__codelineno-5-2 name=__codelineno-5-2 href=#__codelineno-5-2></a><span class=nv>handbrake_extra_args</span><span class=w> </span><span class=o>=</span><span class=w> </span>--encoder-preset<span class=w> </span>fast
</span></code></pre></div> <p>Verfügbare Presets: <code>ultrafast</code>, <code>superfast</code>, <code>veryfast</code>, <code>faster</code>, <code>fast</code>, <code>medium</code>, <code>slow</code>, <code>slower</code>, <code>veryslow</code></p> <h3 id=gpu-encoding-nutzen-nvidia>GPU-Encoding nutzen (NVIDIA)<a class=headerlink href=#gpu-encoding-nutzen-nvidia title="Permanent link">&para;</a></h3> <div class="language-text highlight"><pre><span></span><code><span id=__span-6-1><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a>handbrake_preset = H.265 NVENC 1080p
</span></code></pre></div> <p>Erfordert HandBrake-Build mit NVENC-Unterstützung und NVIDIA-GPU.</p> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/tools/handbrake/ rel=canonical><link href=../makemkv/ rel=prev><link href=../mediainfo/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>HandBrake - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#handbrake class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> HandBrake </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.Line truncated
</span></code></pre></div> <h3 id=encode-vereinfacht>Encode (vereinfacht)<a class=headerlink href=#encode-vereinfacht title="Permanent link">&para;</a></h3> <div class="language-bash highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a>HandBrakeCLI<span class=w> </span><span class=se>\</span>
</span><span id=__span-1-2><a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a><span class=w> </span>-i<span class=w> </span>&lt;input&gt;<span class=w> </span><span class=se>\</span>
</span><span id=__span-1-3><a id=__codelineno-1-3 name=__codelineno-1-3 href=#__codelineno-1-3></a><span class=w> </span>-o<span class=w> </span>&lt;output&gt;<span class=w> </span><span class=se>\</span>
</span><span id=__span-1-4><a id=__codelineno-1-4 name=__codelineno-1-4 href=#__codelineno-1-4></a><span class=w> </span>-t<span class=w> </span>&lt;titleId&gt;<span class=w> </span><span class=se>\</span>
</span><span id=__span-1-5><a id=__codelineno-1-5 name=__codelineno-1-5 href=#__codelineno-1-5></a><span class=w> </span>-Z<span class=w> </span><span class=s2>&quot;&lt;preset&gt;&quot;</span><span class=w> </span><span class=se>\</span>
</span><span id=__span-1-6><a id=__codelineno-1-6 name=__codelineno-1-6 href=#__codelineno-1-6></a><span class=w> </span>&lt;extra-args&gt;<span class=w> </span><span class=se>\</span>
</span><span id=__span-1-7><a id=__codelineno-1-7 name=__codelineno-1-7 href=#__codelineno-1-7></a><span class=w> </span>-a<span class=w> </span>&lt;audioTrackIds<span class=p>|</span>none&gt;<span class=w> </span><span class=se>\</span>
</span><span id=__span-1-8><a id=__codelineno-1-8 name=__codelineno-1-8 href=#__codelineno-1-8></a><span class=w> </span>-s<span class=w> </span>&lt;subtitleTrackIds<span class=p>|</span>none&gt;
</span></code></pre></div> <p>Optional ergänzt Ripster:</p> <ul> <li><code>--subtitle-burned=&lt;id&gt;</code></li> <li><code>--subtitle-default=&lt;id&gt;</code></li> <li><code>--subtitle-forced=&lt;id&gt;</code> oder <code>--subtitle-forced</code></li> </ul> <hr> <h2 id=presets-auslesen>Presets auslesen<a class=headerlink href=#presets-auslesen title="Permanent link">&para;</a></h2> <p>Ripster liest Presets mit:</p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a>HandBrakeCLI<span class=w> </span>-z
</span></code></pre></div> <hr> <h2 id=relevante-settings>Relevante Settings<a class=headerlink href=#relevante-settings title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>Key</th> <th>Bedeutung</th> </tr> </thead> <tbody> <tr> <td><code>handbrake_command</code></td> <td>CLI-Binary</td> </tr> <tr> <td><code>handbrake_preset_bluray</code> / <code>handbrake_preset_dvd</code></td> <td>profilspezifisches Preset</td> </tr> <tr> <td><code>handbrake_extra_args_bluray</code> / <code>handbrake_extra_args_dvd</code></td> <td>profilspezifische Zusatzargumente</td> </tr> <tr> <td><code>output_extension_bluray</code> / <code>output_extension_dvd</code></td> <td>Ausgabeformat</td> </tr> <tr> <td><code>handbrake_restart_delete_incomplete_output</code></td> <td>unvollständige Ausgabe bei Neustart löschen</td> </tr> </tbody> </table> <hr> <h2 id=fortschritts-parsing>Fortschritts-Parsing<a class=headerlink href=#fortschritts-parsing title="Permanent link">&para;</a></h2> <p>Ripster parst HandBrake-Stderr (Prozent/ETA/Detail) und sendet WebSocket-Progress (<code>PIPELINE_PROGRESS</code>).</p> <hr> <h2 id=troubleshooting>Troubleshooting<a class=headerlink href=#troubleshooting title="Permanent link">&para;</a></h2> <ul> <li>Preset nicht gefunden: Preset-Namen mit <code>HandBrakeCLI -z</code> prüfen</li> <li>sehr langsames Encoding: Preset/Extra-Args prüfen (z. B. <code>--encoder-preset</code>)</li> </ul> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere SuchergebLine truncated
+1 -1
View File
@@ -1 +1 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/tools/ rel=canonical><link href=../deployment/production/ rel=prev><link href=makemkv/ rel=next><link rel=icon href=../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Externe Tools - Ripster</title><link rel=stylesheet href=../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../stylesheets/extra.css><script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#externe-tools class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Externe Tools </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56Line truncated
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/tools/ rel=canonical><link href=../deployment/production/ rel=prev><link href=makemkv/ rel=next><link rel=icon href=../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>Externe Tools - Ripster</title><link rel=stylesheet href=../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../stylesheets/extra.css><script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#externe-tools class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Externe Tools </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56Line truncated
+5 -20
View File
@@ -1,20 +1,5 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/tools/makemkv/ rel=canonical><link href=../ rel=prev><link href=../handbrake/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>MakeMKV - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#makemkv class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> MakeMKV </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27Line truncated
</span></code></pre></div> <p>Gibt alle Titel und Playlists der eingelegten Disc aus. Ripster parst diese Ausgabe um die verfügbaren Tracks und Playlists zu bestimmen.</p> <p><strong>Parameter:</strong> - <code>-r</code> – Maschinen-lesbares Ausgabeformat - <code>--cache=1</code> – Minimaler Disc-Cache - <code>info disc:0</code> – Informationsabfrage für erstes Laufwerk</p> <h3 id=mkv-modus-standard>MKV-Modus (Standard)<a class=headerlink href=#mkv-modus-standard title="Permanent link">&para;</a></h3> <div class="language-bash highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a>makemkvcon<span class=w> </span>mkv<span class=w> </span>disc:0<span class=w> </span>all<span class=w> </span>/path/to/raw/<span class=w> </span><span class=se>\</span>
</span><span id=__span-1-2><a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a><span class=w> </span>--minlength<span class=o>=</span><span class=m>900</span><span class=w> </span><span class=se>\</span>
</span><span id=__span-1-3><a id=__codelineno-1-3 name=__codelineno-1-3 href=#__codelineno-1-3></a><span class=w> </span>-r
</span></code></pre></div> <p>Erstellt MKV-Dateien aus allen Titeln, die länger als 15 Minuten sind.</p> <p><strong>Parameter:</strong> - <code>mkv</code> – MKV-Ausgabemodus - <code>disc:0</code> – Erstes Disc-Laufwerk - <code>all</code> – Alle passenden Titel (nicht nur einen bestimmten) - <code>--minlength=900</code> – Mindestlänge in Sekunden (entspricht 15 Minuten)</p> <h3 id=backup-modus>Backup-Modus<a class=headerlink href=#backup-modus title="Permanent link">&para;</a></h3> <div class="language-bash highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a>makemkvcon<span class=w> </span>backup<span class=w> </span>disc:0<span class=w> </span>/path/to/raw/backup/<span class=w> </span><span class=se>\</span>
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a><span class=w> </span>--decrypt<span class=w> </span><span class=se>\</span>
</span><span id=__span-2-3><a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a><span class=w> </span>-r
</span></code></pre></div> <p>Erstellt ein vollständiges Disc-Backup mit Menüs.</p> <p><strong>Parameter:</strong> - <code>backup</code> – Backup-Modus - <code>--decrypt</code> – Verschlüsselung entfernen</p> <hr> <h2 id=ausgabeformat>Ausgabeformat<a class=headerlink href=#ausgabeformat title="Permanent link">&para;</a></h2> <p>MakeMKV gibt Fortschritt und Status in einem strukturierten Format aus:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a>PRGV:current,total,max → Fortschrittsbalken-Werte
</span><span id=__span-3-2><a id=__codelineno-3-2 name=__codelineno-3-2 href=#__codelineno-3-2></a>PRGT:code,id,&quot;Beschreibung&quot; → Aktueller Task
</span><span id=__span-3-3><a id=__codelineno-3-3 name=__codelineno-3-3 href=#__codelineno-3-3></a>PRGC:code,id,&quot;Beschreibung&quot; → Aktueller Sub-Task
</span><span id=__span-3-4><a id=__codelineno-3-4 name=__codelineno-3-4 href=#__codelineno-3-4></a>MSG:code,flags,count,&quot;Text&quot; → Nachricht
</span></code></pre></div> <p>Ripster's <code>progressParsers.js</code> parst diese Ausgabe für die Live-Fortschrittsanzeige.</p> <hr> <h2 id=makemkv-lizenz>MakeMKV-Lizenz<a class=headerlink href=#makemkv-lizenz title="Permanent link">&para;</a></h2> <p>MakeMKV ist <strong>Beta-Software</strong> und kostenlos für den persönlichen Gebrauch während der Beta-Phase. Eine Beta-Lizenz ist regelmäßig im <a href="https://www.makemkv.com/forum/viewtopic.php?t=1053">MakeMKV-Forum</a> verfügbar.</p> <p>Ohne gültige Lizenz können Blu-rays nicht entschlüsselt werden.</p> <h3 id=lizenz-eintragen>Lizenz eintragen<a class=headerlink href=#lizenz-eintragen title="Permanent link">&para;</a></h3> <p>Die Lizenz wird in den MakeMKV-Einstellungen eingetragen (GUI) oder direkt in:</p> <div class="language-text highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a>~/.MakeMKV/settings.conf
</span></code></pre></div> <div class="language-text highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a>app_Key = &quot;XXXX-XXXX-XXXX-XXXX-XXXX&quot;
</span></code></pre></div> <hr> <h2 id=konfiguration-in-ripster>Konfiguration in Ripster<a class=headerlink href=#konfiguration-in-ripster title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>Einstellung</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>makemkv_command</code></td> <td>Pfad/Befehl für <code>makemkvcon</code></td> </tr> <tr> <td><code>makemkv_min_length_minutes</code></td> <td>Mindest-Titellänge (Standard: 15 Min)</td> </tr> <tr> <td><code>makemkv_backup_mode</code></td> <td>Backup-Modus statt MKV</td> </tr> </tbody> </table> <hr> <h2 id=troubleshooting>Troubleshooting<a class=headerlink href=#troubleshooting title="Permanent link">&para;</a></h2> <h3 id=makemkv-erkennt-disc-nicht>MakeMKV erkennt Disc nicht<a class=headerlink href=#makemkv-erkennt-disc-nicht title="Permanent link">&para;</a></h3> <div class="language-bash highlight"><pre><span></span><code><span id=__span-6-1><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a><span class=c1># Laufwerk-Berechtigungen prüfen</span>
</span><span id=__span-6-2><a id=__codelineno-6-2 name=__codelineno-6-2 href=#__codelineno-6-2></a>ls<span class=w> </span>-la<span class=w> </span>/dev/sr0
</span><span id=__span-6-3><a id=__codelineno-6-3 name=__codelineno-6-3 href=#__codelineno-6-3></a>sudo<span class=w> </span>chmod<span class=w> </span>a+rw<span class=w> </span>/dev/sr0
</span><span id=__span-6-4><a id=__codelineno-6-4 name=__codelineno-6-4 href=#__codelineno-6-4></a>
</span><span id=__span-6-5><a id=__codelineno-6-5 name=__codelineno-6-5 href=#__codelineno-6-5></a><span class=c1># Oder Benutzer zur Gruppe cdrom hinzufügen</span>
</span><span id=__span-6-6><a id=__codelineno-6-6 name=__codelineno-6-6 href=#__codelineno-6-6></a>sudo<span class=w> </span>usermod<span class=w> </span>-a<span class=w> </span>-G<span class=w> </span>cdrom<span class=w> </span><span class=nv>$USER</span>
</span></code></pre></div> <h3 id=langer-analyseprozess>Langer Analyseprozess<a class=headerlink href=#langer-analyseprozess title="Permanent link">&para;</a></h3> <p>Blu-ray-Analyse kann bei Discs mit vielen Playlists 5+ Minuten dauern. Dies ist normal.</p> <h3 id=fehlermeldung-libmmbd>Fehlermeldung: "LibMMBD"<a class=headerlink href=#fehlermeldung-libmmbd title="Permanent link">&para;</a></h3> <p>LibMMBD ist MakeMKVs interne Verschlüsselungsbibliothek. Bei Fehlern die MakeMKV-Version aktualisieren.</p> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/tools/makemkv/ rel=canonical><link href=../ rel=prev><link href=../handbrake/ rel=next><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>MakeMKV - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#makemkv class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> MakeMKV </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27Line truncated
</span></code></pre></div> <p><code>&lt;source&gt;</code> ist typischerweise:</p> <ul> <li><code>disc:&lt;index&gt;</code> (Auto-Modus)</li> <li><code>dev:/dev/sr0</code> (explicit)</li> <li><code>file:&lt;path&gt;</code> (Datei/Ordner-Analyse)</li> </ul> <h3 id=rip-mkv-modus>Rip (MKV-Modus)<a class=headerlink href=#rip-mkv-modus title="Permanent link">&para;</a></h3> <div class="language-bash highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a>makemkvcon<span class=w> </span>mkv<span class=w> </span>&lt;source&gt;<span class=w> </span>&lt;title-or-all&gt;<span class=w> </span>&lt;rawDir&gt;<span class=w> </span><span class=o>[</span>--minlength<span class=o>=</span>...<span class=o>]</span><span class=w> </span><span class=o>[</span>...extraArgs<span class=o>]</span>
</span></code></pre></div> <h3 id=rip-backup-modus>Rip (Backup-Modus)<a class=headerlink href=#rip-backup-modus title="Permanent link">&para;</a></h3> <div class="language-bash highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a>makemkvcon<span class=w> </span>backup<span class=w> </span>&lt;source&gt;<span class=w> </span>&lt;rawDir&gt;<span class=w> </span>--decrypt
</span></code></pre></div> <hr> <h2 id=registrierungsschlussel-optional>Registrierungsschlüssel (optional)<a class=headerlink href=#registrierungsschlussel-optional title="Permanent link">&para;</a></h2> <p>Wenn <code>makemkv_registration_key</code> gesetzt ist, führt Ripster vor Analyse/Rip aus:</p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a>makemkvcon<span class=w> </span>reg<span class=w> </span>&lt;key&gt;
</span></code></pre></div> <hr> <h2 id=relevante-settings>Relevante Settings<a class=headerlink href=#relevante-settings title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>Key</th> <th>Bedeutung</th> </tr> </thead> <tbody> <tr> <td><code>makemkv_command</code></td> <td>CLI-Binary</td> </tr> <tr> <td><code>makemkv_source_index</code></td> <td>Source-Index im Auto-Modus</td> </tr> <tr> <td><code>makemkv_min_length_minutes</code></td> <td>Mindestlaufzeitfilter</td> </tr> <tr> <td><code>makemkv_rip_mode_bluray</code> / <code>makemkv_rip_mode_dvd</code></td> <td><code>mkv</code> oder <code>backup</code></td> </tr> <tr> <td><code>makemkv_analyze_extra_args_bluray</code> / <code>_dvd</code></td> <td>Zusatzargs Analyse</td> </tr> <tr> <td><code>makemkv_rip_extra_args_bluray</code> / <code>_dvd</code></td> <td>Zusatzargs Rip</td> </tr> </tbody> </table> <hr> <h2 id=hinweise>Hinweise<a class=headerlink href=#hinweise title="Permanent link">&para;</a></h2> <ul> <li>Blu-ray-Backups werden oft für robuste Playlist-Analyse genutzt.</li> <li>MakeMKV-Ausgaben werden geparst und als <code>makemkvInfo</code> im Job gespeichert.</li> </ul> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "VersiLine truncated
+2 -46
View File
@@ -1,46 +1,2 @@
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/tools/mediainfo/ rel=canonical><link href=../handbrake/ rel=prev><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>MediaInfo - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#mediainfo class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> MediaInfo </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 Line truncated
</span></code></pre></div> <p>Gibt vollständige Track-Informationen als JSON zurück.</p> <hr> <h2 id=ausgabe-struktur>Ausgabe-Struktur<a class=headerlink href=#ausgabe-struktur title="Permanent link">&para;</a></h2> <div class="language-json highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a><span class=p>{</span>
</span><span id=__span-1-2><a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a><span class=w> </span><span class=nt>&quot;media&quot;</span><span class=p>:</span><span class=w> </span><span class=p>{</span>
</span><span id=__span-1-3><a id=__codelineno-1-3 name=__codelineno-1-3 href=#__codelineno-1-3></a><span class=w> </span><span class=nt>&quot;track&quot;</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
</span><span id=__span-1-4><a id=__codelineno-1-4 name=__codelineno-1-4 href=#__codelineno-1-4></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-1-5><a id=__codelineno-1-5 name=__codelineno-1-5 href=#__codelineno-1-5></a><span class=w> </span><span class=nt>&quot;@type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;General&quot;</span><span class=p>,</span>
</span><span id=__span-1-6><a id=__codelineno-1-6 name=__codelineno-1-6 href=#__codelineno-1-6></a><span class=w> </span><span class=nt>&quot;Duration&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;8885.042&quot;</span><span class=p>,</span>
</span><span id=__span-1-7><a id=__codelineno-1-7 name=__codelineno-1-7 href=#__codelineno-1-7></a><span class=w> </span><span class=nt>&quot;Format&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Matroska&quot;</span>
</span><span id=__span-1-8><a id=__codelineno-1-8 name=__codelineno-1-8 href=#__codelineno-1-8></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-1-9><a id=__codelineno-1-9 name=__codelineno-1-9 href=#__codelineno-1-9></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-1-10><a id=__codelineno-1-10 name=__codelineno-1-10 href=#__codelineno-1-10></a><span class=w> </span><span class=nt>&quot;@type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Video&quot;</span><span class=p>,</span>
</span><span id=__span-1-11><a id=__codelineno-1-11 name=__codelineno-1-11 href=#__codelineno-1-11></a><span class=w> </span><span class=nt>&quot;Format&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;HEVC&quot;</span><span class=p>,</span>
</span><span id=__span-1-12><a id=__codelineno-1-12 name=__codelineno-1-12 href=#__codelineno-1-12></a><span class=w> </span><span class=nt>&quot;Width&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;1920&quot;</span><span class=p>,</span>
</span><span id=__span-1-13><a id=__codelineno-1-13 name=__codelineno-1-13 href=#__codelineno-1-13></a><span class=w> </span><span class=nt>&quot;Height&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;1080&quot;</span><span class=p>,</span>
</span><span id=__span-1-14><a id=__codelineno-1-14 name=__codelineno-1-14 href=#__codelineno-1-14></a><span class=w> </span><span class=nt>&quot;FrameRate&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;23.976&quot;</span>
</span><span id=__span-1-15><a id=__codelineno-1-15 name=__codelineno-1-15 href=#__codelineno-1-15></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-1-16><a id=__codelineno-1-16 name=__codelineno-1-16 href=#__codelineno-1-16></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-1-17><a id=__codelineno-1-17 name=__codelineno-1-17 href=#__codelineno-1-17></a><span class=w> </span><span class=nt>&quot;@type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Audio&quot;</span><span class=p>,</span>
</span><span id=__span-1-18><a id=__codelineno-1-18 name=__codelineno-1-18 href=#__codelineno-1-18></a><span class=w> </span><span class=nt>&quot;StreamOrder&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;1&quot;</span><span class=p>,</span>
</span><span id=__span-1-19><a id=__codelineno-1-19 name=__codelineno-1-19 href=#__codelineno-1-19></a><span class=w> </span><span class=nt>&quot;Format&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;TrueHD&quot;</span><span class=p>,</span>
</span><span id=__span-1-20><a id=__codelineno-1-20 name=__codelineno-1-20 href=#__codelineno-1-20></a><span class=w> </span><span class=nt>&quot;Channels&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;8&quot;</span><span class=p>,</span>
</span><span id=__span-1-21><a id=__codelineno-1-21 name=__codelineno-1-21 href=#__codelineno-1-21></a><span class=w> </span><span class=nt>&quot;Language&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;en&quot;</span>
</span><span id=__span-1-22><a id=__codelineno-1-22 name=__codelineno-1-22 href=#__codelineno-1-22></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-1-23><a id=__codelineno-1-23 name=__codelineno-1-23 href=#__codelineno-1-23></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-1-24><a id=__codelineno-1-24 name=__codelineno-1-24 href=#__codelineno-1-24></a><span class=w> </span><span class=nt>&quot;@type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Audio&quot;</span><span class=p>,</span>
</span><span id=__span-1-25><a id=__codelineno-1-25 name=__codelineno-1-25 href=#__codelineno-1-25></a><span class=w> </span><span class=nt>&quot;StreamOrder&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;2&quot;</span><span class=p>,</span>
</span><span id=__span-1-26><a id=__codelineno-1-26 name=__codelineno-1-26 href=#__codelineno-1-26></a><span class=w> </span><span class=nt>&quot;Format&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;AC-3&quot;</span><span class=p>,</span>
</span><span id=__span-1-27><a id=__codelineno-1-27 name=__codelineno-1-27 href=#__codelineno-1-27></a><span class=w> </span><span class=nt>&quot;Channels&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;6&quot;</span><span class=p>,</span>
</span><span id=__span-1-28><a id=__codelineno-1-28 name=__codelineno-1-28 href=#__codelineno-1-28></a><span class=w> </span><span class=nt>&quot;Language&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;de&quot;</span>
</span><span id=__span-1-29><a id=__codelineno-1-29 name=__codelineno-1-29 href=#__codelineno-1-29></a><span class=w> </span><span class=p>},</span>
</span><span id=__span-1-30><a id=__codelineno-1-30 name=__codelineno-1-30 href=#__codelineno-1-30></a><span class=w> </span><span class=p>{</span>
</span><span id=__span-1-31><a id=__codelineno-1-31 name=__codelineno-1-31 href=#__codelineno-1-31></a><span class=w> </span><span class=nt>&quot;@type&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;Text&quot;</span><span class=p>,</span>
</span><span id=__span-1-32><a id=__codelineno-1-32 name=__codelineno-1-32 href=#__codelineno-1-32></a><span class=w> </span><span class=nt>&quot;StreamOrder&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;1&quot;</span><span class=p>,</span>
</span><span id=__span-1-33><a id=__codelineno-1-33 name=__codelineno-1-33 href=#__codelineno-1-33></a><span class=w> </span><span class=nt>&quot;Format&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;UTF-8&quot;</span><span class=p>,</span>
</span><span id=__span-1-34><a id=__codelineno-1-34 name=__codelineno-1-34 href=#__codelineno-1-34></a><span class=w> </span><span class=nt>&quot;Language&quot;</span><span class=p>:</span><span class=w> </span><span class=s2>&quot;de&quot;</span>
</span><span id=__span-1-35><a id=__codelineno-1-35 name=__codelineno-1-35 href=#__codelineno-1-35></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-1-36><a id=__codelineno-1-36 name=__codelineno-1-36 href=#__codelineno-1-36></a><span class=w> </span><span class=p>]</span>
</span><span id=__span-1-37><a id=__codelineno-1-37 name=__codelineno-1-37 href=#__codelineno-1-37></a><span class=w> </span><span class=p>}</span>
</span><span id=__span-1-38><a id=__codelineno-1-38 name=__codelineno-1-38 href=#__codelineno-1-38></a><span class=p>}</span>
</span></code></pre></div> <hr> <h2 id=verarbeitung-in-ripster>Verarbeitung in Ripster<a class=headerlink href=#verarbeitung-in-ripster title="Permanent link">&para;</a></h2> <p><code>encodePlan.js</code> verarbeitet die MediaInfo-Ausgabe:</p> <ol> <li><strong>Track-Extraktion</strong>: Alle Audio- und Untertitel-Tracks werden extrahiert</li> <li><strong>Sprach-Normalisierung</strong>: Sprachcodes werden auf ISO 639-3 normalisiert</li> <li><strong>Codec-Klassifizierung</strong>: Bestimmt ob Codec kopiert oder transcodiert werden kann</li> <li><strong>Track-Labels</strong>: Benutzerfreundliche Bezeichnungen (z.B. "Deutsch (AC-3, 5.1)")</li> </ol> <h3 id=track-label-format>Track-Label-Format<a class=headerlink href=#track-label-format title="Permanent link">&para;</a></h3> <div class="language-text highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a>{Sprache} ({Format}, {Kanäle})
</span></code></pre></div> <p>Beispiele: - <code>Deutsch (AC-3, 5.1)</code> - <code>English (TrueHD, 7.1)</code> - <code>Français (AC-3, 2.0)</code></p> <hr> <h2 id=konfiguration-in-ripster>Konfiguration in Ripster<a class=headerlink href=#konfiguration-in-ripster title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>Einstellung</th> <th>Beschreibung</th> </tr> </thead> <tbody> <tr> <td><code>mediainfo_command</code></td> <td>Pfad/Befehl für <code>mediainfo</code></td> </tr> </tbody> </table> <hr> <h2 id=troubleshooting>Troubleshooting<a class=headerlink href=#troubleshooting title="Permanent link">&para;</a></h2> <h3 id=mediainfo-gibt-kein-json-aus>MediaInfo gibt kein JSON aus<a class=headerlink href=#mediainfo-gibt-kein-json-aus title="Permanent link">&para;</a></h3> <div class="language-bash highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a><span class=c1># Version prüfen</span>
</span><span id=__span-3-2><a id=__codelineno-3-2 name=__codelineno-3-2 href=#__codelineno-3-2></a>mediainfo<span class=w> </span>--Version
</span><span id=__span-3-3><a id=__codelineno-3-3 name=__codelineno-3-3 href=#__codelineno-3-3></a>
</span><span id=__span-3-4><a id=__codelineno-3-4 name=__codelineno-3-4 href=#__codelineno-3-4></a><span class=c1># JSON-Ausgabe testen</span>
</span><span id=__span-3-5><a id=__codelineno-3-5 name=__codelineno-3-5 href=#__codelineno-3-5></a>mediainfo<span class=w> </span>--Output<span class=o>=</span>JSON<span class=w> </span>/path/to/test.mkv
</span></code></pre></div> <p>MediaInfo &gt;= 17.10 wird empfohlen.</p> <h3 id=sprache-als-und-angezeigt>Sprache als "und" angezeigt<a class=headerlink href=#sprache-als-und-angezeigt title="Permanent link">&para;</a></h3> <p><code>und</code> steht für "undetermined" – die Sprache ist in der MKV-Datei nicht getaggt. Dies ist bei manchen Rips normal. Der Track wird trotzdem angezeigt und kann manuell ausgewählt werden.</p> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version ausw\u00e4hlen"}, "version": {"provider": "mike"}}</script> <script src=../../assets/javascripts/bundle.e71a0d61.min.js></script> </body> </html>
<!doctype html><html lang=de class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Halbautomatische Disc-Ripping-Plattform für DVDs & Blu-rays mit HandBrake-Encoding"><meta name=author content=Michael><link href=https://mboehmlaender.github.io/ripster/tools/mediainfo/ rel=canonical><link href=../handbrake/ rel=prev><link rel=icon href=../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.0"><title>MediaInfo - Ripster</title><link rel=stylesheet href=../../assets/stylesheets/main.618322db.min.css><link rel=stylesheet href=../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=../../stylesheets/extra.css><script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#mediainfo class=md-skip> Zum Inhalt </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Kopfzeile> <a href=../.. title=Ripster class="md-header__button md-logo" aria-label=Ripster data-md-component=logo> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 14a2 2 0 0 1-2-2c0-1.11.89-2 2-2s2 .89 2 2a2 2 0 0 1-2 2m0-10a8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8 8 8 0 0 0-8-8"/></svg> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> Ripster </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> MediaInfo </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Dunkelmodus aktivieren" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Dunkelmodus aktivieren" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Hellmodus aktivieren" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Hellmodus aktivieren" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 Line truncated
</span></code></pre></div> <p>Der Input ist typischerweise eine RAW-Datei oder ein vom Workflow gewählter Inputpfad.</p> <hr> <h2 id=verwendung-in-ripster>Verwendung in Ripster<a class=headerlink href=#verwendung-in-ripster title="Permanent link">&para;</a></h2> <ul> <li>Track-/Codec-Metadaten für Review-Plan</li> <li>Fallback-Informationen in bestimmten Analysepfaden</li> <li>Persistenz als <code>mediainfoInfo</code> im Job</li> </ul> <hr> <h2 id=relevante-settings>Relevante Settings<a class=headerlink href=#relevante-settings title="Permanent link">&para;</a></h2> <table> <thead> <tr> <th>Key</th> <th>Bedeutung</th> </tr> </thead> <tbody> <tr> <td><code>mediainfo_command</code></td> <td>CLI-Binary</td> </tr> <tr> <td><code>mediainfo_extra_args_bluray</code> / <code>_dvd</code></td> <td>profilspezifische Zusatzargumente</td> </tr> </tbody> </table> <hr> <h2 id=troubleshooting>Troubleshooting<a class=headerlink href=#troubleshooting title="Permanent link">&para;</a></h2> <ul> <li>JSON-Test: <code>mediainfo --Output=JSON &lt;datei&gt;</code></li> <li>unbekannte Sprache erscheint oft als <code>und</code> (undetermined)</li> </ul> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Zurück zum Seitenanfang </button> </main> <footer class=md-footer> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/YOUR_GITHUB_USERNAME/ripster target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <script id=__config type=application/json>{"annotate": null, "base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.code.annotate", "content.tabs.link", "toc.integrate"], "search": "../../assets/javascripts/workers/search.7a47a382.min.js", "tags": null, "translations": {"clipboard.copied": "In Zwischenablage kopiert", "clipboard.copy": "In Zwischenablage kopieren", "search.result.more.one": "1 weiteres Suchergebnis auf dieser Seite", "search.result.more.other": "# weitere Suchergebnisse auf dieser Seite", "search.result.none": "Keine Suchergebnisse", "search.result.one": "1 Suchergebnis", "search.result.other": "# Suchergebnisse", "search.result.placeholder": "Suchbegriff eingeben", "search.result.term.missing": "Es fehlt", "select.version": "Version auLine truncated