From 715dfbbc38796d8f247d01e2ff44641f36338098 Mon Sep 17 00:00:00 2001 From: mboehmlaender Date: Thu, 12 Mar 2026 10:27:09 +0000 Subject: [PATCH] Prototype --- backend/src/services/pipelineService.js | 58 ++++++++++++++++++++++--- backend/src/services/settingsService.js | 7 +++ install-dev.sh | 35 ++++++++------- install.sh | 39 ++++++++++------- 4 files changed, 100 insertions(+), 39 deletions(-) diff --git a/backend/src/services/pipelineService.js b/backend/src/services/pipelineService.js index fda768e..238841d 100644 --- a/backend/src/services/pipelineService.js +++ b/backend/src/services/pipelineService.js @@ -62,6 +62,7 @@ const PRE_ENCODE_PROGRESS_RESERVE = 10; const POST_ENCODE_PROGRESS_RESERVE = 10; const POST_ENCODE_FINISH_BUFFER = 1; const MIN_EXTENSIONLESS_DISC_IMAGE_BYTES = 256 * 1024 * 1024; +const MAKEMKV_BACKUP_FAILURE_MSG_CODES = new Set([5069, 5080]); const RAW_INCOMPLETE_PREFIX = 'Incomplete_'; const RAW_RIP_COMPLETE_PREFIX = 'Rip_Complete_'; const RAW_FOLDER_STATES = Object.freeze({ @@ -552,6 +553,37 @@ function composeEncodeScriptStatusText(percent, phase, itemType, index, total, l return `ENCODING ${percent.toFixed(2)}% - ${phaseLabel} ${itemLabel}${position}${status}${detail ? `: ${detail}` : ''}`; } +function parseMakeMkvMessageCode(line) { + const match = String(line || '').match(/\bMSG:(\d+),/i); + if (!match) { + return null; + } + const code = Number(match[1]); + if (!Number.isFinite(code)) { + return null; + } + return Math.trunc(code); +} + +function isMakeMkvBackupFailureMarker(line) { + const text = String(line || '').trim(); + if (!text) { + return false; + } + const code = parseMakeMkvMessageCode(text); + if (code !== null && MAKEMKV_BACKUP_FAILURE_MSG_CODES.has(code)) { + return true; + } + return /backup\s+failed/i.test(text) || /backup\s+fehlgeschlagen/i.test(text); +} + +function findMakeMkvBackupFailureMarker(lines) { + if (!Array.isArray(lines)) { + return null; + } + return lines.find((line) => isMakeMkvBackupFailureMarker(line)) || null; +} + function createEncodeScriptProgressTracker({ jobId, preSteps = 0, @@ -664,7 +696,8 @@ function createEncodeScriptProgressTracker({ } function shouldKeepHighlight(line) { - return /error|fail|warn|title\s+#|saving|encoding:|muxing|copying|decrypt/i.test(line); + return /error|fail|warn|fehl|title\s+#|saving|encoding:|muxing|copying|decrypt/i.test(line) + || isMakeMkvBackupFailureMarker(line); } function normalizeNonNegativeInteger(rawValue) { @@ -8626,13 +8659,14 @@ class PipelineService extends EventEmitter { } // Check for MakeMKV backup failure even when exit code is 0. - // MakeMKV can exit 0 but still output "Backup failed" in stdout. - const backupFailed = Array.isArray(makemkvInfo?.highlights) && - makemkvInfo.highlights.some(line => /backup failed/i.test(line)); - if (backupFailed) { - const failMsg = makemkvInfo.highlights.find(line => /backup failed/i.test(line)) || 'Backup failed'; + // MakeMKV can emit localized failure text while still exiting with 0. + const backupFailureLine = ripMode === 'backup' + ? findMakeMkvBackupFailureMarker(makemkvInfo?.highlights) + : null; + if (backupFailureLine) { + const msgCode = parseMakeMkvMessageCode(backupFailureLine); throw Object.assign( - new Error(`MakeMKV Backup fehlgeschlagen (Exit Code 0): ${failMsg}`), + new Error(`MakeMKV Backup fehlgeschlagen${msgCode !== null ? ` (MSG:${msgCode})` : ''}: ${backupFailureLine}`), { runInfo: makemkvInfo } ); } @@ -10082,9 +10116,16 @@ class PipelineService extends EventEmitter { settings.cd_output_template || cdRipService.DEFAULT_CD_OUTPUT_TEMPLATE ).trim() || cdRipService.DEFAULT_CD_OUTPUT_TEMPLATE; const cdBaseDir = String(settings.raw_dir_cd || '').trim() || 'data/output/cd'; + const cdOutputOwner = String(settings.raw_dir_owner || '').trim(); const jobDir = `CD_Job${jobId}_${Date.now()}`; const rawWavDir = path.join(cdBaseDir, '.tmp', jobDir, 'wav'); + const cdTempJobDir = path.dirname(rawWavDir); const outputDir = cdRipService.buildOutputDir(effectiveSelectedMeta, cdBaseDir, cdOutputTemplate); + ensureDir(cdBaseDir); + ensureDir(cdTempJobDir); + ensureDir(outputDir); + chownRecursive(cdTempJobDir, cdOutputOwner); + chownRecursive(outputDir, cdOutputOwner); const previewTrackPos = effectiveSelectedTrackPositions[0] || mergedTracks[0]?.position || 1; const previewWavPath = path.join(rawWavDir, `track${String(previewTrackPos).padStart(2, '0')}.cdda.wav`); const cdparanoiaCommandPreview = `${cdparanoiaCmd} -d ${devicePath} ${previewTrackPos} ${previewWavPath}`; @@ -10149,6 +10190,7 @@ class PipelineService extends EventEmitter { format, formatOptions, outputTemplate: cdOutputTemplate, + outputOwner: cdOutputOwner, selectedTrackPositions: effectiveSelectedTrackPositions, tocTracks: mergedTracks, selectedMeta: effectiveSelectedMeta @@ -10168,6 +10210,7 @@ class PipelineService extends EventEmitter { format, formatOptions, outputTemplate, + outputOwner, selectedTrackPositions, tocTracks, selectedMeta @@ -10275,6 +10318,7 @@ class PipelineService extends EventEmitter { rip_successful: 1, output_path: outputDir }); + chownRecursive(outputDir, outputOwner); await historyService.appendLog(jobId, 'SYSTEM', `CD-Rip abgeschlossen. Ausgabe: ${outputDir}`); await this.setState('FINISHED', { diff --git a/backend/src/services/settingsService.js b/backend/src/services/settingsService.js index b19b5ca..6200bb7 100644 --- a/backend/src/services/settingsService.js +++ b/backend/src/services/settingsService.js @@ -1317,6 +1317,13 @@ class SettingsService { return `dev:${device}`; } + const devicePath = String(deviceInfo?.path || '').trim(); + if (devicePath) { + // Prefer stable Linux device path over MakeMKV disc index mapping. + // MakeMKV drive indices (disc:N) do not reliably match /dev/srN numbering. + return `dev:${devicePath}`; + } + if (deviceInfo && deviceInfo.index !== undefined && deviceInfo.index !== null) { return `disc:${deviceInfo.index}`; } diff --git a/install-dev.sh b/install-dev.sh index 9fe8efb..43eda71 100755 --- a/install-dev.sh +++ b/install-dev.sh @@ -658,6 +658,15 @@ else ok "Benutzer '$SERVICE_USER' angelegt" fi +SERVICE_HOME="$(getent passwd "$SERVICE_USER" | cut -d: -f6)" +if [[ -z "$SERVICE_HOME" || "$SERVICE_HOME" == "/" || "$SERVICE_HOME" == "/nonexistent" ]]; then + SERVICE_HOME="/home/$SERVICE_USER" +fi +mkdir -p "$SERVICE_HOME" +chown "$SERVICE_USER:$SERVICE_USER" "$SERVICE_HOME" 2>/dev/null || true +chmod 755 "$SERVICE_HOME" 2>/dev/null || true +info "Service-Home für '$SERVICE_USER': $SERVICE_HOME" + # Optisches Laufwerk: Benutzer zur cdrom/optical-Gruppe hinzufügen for grp in cdrom optical disk; do if getent group "$grp" &>/dev/null; then @@ -767,22 +776,15 @@ chmod -R 755 "$INSTALL_DIR" chmod 600 "$ENV_FILE" # MakeMKV erwartet pro Benutzer ein eigenes Konfigurationsverzeichnis. -ACTUAL_USER="${SUDO_USER:-}" -if [[ -n "$ACTUAL_USER" && "$ACTUAL_USER" != "root" ]]; then - ACTUAL_HOME="$(getent passwd "$ACTUAL_USER" | cut -d: -f6)" - if [[ -z "$ACTUAL_HOME" ]]; then - ACTUAL_HOME="/home/$ACTUAL_USER" - fi - MAKEMKV_USER_DIR="${ACTUAL_HOME}/.MakeMKV" - if [[ ! -d "$MAKEMKV_USER_DIR" ]]; then - mkdir -p "$MAKEMKV_USER_DIR" - ok "MakeMKV-Verzeichnis erstellt: $MAKEMKV_USER_DIR" - else - info "MakeMKV-Verzeichnis vorhanden: $MAKEMKV_USER_DIR" - fi - chown "$ACTUAL_USER:$ACTUAL_USER" "$MAKEMKV_USER_DIR" 2>/dev/null || true - chmod 700 "$MAKEMKV_USER_DIR" 2>/dev/null || true +MAKEMKV_SERVICE_DIR="${SERVICE_HOME}/.MakeMKV" +if [[ ! -d "$MAKEMKV_SERVICE_DIR" ]]; then + mkdir -p "$MAKEMKV_SERVICE_DIR" + ok "MakeMKV-Verzeichnis erstellt: $MAKEMKV_SERVICE_DIR" +else + info "MakeMKV-Verzeichnis vorhanden: $MAKEMKV_SERVICE_DIR" fi +chown "$SERVICE_USER:$SERVICE_USER" "$MAKEMKV_SERVICE_DIR" 2>/dev/null || true +chmod 700 "$MAKEMKV_SERVICE_DIR" 2>/dev/null || true # --- Systemd-Dienst: Backend ------------------------------------------------- header "Systemd-Dienst (Backend) erstellen" @@ -807,6 +809,7 @@ StartLimitBurst=3 # Umgebung Environment=NODE_ENV=production +Environment=HOME=${SERVICE_HOME} Environment=LANG=C.UTF-8 Environment=LC_ALL=C.UTF-8 Environment=LANGUAGE=C.UTF-8 @@ -823,7 +826,7 @@ SyslogIdentifier=ripster-backend NoNewPrivileges=false ProtectSystem=full ProtectHome=read-only -ReadWritePaths=${INSTALL_DIR}/backend/data ${INSTALL_DIR}/backend/logs /tmp +ReadWritePaths=${INSTALL_DIR}/backend/data ${INSTALL_DIR}/backend/logs /tmp ${SERVICE_HOME} ${MAKEMKV_SERVICE_DIR} PrivateTmp=true [Install] diff --git a/install.sh b/install.sh index 2c4ca63..7ae0379 100755 --- a/install.sh +++ b/install.sh @@ -402,6 +402,15 @@ else ok "Benutzer '$SERVICE_USER' angelegt" fi +SERVICE_HOME="$(getent passwd "$SERVICE_USER" | cut -d: -f6)" +if [[ -z "$SERVICE_HOME" || "$SERVICE_HOME" == "/" || "$SERVICE_HOME" == "/nonexistent" ]]; then + SERVICE_HOME="/home/$SERVICE_USER" +fi +mkdir -p "$SERVICE_HOME" +chown "$SERVICE_USER:$SERVICE_USER" "$SERVICE_HOME" 2>/dev/null || true +chmod 755 "$SERVICE_HOME" 2>/dev/null || true +info "Service-Home für '$SERVICE_USER': $SERVICE_HOME" + 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 @@ -530,25 +539,22 @@ if [[ -n "$ACTUAL_USER" && "$ACTUAL_USER" != "root" ]]; then "$INSTALL_DIR/backend/data/output" \ "$INSTALL_DIR/backend/data/logs" ok "Verzeichnisse $ACTUAL_USER:$SERVICE_USER (775) zugewiesen" - - # MakeMKV erwartet pro Benutzer ein eigenes Konfigurationsverzeichnis. - ACTUAL_HOME="$(getent passwd "$ACTUAL_USER" | cut -d: -f6)" - if [[ -z "$ACTUAL_HOME" ]]; then - ACTUAL_HOME="/home/$ACTUAL_USER" - fi - MAKEMKV_USER_DIR="${ACTUAL_HOME}/.MakeMKV" - if [[ ! -d "$MAKEMKV_USER_DIR" ]]; then - mkdir -p "$MAKEMKV_USER_DIR" - ok "MakeMKV-Verzeichnis erstellt: $MAKEMKV_USER_DIR" - else - info "MakeMKV-Verzeichnis vorhanden: $MAKEMKV_USER_DIR" - fi - chown "$ACTUAL_USER:$ACTUAL_USER" "$MAKEMKV_USER_DIR" 2>/dev/null || true - chmod 700 "$MAKEMKV_USER_DIR" 2>/dev/null || true else ok "Verzeichnisse bereits $SERVICE_USER gehörig (kein SUDO_USER erkannt)" fi +# MakeMKV erwartet pro Benutzer ein eigenes Konfigurationsverzeichnis. +# Laufzeit-relevant ist das Verzeichnis des Service-Users. +MAKEMKV_SERVICE_DIR="${SERVICE_HOME}/.MakeMKV" +if [[ ! -d "$MAKEMKV_SERVICE_DIR" ]]; then + mkdir -p "$MAKEMKV_SERVICE_DIR" + ok "MakeMKV-Verzeichnis erstellt: $MAKEMKV_SERVICE_DIR" +else + info "MakeMKV-Verzeichnis vorhanden: $MAKEMKV_SERVICE_DIR" +fi +chown "$SERVICE_USER:$SERVICE_USER" "$MAKEMKV_SERVICE_DIR" 2>/dev/null || true +chmod 700 "$MAKEMKV_SERVICE_DIR" 2>/dev/null || true + # --- Systemd-Dienst: Backend ------------------------------------------------- header "Systemd-Dienst (Backend) erstellen" @@ -570,6 +576,7 @@ StartLimitIntervalSec=60 StartLimitBurst=3 Environment=NODE_ENV=production +Environment=HOME=${SERVICE_HOME} Environment=LANG=C.UTF-8 Environment=LC_ALL=C.UTF-8 Environment=LANGUAGE=C.UTF-8 @@ -589,7 +596,7 @@ SupplementaryGroups=video render cdrom disk NoNewPrivileges=false ProtectSystem=full ProtectHome=read-only -ReadWritePaths=${INSTALL_DIR}/backend/data ${INSTALL_DIR}/backend/logs /tmp +ReadWritePaths=${INSTALL_DIR}/backend/data ${INSTALL_DIR}/backend/logs /tmp ${SERVICE_HOME} ${MAKEMKV_SERVICE_DIR} PrivateTmp=true [Install]