From 6378a9a4cba2b1de60a4892af892fd14bcf437a3 Mon Sep 17 00:00:00 2001 From: mboehmlaender Date: Sat, 14 Mar 2026 10:06:59 +0000 Subject: [PATCH] 0.9.1-5 Minor fixes --- backend/package-lock.json | 4 +- backend/package.json | 2 +- backend/src/services/cdRipService.js | 12 +++ frontend/package-lock.json | 4 +- frontend/package.json | 2 +- .../src/components/MediaInfoReviewPanel.jsx | 91 ++++++++++++++++++- frontend/src/pages/HistoryPage.jsx | 74 +++++++++------ package-lock.json | 4 +- package.json | 2 +- 9 files changed, 155 insertions(+), 40 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index fd16426..7b3b361 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1,12 +1,12 @@ { "name": "ripster-backend", - "version": "0.9.1-4", + "version": "0.9.1-5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ripster-backend", - "version": "0.9.1-4", + "version": "0.9.1-5", "dependencies": { "cors": "^2.8.5", "dotenv": "^16.4.7", diff --git a/backend/package.json b/backend/package.json index e2ea817..cc86f03 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "ripster-backend", - "version": "0.9.1-4", + "version": "0.9.1-5", "private": true, "type": "commonjs", "scripts": { diff --git a/backend/src/services/cdRipService.js b/backend/src/services/cdRipService.js index 45f0b8d..e966030 100644 --- a/backend/src/services/cdRipService.js +++ b/backend/src/services/cdRipService.js @@ -585,6 +585,17 @@ async function ripAndEncode(options) { ); } + // Safety net: some encoders (e.g. older flac without --no-delete-input-file) may remove + // the source WAV. Restore it from the encoded output so the RAW folder stays filled. + if (!fs.existsSync(wavFile) && fs.existsSync(outFile)) { + try { + fs.copyFileSync(outFile, wavFile); + log('info', `Track ${track.position}: WAV-Quelldatei vom Encoder gelöscht – aus Output wiederhergestellt.`); + } catch (restoreErr) { + log('warn', `Track ${track.position}: WAV-Wiederherstellung fehlgeschlagen: ${restoreErr.message}`); + } + } + onProgress && onProgress({ phase: 'encode', trackEvent: 'complete', @@ -615,6 +626,7 @@ function buildEncodeArgs(format, opts, track, meta, wavFile, outFile) { cmd: 'flac', args: [ `--compression-level-${clampedLevel}`, + '--no-delete-input-file', // flac deletes input WAV by default; keep RAW folder filled '--tag', `TITLE=${trackTitle}`, '--tag', `ARTIST=${artist}`, '--tag', `ALBUM=${album}`, diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a00a739..1cfeaf4 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "ripster-frontend", - "version": "0.9.1-4", + "version": "0.9.1-5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ripster-frontend", - "version": "0.9.1-4", + "version": "0.9.1-5", "dependencies": { "primeicons": "^7.0.0", "primereact": "^10.9.2", diff --git a/frontend/package.json b/frontend/package.json index 5922f74..6c98de8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "ripster-frontend", - "version": "0.9.1-4", + "version": "0.9.1-5", "private": true, "type": "module", "scripts": { diff --git a/frontend/src/components/MediaInfoReviewPanel.jsx b/frontend/src/components/MediaInfoReviewPanel.jsx index 25147e7..3b09168 100644 --- a/frontend/src/components/MediaInfoReviewPanel.jsx +++ b/frontend/src/components/MediaInfoReviewPanel.jsx @@ -532,6 +532,84 @@ function resolveAudioEncoderPreviewLabel(track, encoderToken, copyMask, fallback return `Transcode (${normalizedToken})`; } +function parseAudioSelectorFromArgs(extraArgsString, baseSelector) { + const base = baseSelector && typeof baseSelector === 'object' ? baseSelector : {}; + const args = String(extraArgsString || '').trim(); + if (!args) { + return base; + } + + // Tokenize: split on whitespace but respect quoted strings + const tokens = []; + let current = ''; + let inQuote = null; + for (let i = 0; i < args.length; i++) { + const ch = args[i]; + if (inQuote) { + if (ch === inQuote) { + inQuote = null; + } else { + current += ch; + } + } else if (ch === '"' || ch === "'") { + inQuote = ch; + } else if (ch === ' ' || ch === '\t') { + if (current) { + tokens.push(current); + current = ''; + } + } else { + current += ch; + } + } + if (current) { + tokens.push(current); + } + + const result = { ...base }; + + const getNext = (i) => (i + 1 < tokens.length ? tokens[i + 1] : null); + + for (let i = 0; i < tokens.length; i++) { + const token = tokens[i]; + + // Support both --flag=value and --flag value + const eqIdx = token.indexOf('='); + const flag = eqIdx !== -1 ? token.slice(0, eqIdx) : token; + const inlineVal = eqIdx !== -1 ? token.slice(eqIdx + 1) : null; + + const getValue = () => { + if (inlineVal !== null) return inlineVal; + const next = getNext(i); + if (next && !next.startsWith('-')) { + i++; + return next; + } + return null; + }; + + if (flag === '--aencoder') { + const val = getValue(); + if (val) { + result.encoders = val.split(',').map((s) => s.trim()).filter(Boolean); + result.encoderSource = 'args'; + } + } else if (flag === '--audio-copy-mask') { + const val = getValue(); + if (val) { + result.copyMask = val.split(',').map((s) => s.trim()).filter(Boolean); + } + } else if (flag === '--audio-fallback') { + const val = getValue(); + if (val) { + result.fallbackEncoder = val.trim(); + } + } + } + + return result; +} + function buildAudioActionPreviewSummary(track, selectedIndex, audioSelector) { const selector = audioSelector && typeof audioSelector === 'object' ? audioSelector : {}; const availableEncoders = Array.isArray(selector.encoders) ? selector.encoders : []; @@ -759,6 +837,9 @@ export default function MediaInfoReviewPanel({ const effectivePresetOverride = selectedUserPreset ? { handbrakePreset: selectedUserPreset.handbrakePreset || '', extraArgs: selectedUserPreset.extraArgs || '' } : null; + const effectiveAudioSelector = effectivePresetOverride?.extraArgs + ? parseAudioSelectorFromArgs(effectivePresetOverride.extraArgs, review?.selectors?.audio) + : (review?.selectors?.audio || null); const hasUserPresets = normalizedUserPresets.length > 0; const allowUserPresetSelection = hasUserPresets && typeof onUserPresetChange === 'function' && allowEncodeItemSelection; @@ -843,10 +924,10 @@ export default function MediaInfoReviewPanel({
Preset-Profil: {effectivePresetOverride ? 'user-preset' : (review.selectors?.presetProfileSource || '-')}
MIN_LENGTH_MINUTES: {review.minLengthMinutes}
Encode Input: {encodeInputTitle?.fileName || '-'}
-
Audio Auswahl: {review.selectors?.audio?.mode || '-'}
-
Audio Encoder: {(review.selectors?.audio?.encoders || []).join(', ') || 'Preset-Default'}
-
Audio Copy-Mask: {(review.selectors?.audio?.copyMask || []).join(', ') || '-'}
-
Audio Fallback: {review.selectors?.audio?.fallbackEncoder || '-'}
+
Audio Auswahl: {effectiveAudioSelector?.mode || '-'}
+
Audio Encoder: {(effectiveAudioSelector?.encoders || []).join(', ') || 'Preset-Default'}
+
Audio Copy-Mask: {(effectiveAudioSelector?.copyMask || []).join(', ') || '-'}
+
Audio Fallback: {effectiveAudioSelector?.fallbackEncoder || '-'}
Subtitle Auswahl: {review.selectors?.subtitle?.mode || '-'}
Subtitle Flags: {review.selectors?.subtitle?.forcedOnly ? 'forced-only' : '-'}{review.selectors?.subtitle?.burnBehavior === 'first' ? ' + burned(first)' : ''}
@@ -1206,7 +1287,7 @@ export default function MediaInfoReviewPanel({ type="audio" allowSelection={allowTrackSelectionForTitle} selectedTrackIds={selectedAudioTrackIds} - audioSelector={review?.selectors?.audio || null} + audioSelector={effectiveAudioSelector} onToggleTrack={(trackId, checked) => { if (!allowTrackSelectionForTitle || typeof onTrackSelectionChange !== 'function') { return; diff --git a/frontend/src/pages/HistoryPage.jsx b/frontend/src/pages/HistoryPage.jsx index 828d746..6aa6112 100644 --- a/frontend/src/pages/HistoryPage.jsx +++ b/frontend/src/pages/HistoryPage.jsx @@ -667,7 +667,7 @@ export default function HistoryPage() { const confirmDeleteEntry = async (target) => { const normalizedTarget = String(target || '').trim().toLowerCase(); - if (!['raw', 'movie', 'both'].includes(normalizedTarget)) { + if (!['raw', 'movie', 'both', 'none'].includes(normalizedTarget)) { return; } const jobId = Number(deleteEntryDialogRow?.id || 0); @@ -686,10 +686,13 @@ export default function HistoryPage() { const rawDirs = Number(fileSummary?.raw?.dirsRemoved || 0); const movieDirs = Number(fileSummary?.movie?.dirsRemoved || 0); + const detail = normalizedTarget === 'none' + ? `${deletedJobIds.length || 1} Eintrag/Einträge entfernt (Dateien bleiben erhalten)` + : `${deletedJobIds.length || 1} Eintrag/Einträge entfernt | RAW: ${rawFiles} Dateien, ${rawDirs} Ordner | ${deleteEntryOutputShortLabel}: ${movieFiles} Dateien, ${movieDirs} Ordner`; toastRef.current?.show({ severity: 'success', summary: 'Historie gelöscht', - detail: `${deletedJobIds.length || 1} Eintrag/Einträge entfernt | RAW: ${rawFiles} Dateien, ${rawDirs} Ordner | ${deleteEntryOutputShortLabel}: ${movieFiles} Dateien, ${movieDirs} Ordner`, + detail, life: 5000 }); @@ -1124,36 +1127,46 @@ export default function HistoryPage() {

{`RAW (${previewRawExisting.length}/${previewRawPaths.length})`}

- {previewRawPaths.length > 0 ? ( - - ) : ( + {previewRawPaths.length > 0 ? (() => { + const display = previewRawPaths.filter(p => p.exists).length > 0 + ? previewRawPaths.filter(p => p.exists) + : previewRawPaths.slice(0, 1); + return ( + + ); + })() : ( Keine RAW-Pfade. )}

{`${deleteEntryOutputShortLabel} (${previewMovieExisting.length}/${previewMoviePaths.length})`}

- {previewMoviePaths.length > 0 ? ( - - ) : ( + {previewMoviePaths.length > 0 ? (() => { + const display = previewMoviePaths.filter(p => p.exists).length > 0 + ? previewMoviePaths.filter(p => p.exists) + : previewMoviePaths.slice(0, 1); + return ( + + ); + })() : ( Keine Movie-Pfade. )}
@@ -1187,6 +1200,15 @@ export default function HistoryPage() { loading={deleteEntryTargetBusy === 'both'} disabled={deleteTargetActionsDisabled} /> +