0.9.1-5 Minor fixes
This commit is contained in:
4
backend/package-lock.json
generated
4
backend/package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ripster-backend",
|
||||
"version": "0.9.1-4",
|
||||
"version": "0.9.1-5",
|
||||
"private": true,
|
||||
"type": "commonjs",
|
||||
"scripts": {
|
||||
|
||||
@@ -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}`,
|
||||
|
||||
4
frontend/package-lock.json
generated
4
frontend/package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ripster-frontend",
|
||||
"version": "0.9.1-4",
|
||||
"version": "0.9.1-5",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -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({
|
||||
<div><strong>Preset-Profil:</strong> {effectivePresetOverride ? 'user-preset' : (review.selectors?.presetProfileSource || '-')}</div>
|
||||
<div><strong>MIN_LENGTH_MINUTES:</strong> {review.minLengthMinutes}</div>
|
||||
<div><strong>Encode Input:</strong> {encodeInputTitle?.fileName || '-'}</div>
|
||||
<div><strong>Audio Auswahl:</strong> {review.selectors?.audio?.mode || '-'}</div>
|
||||
<div><strong>Audio Encoder:</strong> {(review.selectors?.audio?.encoders || []).join(', ') || 'Preset-Default'}</div>
|
||||
<div><strong>Audio Copy-Mask:</strong> {(review.selectors?.audio?.copyMask || []).join(', ') || '-'}</div>
|
||||
<div><strong>Audio Fallback:</strong> {review.selectors?.audio?.fallbackEncoder || '-'}</div>
|
||||
<div><strong>Audio Auswahl:</strong> {effectiveAudioSelector?.mode || '-'}</div>
|
||||
<div><strong>Audio Encoder:</strong> {(effectiveAudioSelector?.encoders || []).join(', ') || 'Preset-Default'}</div>
|
||||
<div><strong>Audio Copy-Mask:</strong> {(effectiveAudioSelector?.copyMask || []).join(', ') || '-'}</div>
|
||||
<div><strong>Audio Fallback:</strong> {effectiveAudioSelector?.fallbackEncoder || '-'}</div>
|
||||
<div><strong>Subtitle Auswahl:</strong> {review.selectors?.subtitle?.mode || '-'}</div>
|
||||
<div><strong>Subtitle Flags:</strong> {review.selectors?.subtitle?.forcedOnly ? 'forced-only' : '-'}{review.selectors?.subtitle?.burnBehavior === 'first' ? ' + burned(first)' : ''}</div>
|
||||
</div>
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
<div>
|
||||
<h4>{`RAW (${previewRawExisting.length}/${previewRawPaths.length})`}</h4>
|
||||
{previewRawPaths.length > 0 ? (
|
||||
<ul className="history-delete-preview-list">
|
||||
{previewRawPaths.map((item) => (
|
||||
<li key={`delete-raw-${item.path}`}>
|
||||
<span className={item.exists ? 'exists-yes' : 'exists-no'}>
|
||||
{item.exists ? 'vorhanden' : 'nicht gefunden'}
|
||||
</span>
|
||||
{' '}| {item.path}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
{previewRawPaths.length > 0 ? (() => {
|
||||
const display = previewRawPaths.filter(p => p.exists).length > 0
|
||||
? previewRawPaths.filter(p => p.exists)
|
||||
: previewRawPaths.slice(0, 1);
|
||||
return (
|
||||
<ul className="history-delete-preview-list">
|
||||
{display.map((item) => (
|
||||
<li key={`delete-raw-${item.path}`}>
|
||||
<span className={item.exists ? 'exists-yes' : 'exists-no'}>
|
||||
{item.exists ? 'vorhanden' : 'nicht gefunden'}
|
||||
</span>
|
||||
{' '}| {item.path}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
})() : (
|
||||
<small className="history-dv-subtle">Keine RAW-Pfade.</small>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4>{`${deleteEntryOutputShortLabel} (${previewMovieExisting.length}/${previewMoviePaths.length})`}</h4>
|
||||
{previewMoviePaths.length > 0 ? (
|
||||
<ul className="history-delete-preview-list">
|
||||
{previewMoviePaths.map((item) => (
|
||||
<li key={`delete-movie-${item.path}`}>
|
||||
<span className={item.exists ? 'exists-yes' : 'exists-no'}>
|
||||
{item.exists ? 'vorhanden' : 'nicht gefunden'}
|
||||
</span>
|
||||
{' '}| {item.path}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
{previewMoviePaths.length > 0 ? (() => {
|
||||
const display = previewMoviePaths.filter(p => p.exists).length > 0
|
||||
? previewMoviePaths.filter(p => p.exists)
|
||||
: previewMoviePaths.slice(0, 1);
|
||||
return (
|
||||
<ul className="history-delete-preview-list">
|
||||
{display.map((item) => (
|
||||
<li key={`delete-movie-${item.path}`}>
|
||||
<span className={item.exists ? 'exists-yes' : 'exists-no'}>
|
||||
{item.exists ? 'vorhanden' : 'nicht gefunden'}
|
||||
</span>
|
||||
{' '}| {item.path}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
})() : (
|
||||
<small className="history-dv-subtle">Keine Movie-Pfade.</small>
|
||||
)}
|
||||
</div>
|
||||
@@ -1187,6 +1200,15 @@ export default function HistoryPage() {
|
||||
loading={deleteEntryTargetBusy === 'both'}
|
||||
disabled={deleteTargetActionsDisabled}
|
||||
/>
|
||||
<Button
|
||||
label="Nur Eintrag löschen"
|
||||
icon="pi pi-database"
|
||||
severity="secondary"
|
||||
outlined
|
||||
onClick={() => confirmDeleteEntry('none')}
|
||||
loading={deleteEntryTargetBusy === 'none'}
|
||||
disabled={deleteTargetActionsDisabled}
|
||||
/>
|
||||
<Button
|
||||
label="Abbrechen"
|
||||
severity="secondary"
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "ripster",
|
||||
"version": "0.9.1-4",
|
||||
"version": "0.9.1-5",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "ripster",
|
||||
"version": "0.9.1-4",
|
||||
"version": "0.9.1-5",
|
||||
"devDependencies": {
|
||||
"concurrently": "^9.1.2"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "ripster",
|
||||
"private": true,
|
||||
"version": "0.9.1-4",
|
||||
"version": "0.9.1-5",
|
||||
"scripts": {
|
||||
"dev": "concurrently \"npm run dev --prefix backend\" \"npm run dev --prefix frontend\"",
|
||||
"dev:backend": "npm run dev --prefix backend",
|
||||
|
||||
Reference in New Issue
Block a user