merge
This commit is contained in:
@@ -855,6 +855,21 @@ async function migrateSettingsSchemaMetadata(db) {
|
|||||||
logger.info('migrate:settings-schema-category-moved', { key: move.key, category: move.category });
|
logger.info('migrate:settings-schema-category-moved', { key: move.key, category: move.category });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const rawDirCdLabel = 'CD RAW-Ordner';
|
||||||
|
const rawDirCdDescription = 'Basisordner für CD-Rips. Enthält die WAV-Rohdaten (RAW) sowie den encodierten Audio-Output. Leer = Standardpfad (data/output/cd).';
|
||||||
|
const rawDirCdResult = await db.run(
|
||||||
|
`UPDATE settings_schema
|
||||||
|
SET label = ?, description = ?, updated_at = CURRENT_TIMESTAMP
|
||||||
|
WHERE key = 'raw_dir_cd' AND (label != ? OR description != ?)`,
|
||||||
|
[rawDirCdLabel, rawDirCdDescription, rawDirCdLabel, rawDirCdDescription]
|
||||||
|
);
|
||||||
|
if (rawDirCdResult?.changes > 0) {
|
||||||
|
logger.info('migrate:settings-schema-cd-raw-updated', {
|
||||||
|
key: 'raw_dir_cd',
|
||||||
|
label: rawDirCdLabel
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getDb() {
|
async function getDb() {
|
||||||
|
|||||||
@@ -321,6 +321,20 @@ function formatCommandLine(cmd, args = []) {
|
|||||||
return [quoteShellArg(cmd), ...normalizedArgs.map((arg) => quoteShellArg(arg))].join(' ');
|
return [quoteShellArg(cmd), ...normalizedArgs.map((arg) => quoteShellArg(arg))].join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function copyFilePreservingRaw(sourcePath, targetPath) {
|
||||||
|
const rawSource = String(sourcePath || '').trim();
|
||||||
|
const rawTarget = String(targetPath || '').trim();
|
||||||
|
if (!rawSource || !rawTarget) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const source = path.resolve(rawSource);
|
||||||
|
const target = path.resolve(rawTarget);
|
||||||
|
if (source === target) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fs.copyFileSync(source, target);
|
||||||
|
}
|
||||||
|
|
||||||
async function runProcessTracked({
|
async function runProcessTracked({
|
||||||
cmd,
|
cmd,
|
||||||
args,
|
args,
|
||||||
@@ -492,7 +506,7 @@ async function ripAndEncode(options) {
|
|||||||
|
|
||||||
// ── Phase 2: Encode WAVs to target format ─────────────────────────────────
|
// ── Phase 2: Encode WAVs to target format ─────────────────────────────────
|
||||||
if (format === 'wav') {
|
if (format === 'wav') {
|
||||||
// Just move WAV files to output dir with proper names
|
// Keep RAW WAVs in place and copy them to the final output structure.
|
||||||
for (let i = 0; i < tracksToRip.length; i++) {
|
for (let i = 0; i < tracksToRip.length; i++) {
|
||||||
assertNotCancelled(isCancelled);
|
assertNotCancelled(isCancelled);
|
||||||
const track = tracksToRip[i];
|
const track = tracksToRip[i];
|
||||||
@@ -508,8 +522,8 @@ async function ripAndEncode(options) {
|
|||||||
percent: 50 + ((i / tracksToRip.length) * 50)
|
percent: 50 + ((i / tracksToRip.length) * 50)
|
||||||
});
|
});
|
||||||
ensureDir(path.dirname(outFile));
|
ensureDir(path.dirname(outFile));
|
||||||
log('info', `Promptkette [Move ${i + 1}/${tracksToRip.length}]: mv ${quoteShellArg(wavFile)} ${quoteShellArg(outFile)}`);
|
log('info', `Promptkette [Copy ${i + 1}/${tracksToRip.length}]: cp ${quoteShellArg(wavFile)} ${quoteShellArg(outFile)}`);
|
||||||
fs.renameSync(wavFile, outFile);
|
copyFilePreservingRaw(wavFile, outFile);
|
||||||
onProgress && onProgress({
|
onProgress && onProgress({
|
||||||
phase: 'encode',
|
phase: 'encode',
|
||||||
trackEvent: 'complete',
|
trackEvent: 'complete',
|
||||||
|
|||||||
@@ -421,7 +421,7 @@ class HardwareMonitorService {
|
|||||||
} else {
|
} else {
|
||||||
addPath('raw_dir', 'RAW-Verzeichnis', blurayRawPath || dvdRawPath || sourceMap.raw_dir);
|
addPath('raw_dir', 'RAW-Verzeichnis', blurayRawPath || dvdRawPath || sourceMap.raw_dir);
|
||||||
}
|
}
|
||||||
addPath('raw_dir_cd', 'CD-Verzeichnis', cdRawPath || sourceMap.raw_dir_cd);
|
addPath('raw_dir_cd', 'CD RAW-Ordner', cdRawPath || sourceMap.raw_dir_cd);
|
||||||
|
|
||||||
if (blurayMoviePath && dvdMoviePath && blurayMoviePath !== dvdMoviePath) {
|
if (blurayMoviePath && dvdMoviePath && blurayMoviePath !== dvdMoviePath) {
|
||||||
addPath('movie_dir_bluray', 'Movie-Verzeichnis (Blu-ray)', blurayMoviePath);
|
addPath('movie_dir_bluray', 'Movie-Verzeichnis (Blu-ray)', blurayMoviePath);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ function parseJsonSafe(raw, fallback = null) {
|
|||||||
|
|
||||||
const PROCESS_LOG_TAIL_MAX_BYTES = 1024 * 1024;
|
const PROCESS_LOG_TAIL_MAX_BYTES = 1024 * 1024;
|
||||||
const processLogStreams = new Map();
|
const processLogStreams = new Map();
|
||||||
const PROFILE_PATH_SUFFIXES = ['bluray', 'dvd', 'other'];
|
const PROFILE_PATH_SUFFIXES = ['bluray', 'dvd', 'cd', 'other'];
|
||||||
const RAW_INCOMPLETE_PREFIX = 'Incomplete_';
|
const RAW_INCOMPLETE_PREFIX = 'Incomplete_';
|
||||||
const RAW_RIP_COMPLETE_PREFIX = 'Rip_Complete_';
|
const RAW_RIP_COMPLETE_PREFIX = 'Rip_Complete_';
|
||||||
|
|
||||||
@@ -356,12 +356,46 @@ function toProcessLogStreamKey(jobId) {
|
|||||||
return String(Math.trunc(normalizedId));
|
return String(Math.trunc(normalizedId));
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveEffectiveRawPath(storedPath, rawDir) {
|
function resolveEffectiveRawPath(storedPath, rawDir, extraDirs = []) {
|
||||||
const stored = String(storedPath || '').trim();
|
const stored = String(storedPath || '').trim();
|
||||||
if (!stored || !rawDir) return stored;
|
if (!stored) return stored;
|
||||||
const folderName = path.basename(stored);
|
const folderName = path.basename(stored);
|
||||||
if (!folderName) return stored;
|
if (!folderName) return stored;
|
||||||
return path.join(String(rawDir).trim(), folderName);
|
|
||||||
|
const candidates = [];
|
||||||
|
const seen = new Set();
|
||||||
|
const pushCandidate = (candidatePath) => {
|
||||||
|
const normalized = String(candidatePath || '').trim();
|
||||||
|
if (!normalized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const comparable = normalizeComparablePath(normalized);
|
||||||
|
if (!comparable || seen.has(comparable)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
seen.add(comparable);
|
||||||
|
candidates.push(normalized);
|
||||||
|
};
|
||||||
|
|
||||||
|
pushCandidate(stored);
|
||||||
|
if (rawDir) {
|
||||||
|
pushCandidate(path.join(String(rawDir).trim(), folderName));
|
||||||
|
}
|
||||||
|
for (const extraDir of Array.isArray(extraDirs) ? extraDirs : []) {
|
||||||
|
pushCandidate(path.join(String(extraDir || '').trim(), folderName));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const candidate of candidates) {
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
} catch (_error) {
|
||||||
|
// ignore fs errors and continue with fallbacks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rawDir ? path.join(String(rawDir).trim(), folderName) : stored;
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveEffectiveOutputPath(storedPath, movieDir) {
|
function resolveEffectiveOutputPath(storedPath, movieDir) {
|
||||||
@@ -406,11 +440,11 @@ function resolveEffectiveStoragePathsForJob(settings = null, job = {}, parsed =
|
|||||||
const rawDir = String(effectiveSettings?.raw_dir || '').trim();
|
const rawDir = String(effectiveSettings?.raw_dir || '').trim();
|
||||||
const configuredMovieDir = String(effectiveSettings?.movie_dir || '').trim();
|
const configuredMovieDir = String(effectiveSettings?.movie_dir || '').trim();
|
||||||
const movieDir = mediaType === 'cd' ? rawDir : configuredMovieDir;
|
const movieDir = mediaType === 'cd' ? rawDir : configuredMovieDir;
|
||||||
const effectiveRawPath = mediaType === 'cd'
|
const rawLookupDirs = getConfiguredMediaPathList(settings || {}, 'raw_dir')
|
||||||
? (job?.raw_path || null)
|
.filter((candidate) => normalizeComparablePath(candidate) !== normalizeComparablePath(rawDir));
|
||||||
: (rawDir && job?.raw_path
|
const effectiveRawPath = job?.raw_path
|
||||||
? resolveEffectiveRawPath(job.raw_path, rawDir)
|
? resolveEffectiveRawPath(job.raw_path, rawDir, rawLookupDirs)
|
||||||
: (job?.raw_path || null));
|
: (job?.raw_path || null);
|
||||||
const effectiveOutputPath = mediaType === 'cd'
|
const effectiveOutputPath = mediaType === 'cd'
|
||||||
? (job?.output_path || null)
|
? (job?.output_path || null)
|
||||||
: (configuredMovieDir && job?.output_path
|
: (configuredMovieDir && job?.output_path
|
||||||
|
|||||||
@@ -3694,6 +3694,46 @@ class PipelineService extends EventEmitter {
|
|||||||
return existingDirectories[0];
|
return existingDirectories[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buildRawPathLookupConfig(settingsMap = {}, mediaProfile = null) {
|
||||||
|
const sourceMap = settingsMap && typeof settingsMap === 'object' ? settingsMap : {};
|
||||||
|
const normalizedMediaProfile = normalizeMediaProfile(mediaProfile);
|
||||||
|
const effectiveSettings = settingsService.resolveEffectiveToolSettings(sourceMap, normalizedMediaProfile);
|
||||||
|
const preferredDefaultRawDir = normalizedMediaProfile === 'cd'
|
||||||
|
? settingsService.DEFAULT_CD_DIR
|
||||||
|
: settingsService.DEFAULT_RAW_DIR;
|
||||||
|
const uniqueRawDirs = Array.from(
|
||||||
|
new Set(
|
||||||
|
[
|
||||||
|
effectiveSettings?.raw_dir,
|
||||||
|
sourceMap?.raw_dir,
|
||||||
|
sourceMap?.raw_dir_bluray,
|
||||||
|
sourceMap?.raw_dir_dvd,
|
||||||
|
sourceMap?.raw_dir_cd,
|
||||||
|
preferredDefaultRawDir,
|
||||||
|
settingsService.DEFAULT_RAW_DIR,
|
||||||
|
settingsService.DEFAULT_CD_DIR
|
||||||
|
]
|
||||||
|
.map((item) => String(item || '').trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
effectiveSettings,
|
||||||
|
rawBaseDir: uniqueRawDirs[0] || String(preferredDefaultRawDir || '').trim() || null,
|
||||||
|
rawExtraDirs: uniqueRawDirs.slice(1)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveCurrentRawPathForSettings(settingsMap = {}, mediaProfile = null, storedRawPath = null) {
|
||||||
|
const stored = String(storedRawPath || '').trim();
|
||||||
|
if (!stored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const { rawBaseDir, rawExtraDirs } = this.buildRawPathLookupConfig(settingsMap, mediaProfile);
|
||||||
|
return this.resolveCurrentRawPath(rawBaseDir, stored, rawExtraDirs);
|
||||||
|
}
|
||||||
|
|
||||||
async migrateRawFolderNamingOnStartup(db) {
|
async migrateRawFolderNamingOnStartup(db) {
|
||||||
const settings = await settingsService.getSettingsMap();
|
const settings = await settingsService.getSettingsMap();
|
||||||
const rawBaseDir = String(settings?.raw_dir || settingsService.DEFAULT_RAW_DIR || '').trim();
|
const rawBaseDir = String(settings?.raw_dir || settingsService.DEFAULT_RAW_DIR || '').trim();
|
||||||
@@ -5385,15 +5425,17 @@ class PipelineService extends EventEmitter {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const existingPlan = this.safeParseJson(job.encode_plan_json);
|
||||||
const refreshSettings = await settingsService.getSettingsMap();
|
const refreshSettings = await settingsService.getSettingsMap();
|
||||||
const refreshRawBaseDir = settingsService.DEFAULT_RAW_DIR;
|
const refreshMediaProfile = this.resolveMediaProfileForJob(job, {
|
||||||
const refreshRawExtraDirs = [
|
encodePlan: existingPlan,
|
||||||
refreshSettings?.raw_dir_bluray,
|
rawPath: job.raw_path
|
||||||
refreshSettings?.raw_dir_dvd
|
});
|
||||||
].map((d) => String(d || '').trim()).filter(Boolean);
|
const resolvedRefreshRawPath = this.resolveCurrentRawPathForSettings(
|
||||||
const resolvedRefreshRawPath = job.raw_path
|
refreshSettings,
|
||||||
? this.resolveCurrentRawPath(refreshRawBaseDir, job.raw_path, refreshRawExtraDirs)
|
refreshMediaProfile,
|
||||||
: null;
|
job.raw_path
|
||||||
|
);
|
||||||
|
|
||||||
if (!resolvedRefreshRawPath) {
|
if (!resolvedRefreshRawPath) {
|
||||||
return {
|
return {
|
||||||
@@ -5409,7 +5451,6 @@ class PipelineService extends EventEmitter {
|
|||||||
await historyService.updateJob(activeJobId, { raw_path: resolvedRefreshRawPath });
|
await historyService.updateJob(activeJobId, { raw_path: resolvedRefreshRawPath });
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingPlan = this.safeParseJson(job.encode_plan_json);
|
|
||||||
const mode = existingPlan?.mode || this.snapshot.context?.mode || 'rip';
|
const mode = existingPlan?.mode || this.snapshot.context?.mode || 'rip';
|
||||||
const sourceJobId = existingPlan?.sourceJobId || this.snapshot.context?.sourceJobId || null;
|
const sourceJobId = existingPlan?.sourceJobId || this.snapshot.context?.sourceJobId || null;
|
||||||
|
|
||||||
@@ -7339,14 +7380,11 @@ class PipelineService extends EventEmitter {
|
|||||||
encodePlan: confirmedPlan
|
encodePlan: confirmedPlan
|
||||||
});
|
});
|
||||||
const confirmSettings = await settingsService.getEffectiveSettingsMap(readyMediaProfile);
|
const confirmSettings = await settingsService.getEffectiveSettingsMap(readyMediaProfile);
|
||||||
const confirmRawBaseDir = String(confirmSettings?.raw_dir || '').trim();
|
const resolvedConfirmRawPath = this.resolveCurrentRawPathForSettings(
|
||||||
const confirmRawExtraDirs = [
|
confirmSettings,
|
||||||
confirmSettings?.raw_dir_bluray,
|
readyMediaProfile,
|
||||||
confirmSettings?.raw_dir_dvd
|
job.raw_path
|
||||||
].map((d) => String(d || '').trim()).filter(Boolean);
|
);
|
||||||
const resolvedConfirmRawPath = job.raw_path
|
|
||||||
? this.resolveCurrentRawPath(confirmRawBaseDir, job.raw_path, confirmRawExtraDirs)
|
|
||||||
: null;
|
|
||||||
const activeConfirmRawPath = resolvedConfirmRawPath || String(job.raw_path || '').trim() || null;
|
const activeConfirmRawPath = resolvedConfirmRawPath || String(job.raw_path || '').trim() || null;
|
||||||
|
|
||||||
let inputPath = isPreRipMode
|
let inputPath = isPreRipMode
|
||||||
@@ -7460,13 +7498,16 @@ class PipelineService extends EventEmitter {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const reencodeMediaProfile = this.resolveMediaProfileForJob(sourceJob, {
|
||||||
|
makemkvInfo: mkInfo,
|
||||||
|
rawPath: sourceJob.raw_path
|
||||||
|
});
|
||||||
const reencodeSettings = await settingsService.getSettingsMap();
|
const reencodeSettings = await settingsService.getSettingsMap();
|
||||||
const reencodeRawBaseDir = settingsService.DEFAULT_RAW_DIR;
|
const resolvedReencodeRawPath = this.resolveCurrentRawPathForSettings(
|
||||||
const reencodeRawExtraDirs = [
|
reencodeSettings,
|
||||||
reencodeSettings?.raw_dir_bluray,
|
reencodeMediaProfile,
|
||||||
reencodeSettings?.raw_dir_dvd
|
sourceJob.raw_path
|
||||||
].map((d) => String(d || '').trim()).filter(Boolean);
|
);
|
||||||
const resolvedReencodeRawPath = this.resolveCurrentRawPath(reencodeRawBaseDir, sourceJob.raw_path, reencodeRawExtraDirs);
|
|
||||||
if (!resolvedReencodeRawPath) {
|
if (!resolvedReencodeRawPath) {
|
||||||
const error = new Error(`Re-Encode nicht möglich: RAW-Pfad existiert nicht (${sourceJob.raw_path}).`);
|
const error = new Error(`Re-Encode nicht möglich: RAW-Pfad existiert nicht (${sourceJob.raw_path}).`);
|
||||||
error.statusCode = 400;
|
error.statusCode = 400;
|
||||||
@@ -8339,14 +8380,7 @@ class PipelineService extends EventEmitter {
|
|||||||
rawPath: job.raw_path
|
rawPath: job.raw_path
|
||||||
});
|
});
|
||||||
const settings = await settingsService.getEffectiveSettingsMap(mediaProfile);
|
const settings = await settingsService.getEffectiveSettingsMap(mediaProfile);
|
||||||
const rawBaseDir = String(settings.raw_dir || '').trim();
|
const resolvedRawPath = this.resolveCurrentRawPathForSettings(settings, mediaProfile, job.raw_path);
|
||||||
const rawExtraDirs = [
|
|
||||||
settings.raw_dir_bluray,
|
|
||||||
settings.raw_dir_dvd
|
|
||||||
].map((item) => String(item || '').trim()).filter(Boolean);
|
|
||||||
const resolvedRawPath = job.raw_path
|
|
||||||
? this.resolveCurrentRawPath(rawBaseDir, job.raw_path, rawExtraDirs)
|
|
||||||
: null;
|
|
||||||
const activeRawPath = resolvedRawPath || String(job.raw_path || '').trim() || null;
|
const activeRawPath = resolvedRawPath || String(job.raw_path || '').trim() || null;
|
||||||
if (activeRawPath && normalizeComparablePath(activeRawPath) !== normalizeComparablePath(job.raw_path)) {
|
if (activeRawPath && normalizeComparablePath(activeRawPath) !== normalizeComparablePath(job.raw_path)) {
|
||||||
await historyService.updateJob(jobId, { raw_path: activeRawPath });
|
await historyService.updateJob(jobId, { raw_path: activeRawPath });
|
||||||
@@ -9251,14 +9285,15 @@ class PipelineService extends EventEmitter {
|
|||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
const retrySettings = await settingsService.getEffectiveSettingsMap(mediaProfile);
|
const retrySettings = await settingsService.getEffectiveSettingsMap(mediaProfile);
|
||||||
const retryRawBaseDir = String(retrySettings?.raw_dir || '').trim();
|
const { rawBaseDir: retryRawBaseDir, rawExtraDirs: retryRawExtraDirs } = this.buildRawPathLookupConfig(
|
||||||
const retryRawExtraDirs = [
|
retrySettings,
|
||||||
retrySettings?.raw_dir_bluray,
|
mediaProfile
|
||||||
retrySettings?.raw_dir_dvd
|
);
|
||||||
].map((dirPath) => String(dirPath || '').trim()).filter(Boolean);
|
const resolvedOldRawPath = this.resolveCurrentRawPathForSettings(
|
||||||
const resolvedOldRawPath = sourceJob.raw_path
|
retrySettings,
|
||||||
? this.resolveCurrentRawPath(retryRawBaseDir, sourceJob.raw_path, retryRawExtraDirs)
|
mediaProfile,
|
||||||
: null;
|
sourceJob.raw_path
|
||||||
|
);
|
||||||
|
|
||||||
if (resolvedOldRawPath) {
|
if (resolvedOldRawPath) {
|
||||||
const oldRawFolderName = path.basename(resolvedOldRawPath);
|
const oldRawFolderName = path.basename(resolvedOldRawPath);
|
||||||
@@ -9419,15 +9454,13 @@ class PipelineService extends EventEmitter {
|
|||||||
const mode = String(encodePlan?.mode || 'rip').trim().toLowerCase();
|
const mode = String(encodePlan?.mode || 'rip').trim().toLowerCase();
|
||||||
const isPreRipMode = mode === 'pre_rip' || Boolean(encodePlan?.preRip);
|
const isPreRipMode = mode === 'pre_rip' || Boolean(encodePlan?.preRip);
|
||||||
const reviewConfirmed = Boolean(Number(job.encode_review_confirmed || 0) || encodePlan?.reviewConfirmed);
|
const reviewConfirmed = Boolean(Number(job.encode_review_confirmed || 0) || encodePlan?.reviewConfirmed);
|
||||||
const resumeSettings = await settingsService.getEffectiveSettingsMap(this.resolveMediaProfileForJob(job, { encodePlan }));
|
const readyMediaProfile = this.resolveMediaProfileForJob(job, { encodePlan });
|
||||||
const resumeRawBaseDir = String(resumeSettings?.raw_dir || '').trim();
|
const resumeSettings = await settingsService.getEffectiveSettingsMap(readyMediaProfile);
|
||||||
const resumeRawExtraDirs = [
|
const resolvedResumeRawPath = this.resolveCurrentRawPathForSettings(
|
||||||
resumeSettings?.raw_dir_bluray,
|
resumeSettings,
|
||||||
resumeSettings?.raw_dir_dvd
|
readyMediaProfile,
|
||||||
].map((d) => String(d || '').trim()).filter(Boolean);
|
job.raw_path
|
||||||
const resolvedResumeRawPath = job.raw_path
|
);
|
||||||
? this.resolveCurrentRawPath(resumeRawBaseDir, job.raw_path, resumeRawExtraDirs)
|
|
||||||
: null;
|
|
||||||
const activeResumeRawPath = resolvedResumeRawPath || String(job.raw_path || '').trim() || null;
|
const activeResumeRawPath = resolvedResumeRawPath || String(job.raw_path || '').trim() || null;
|
||||||
|
|
||||||
let inputPath = isPreRipMode
|
let inputPath = isPreRipMode
|
||||||
@@ -9463,10 +9496,6 @@ class PipelineService extends EventEmitter {
|
|||||||
imdbId: job.imdb_id || null,
|
imdbId: job.imdb_id || null,
|
||||||
poster: job.poster_url || null
|
poster: job.poster_url || null
|
||||||
};
|
};
|
||||||
const readyMediaProfile = this.resolveMediaProfileForJob(job, {
|
|
||||||
encodePlan
|
|
||||||
});
|
|
||||||
|
|
||||||
await this.setState('READY_TO_ENCODE', {
|
await this.setState('READY_TO_ENCODE', {
|
||||||
activeJobId: jobId,
|
activeJobId: jobId,
|
||||||
progress: 0,
|
progress: 0,
|
||||||
@@ -9613,14 +9642,11 @@ class PipelineService extends EventEmitter {
|
|||||||
encodePlan: restartPlan
|
encodePlan: restartPlan
|
||||||
});
|
});
|
||||||
const restartSettings = await settingsService.getEffectiveSettingsMap(readyMediaProfile);
|
const restartSettings = await settingsService.getEffectiveSettingsMap(readyMediaProfile);
|
||||||
const restartRawBaseDir = String(restartSettings?.raw_dir || '').trim();
|
const resolvedRestartRawPath = this.resolveCurrentRawPathForSettings(
|
||||||
const restartRawExtraDirs = [
|
restartSettings,
|
||||||
restartSettings?.raw_dir_bluray,
|
readyMediaProfile,
|
||||||
restartSettings?.raw_dir_dvd
|
job.raw_path
|
||||||
].map((d) => String(d || '').trim()).filter(Boolean);
|
);
|
||||||
const resolvedRestartRawPath = job.raw_path
|
|
||||||
? this.resolveCurrentRawPath(restartRawBaseDir, job.raw_path, restartRawExtraDirs)
|
|
||||||
: null;
|
|
||||||
const activeRestartRawPath = resolvedRestartRawPath || String(job.raw_path || '').trim() || null;
|
const activeRestartRawPath = resolvedRestartRawPath || String(job.raw_path || '').trim() || null;
|
||||||
|
|
||||||
let inputPath = isPreRipMode
|
let inputPath = isPreRipMode
|
||||||
@@ -9761,13 +9787,19 @@ class PipelineService extends EventEmitter {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const reviewMakemkvInfo = this.safeParseJson(sourceJob.makemkv_info_json);
|
||||||
|
const reviewEncodePlan = this.safeParseJson(sourceJob.encode_plan_json);
|
||||||
|
const reviewMediaProfile = this.resolveMediaProfileForJob(sourceJob, {
|
||||||
|
makemkvInfo: reviewMakemkvInfo,
|
||||||
|
encodePlan: reviewEncodePlan,
|
||||||
|
rawPath: sourceJob.raw_path
|
||||||
|
});
|
||||||
const reviewSettings = await settingsService.getSettingsMap();
|
const reviewSettings = await settingsService.getSettingsMap();
|
||||||
const reviewRawBaseDir = settingsService.DEFAULT_RAW_DIR;
|
const resolvedReviewRawPath = this.resolveCurrentRawPathForSettings(
|
||||||
const reviewRawExtraDirs = [
|
reviewSettings,
|
||||||
reviewSettings?.raw_dir_bluray,
|
reviewMediaProfile,
|
||||||
reviewSettings?.raw_dir_dvd
|
sourceJob.raw_path
|
||||||
].map((d) => String(d || '').trim()).filter(Boolean);
|
);
|
||||||
const resolvedReviewRawPath = this.resolveCurrentRawPath(reviewRawBaseDir, sourceJob.raw_path, reviewRawExtraDirs);
|
|
||||||
if (!resolvedReviewRawPath) {
|
if (!resolvedReviewRawPath) {
|
||||||
const error = new Error(`Review-Neustart nicht möglich: RAW-Pfad existiert nicht (${sourceJob.raw_path}).`);
|
const error = new Error(`Review-Neustart nicht möglich: RAW-Pfad existiert nicht (${sourceJob.raw_path}).`);
|
||||||
error.statusCode = 400;
|
error.statusCode = 400;
|
||||||
|
|||||||
@@ -380,7 +380,7 @@ function PathCategoryTab({ settings, values, errors, dirtyKeys, onChange, effect
|
|||||||
const blurayMovies = ep.bluray?.movies || defaultMovies;
|
const blurayMovies = ep.bluray?.movies || defaultMovies;
|
||||||
const dvdRaw = ep.dvd?.raw || defaultRaw;
|
const dvdRaw = ep.dvd?.raw || defaultRaw;
|
||||||
const dvdMovies = ep.dvd?.movies || defaultMovies;
|
const dvdMovies = ep.dvd?.movies || defaultMovies;
|
||||||
const cdOutput = ep.cd?.raw || defaultCd;
|
const cdRaw = ep.cd?.raw || defaultCd;
|
||||||
|
|
||||||
const isDefault = (path, def) => path === def;
|
const isDefault = (path, def) => path === def;
|
||||||
|
|
||||||
@@ -424,10 +424,10 @@ function PathCategoryTab({ settings, values, errors, dirtyKeys, onChange, effect
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong>CD / Audio</strong></td>
|
<td><strong>CD / Audio (RAW + Output)</strong></td>
|
||||||
<td colSpan={2}>
|
<td colSpan={2}>
|
||||||
<code>{cdOutput}</code>
|
<code>{cdRaw}</code>
|
||||||
{isDefault(cdOutput, defaultCd) && <span className="path-default-badge">Standard</span>}
|
{isDefault(cdRaw, defaultCd) && <span className="path-default-badge">Standard</span>}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -768,20 +768,6 @@ export default function DashboardPage({
|
|||||||
const memoryMetrics = monitoringSample?.memory || null;
|
const memoryMetrics = monitoringSample?.memory || null;
|
||||||
const gpuMetrics = monitoringSample?.gpu || null;
|
const gpuMetrics = monitoringSample?.gpu || null;
|
||||||
const storageMetrics = Array.isArray(monitoringSample?.storage) ? monitoringSample.storage : [];
|
const storageMetrics = Array.isArray(monitoringSample?.storage) ? monitoringSample.storage : [];
|
||||||
const storageGroups = useMemo(() => {
|
|
||||||
const groups = [];
|
|
||||||
const mountMap = new Map();
|
|
||||||
for (const entry of storageMetrics) {
|
|
||||||
const groupKey = entry?.mountPoint || `__no_mount_${entry?.key}`;
|
|
||||||
if (!mountMap.has(groupKey)) {
|
|
||||||
const group = { mountPoint: entry?.mountPoint || null, entries: [], representative: entry };
|
|
||||||
mountMap.set(groupKey, group);
|
|
||||||
groups.push(group);
|
|
||||||
}
|
|
||||||
mountMap.get(groupKey).entries.push(entry);
|
|
||||||
}
|
|
||||||
return groups;
|
|
||||||
}, [storageMetrics]);
|
|
||||||
const cpuPerCoreMetrics = Array.isArray(cpuMetrics?.perCore) ? cpuMetrics.perCore : [];
|
const cpuPerCoreMetrics = Array.isArray(cpuMetrics?.perCore) ? cpuMetrics.perCore : [];
|
||||||
const gpuDevices = Array.isArray(gpuMetrics?.devices) ? gpuMetrics.devices : [];
|
const gpuDevices = Array.isArray(gpuMetrics?.devices) ? gpuMetrics.devices : [];
|
||||||
|
|
||||||
@@ -1868,55 +1854,57 @@ export default function DashboardPage({
|
|||||||
<section className="hardware-monitor-block">
|
<section className="hardware-monitor-block">
|
||||||
<h4>Freier Speicher in Pfaden</h4>
|
<h4>Freier Speicher in Pfaden</h4>
|
||||||
<div className="hardware-storage-list">
|
<div className="hardware-storage-list">
|
||||||
{storageGroups.map((group) => {
|
{storageMetrics.map((entry) => {
|
||||||
const rep = group.representative;
|
const tone = getStorageUsageTone(entry?.usagePercent);
|
||||||
const tone = getStorageUsageTone(rep?.usagePercent);
|
const usagePercent = Number(entry?.usagePercent);
|
||||||
const usagePercent = Number(rep?.usagePercent);
|
|
||||||
const barValue = Number.isFinite(usagePercent)
|
const barValue = Number.isFinite(usagePercent)
|
||||||
? Math.max(0, Math.min(100, usagePercent))
|
? Math.max(0, Math.min(100, usagePercent))
|
||||||
: 0;
|
: 0;
|
||||||
const hasError = group.entries.every((e) => e?.error);
|
const hasError = Boolean(entry?.error);
|
||||||
const groupKey = group.mountPoint || group.entries.map((e) => e?.key).join('-');
|
const entryKey = entry?.key || entry?.path || 'storage-entry';
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={`storage-group-${groupKey}`}
|
key={`storage-entry-${entryKey}`}
|
||||||
className={`hardware-storage-item compact${hasError ? ' has-error' : ''}`}
|
className={`hardware-storage-item compact${hasError ? ' has-error' : ''}`}
|
||||||
>
|
>
|
||||||
<div className="hardware-storage-head">
|
<div className="hardware-storage-head">
|
||||||
<strong>{group.entries.map((e) => e?.label || e?.key || 'Pfad').join(' · ')}</strong>
|
<strong>{entry?.label || entry?.key || 'Pfad'}</strong>
|
||||||
<span className={`hardware-storage-percent tone-${tone}`}>
|
<span className={`hardware-storage-percent tone-${tone}`}>
|
||||||
{hasError ? 'Fehler' : formatPercent(rep?.usagePercent)}
|
{hasError ? 'Fehler' : formatPercent(entry?.usagePercent)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{hasError ? (
|
{hasError ? (
|
||||||
<small className="error-text">{rep?.error}</small>
|
<small className="error-text">{entry?.error}</small>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div className={`hardware-storage-bar tone-${tone}`}>
|
<div className={`hardware-storage-bar tone-${tone}`}>
|
||||||
<ProgressBar value={barValue} showValue={false} />
|
<ProgressBar value={barValue} showValue={false} />
|
||||||
</div>
|
</div>
|
||||||
<div className="hardware-storage-summary">
|
<div className="hardware-storage-summary">
|
||||||
<small>Frei: {formatBytes(rep?.freeBytes)}</small>
|
<small>Frei: {formatBytes(entry?.freeBytes)}</small>
|
||||||
<small>Gesamt: {formatBytes(rep?.totalBytes)}</small>
|
<small>Gesamt: {formatBytes(entry?.totalBytes)}</small>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{group.entries.map((entry) => (
|
<div className="hardware-storage-paths">
|
||||||
<div key={entry?.key} className="hardware-storage-paths">
|
<small className="hardware-storage-label-tag">Pfad:</small>
|
||||||
<small className="hardware-storage-label-tag">{entry?.label || entry?.key}:</small>
|
|
||||||
<small className="hardware-storage-path" title={entry?.path || '-'}>
|
<small className="hardware-storage-path" title={entry?.path || '-'}>
|
||||||
{entry?.path || '-'}
|
{entry?.path || '-'}
|
||||||
</small>
|
</small>
|
||||||
|
{entry?.mountPoint ? (
|
||||||
|
<small className="hardware-storage-path" title={entry.mountPoint}>
|
||||||
|
Mount: {entry.mountPoint}
|
||||||
|
</small>
|
||||||
|
) : null}
|
||||||
{entry?.queryPath && entry.queryPath !== entry.path ? (
|
{entry?.queryPath && entry.queryPath !== entry.path ? (
|
||||||
<small className="hardware-storage-path" title={entry.queryPath}>
|
<small className="hardware-storage-path" title={entry.queryPath}>
|
||||||
(Parent: {entry.queryPath})
|
Parent: {entry.queryPath}
|
||||||
</small>
|
</small>
|
||||||
) : null}
|
) : null}
|
||||||
{entry?.note ? <small className="hardware-storage-path">{entry.note}</small> : null}
|
{entry?.note ? <small className="hardware-storage-path">{entry.note}</small> : null}
|
||||||
</div>
|
</div>
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
Reference in New Issue
Block a user