0.10.2-13 DVD Media Info Fix
This commit is contained in:
@@ -8154,8 +8154,10 @@ class PipelineService extends EventEmitter {
|
||||
|
||||
const mediaInfoByPath = {};
|
||||
const mediaInfoRuns = [];
|
||||
let effectiveMediaFiles = mediaFiles;
|
||||
|
||||
const buildReviewSnapshot = (processedCount) => {
|
||||
const processedFiles = mediaFiles
|
||||
const processedFiles = effectiveMediaFiles
|
||||
.slice(0, processedCount)
|
||||
.filter((item) => Boolean(mediaInfoByPath[item.path]));
|
||||
|
||||
@@ -8178,73 +8180,95 @@ class PipelineService extends EventEmitter {
|
||||
mediaProfile,
|
||||
sourceJobId: options.sourceJobId || null,
|
||||
reviewConfirmed: false,
|
||||
partial: processedFiles.length < mediaFiles.length,
|
||||
partial: processedFiles.length < effectiveMediaFiles.length,
|
||||
processedFiles: processedFiles.length,
|
||||
totalFiles: mediaFiles.length
|
||||
totalFiles: effectiveMediaFiles.length
|
||||
};
|
||||
};
|
||||
|
||||
for (let i = 0; i < mediaFiles.length; i += 1) {
|
||||
const file = mediaFiles[i];
|
||||
const percent = Number((((i + 1) / mediaFiles.length) * 100).toFixed(2));
|
||||
await this.updateProgress('MEDIAINFO_CHECK', percent, null, `Mediainfo ${i + 1}/${mediaFiles.length}: ${path.basename(file.path)}`, jobId);
|
||||
// For DVD sources, use HandBrake scan directly instead of mediainfo (mediainfo
|
||||
// is unreliable on VOB files and often returns incomplete track information).
|
||||
const isDvdSource = rawMedia.source === 'dvd'
|
||||
|| rawMedia.source === 'dvd_image'
|
||||
|| rawMedia.source === 'single_extensionless';
|
||||
let dvdHandBrakeScanJson = null;
|
||||
let dvdHandBrakeScanInputPath = null;
|
||||
|
||||
const result = await this.runMediainfoForFile(jobId, file.path, {
|
||||
if (isDvdSource && mediaFiles.length > 0) {
|
||||
// For 'dvd': scan the folder that contains VIDEO_TS (parent of the VIDEO_TS dir).
|
||||
// For image/extensionless: scan the file directly.
|
||||
dvdHandBrakeScanInputPath = rawMedia.source === 'dvd'
|
||||
? path.dirname(path.dirname(mediaFiles[0].path))
|
||||
: mediaFiles[0].path;
|
||||
|
||||
await historyService.appendLog(
|
||||
jobId,
|
||||
'SYSTEM',
|
||||
`DVD-Analyse via HandBrake Scan: ${path.basename(dvdHandBrakeScanInputPath)}`
|
||||
);
|
||||
await this.updateProgress('MEDIAINFO_CHECK', 10, null, 'HandBrake DVD-Scan läuft', jobId);
|
||||
|
||||
const dvdScanLines = [];
|
||||
const dvdScanConfig = await settingsService.buildHandBrakeScanConfigForInput(dvdHandBrakeScanInputPath, {
|
||||
mediaProfile,
|
||||
settingsMap: settings
|
||||
});
|
||||
let parsedMediaInfo = result.parsed;
|
||||
let fallbackRunInfo = null;
|
||||
if (shouldRunDvdTrackFallback(parsedMediaInfo, mediaProfile, file.path)) {
|
||||
try {
|
||||
const fallback = await this.runDvdTrackFallbackForFile(jobId, file.path, {
|
||||
mediaProfile,
|
||||
settingsMap: settings
|
||||
});
|
||||
if (fallback?.parsedMediaInfo) {
|
||||
parsedMediaInfo = fallback.parsedMediaInfo;
|
||||
fallbackRunInfo = fallback.runInfo || null;
|
||||
const audioCount = Array.isArray(fallback?.titleInfo?.audioTracks)
|
||||
? fallback.titleInfo.audioTracks.length
|
||||
: 0;
|
||||
const subtitleCount = Array.isArray(fallback?.titleInfo?.subtitleTracks)
|
||||
? fallback.titleInfo.subtitleTracks.length
|
||||
: 0;
|
||||
await historyService.appendLog(
|
||||
jobId,
|
||||
'SYSTEM',
|
||||
`DVD Track-Fallback aktiv (${path.basename(file.path)}): Audio=${audioCount}, Subtitle=${subtitleCount}.`
|
||||
);
|
||||
} else {
|
||||
await historyService.appendLog(
|
||||
jobId,
|
||||
'SYSTEM',
|
||||
`DVD Track-Fallback ohne Ergebnis (${path.basename(file.path)}).`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn('mediainfo:track-fallback:failed', {
|
||||
jobId,
|
||||
inputPath: file.path,
|
||||
error: errorToMeta(error)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
mediaInfoByPath[file.path] = parsedMediaInfo;
|
||||
mediaInfoRuns.push({
|
||||
filePath: file.path,
|
||||
runInfo: result.runInfo,
|
||||
fallbackRunInfo
|
||||
logger.info('mediainfo:review:dvd-handbrake-scan:command', {
|
||||
jobId,
|
||||
dvdHandBrakeScanInputPath,
|
||||
cmd: dvdScanConfig.cmd,
|
||||
args: dvdScanConfig.args
|
||||
});
|
||||
|
||||
const partialReview = buildReviewSnapshot(i + 1);
|
||||
const dvdScanRunInfo = await this.runCommand({
|
||||
jobId,
|
||||
stage: 'MEDIAINFO_CHECK',
|
||||
source: 'HANDBRAKE_SCAN_DVD',
|
||||
cmd: dvdScanConfig.cmd,
|
||||
args: dvdScanConfig.args,
|
||||
collectLines: dvdScanLines,
|
||||
collectStderrLines: false
|
||||
});
|
||||
|
||||
dvdHandBrakeScanJson = parseMediainfoJsonOutput(dvdScanLines.join('\n'));
|
||||
if (dvdHandBrakeScanJson) {
|
||||
const titleInfo = parseHandBrakeSelectedTitleInfo(dvdHandBrakeScanJson);
|
||||
if (titleInfo) {
|
||||
const syntheticMediaInfo = buildSyntheticMediaInfoFromMakeMkvTitle(titleInfo);
|
||||
mediaInfoByPath[dvdHandBrakeScanInputPath] = syntheticMediaInfo;
|
||||
effectiveMediaFiles = [{ path: dvdHandBrakeScanInputPath, size: 0 }];
|
||||
const audioCount = Array.isArray(titleInfo.audioTracks) ? titleInfo.audioTracks.length : 0;
|
||||
const subtitleCount = Array.isArray(titleInfo.subtitleTracks) ? titleInfo.subtitleTracks.length : 0;
|
||||
await historyService.appendLog(
|
||||
jobId,
|
||||
'SYSTEM',
|
||||
`HandBrake DVD-Scan: Audio=${audioCount}, Subtitle=${subtitleCount}, Dauer=${formatDurationClock(titleInfo.durationSeconds)}`
|
||||
);
|
||||
} else {
|
||||
await historyService.appendLog(
|
||||
jobId,
|
||||
'SYSTEM',
|
||||
'HandBrake DVD-Scan: Kein Titel erkannt, falle auf MediaInfo zurück.'
|
||||
);
|
||||
dvdHandBrakeScanJson = null;
|
||||
}
|
||||
} else {
|
||||
await historyService.appendLog(
|
||||
jobId,
|
||||
'SYSTEM',
|
||||
'HandBrake DVD-Scan: Ausgabe nicht lesbar, falle auf MediaInfo zurück.'
|
||||
);
|
||||
}
|
||||
|
||||
mediaInfoRuns.push({ filePath: dvdHandBrakeScanInputPath, runInfo: dvdScanRunInfo, fallbackRunInfo: null });
|
||||
|
||||
const partialReview = buildReviewSnapshot(1);
|
||||
if (this.isPrimaryJob(jobId)) {
|
||||
await this.setState('MEDIAINFO_CHECK', {
|
||||
activeJobId: jobId,
|
||||
progress: percent,
|
||||
progress: 50,
|
||||
eta: null,
|
||||
statusText: `Mediainfo ${i + 1}/${mediaFiles.length} analysiert: ${path.basename(file.path)}`,
|
||||
statusText: `HandBrake DVD-Scan abgeschlossen: ${path.basename(dvdHandBrakeScanInputPath)}`,
|
||||
context: {
|
||||
jobId,
|
||||
rawPath,
|
||||
@@ -8261,8 +8285,88 @@ class PipelineService extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
// For non-DVD sources (or when HandBrake scan failed), run mediainfo on each file.
|
||||
if (!isDvdSource || !dvdHandBrakeScanJson) {
|
||||
for (let i = 0; i < mediaFiles.length; i += 1) {
|
||||
const file = mediaFiles[i];
|
||||
const percent = Number((((i + 1) / mediaFiles.length) * 100).toFixed(2));
|
||||
await this.updateProgress('MEDIAINFO_CHECK', percent, null, `Mediainfo ${i + 1}/${mediaFiles.length}: ${path.basename(file.path)}`, jobId);
|
||||
|
||||
const result = await this.runMediainfoForFile(jobId, file.path, {
|
||||
mediaProfile,
|
||||
settingsMap: settings
|
||||
});
|
||||
let parsedMediaInfo = result.parsed;
|
||||
let fallbackRunInfo = null;
|
||||
if (shouldRunDvdTrackFallback(parsedMediaInfo, mediaProfile, file.path)) {
|
||||
try {
|
||||
const fallback = await this.runDvdTrackFallbackForFile(jobId, file.path, {
|
||||
mediaProfile,
|
||||
settingsMap: settings
|
||||
});
|
||||
if (fallback?.parsedMediaInfo) {
|
||||
parsedMediaInfo = fallback.parsedMediaInfo;
|
||||
fallbackRunInfo = fallback.runInfo || null;
|
||||
const audioCount = Array.isArray(fallback?.titleInfo?.audioTracks)
|
||||
? fallback.titleInfo.audioTracks.length
|
||||
: 0;
|
||||
const subtitleCount = Array.isArray(fallback?.titleInfo?.subtitleTracks)
|
||||
? fallback.titleInfo.subtitleTracks.length
|
||||
: 0;
|
||||
await historyService.appendLog(
|
||||
jobId,
|
||||
'SYSTEM',
|
||||
`DVD Track-Fallback aktiv (${path.basename(file.path)}): Audio=${audioCount}, Subtitle=${subtitleCount}.`
|
||||
);
|
||||
} else {
|
||||
await historyService.appendLog(
|
||||
jobId,
|
||||
'SYSTEM',
|
||||
`DVD Track-Fallback ohne Ergebnis (${path.basename(file.path)}).`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn('mediainfo:track-fallback:failed', {
|
||||
jobId,
|
||||
inputPath: file.path,
|
||||
error: errorToMeta(error)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
mediaInfoByPath[file.path] = parsedMediaInfo;
|
||||
mediaInfoRuns.push({
|
||||
filePath: file.path,
|
||||
runInfo: result.runInfo,
|
||||
fallbackRunInfo
|
||||
});
|
||||
|
||||
const partialReview = buildReviewSnapshot(i + 1);
|
||||
if (this.isPrimaryJob(jobId)) {
|
||||
await this.setState('MEDIAINFO_CHECK', {
|
||||
activeJobId: jobId,
|
||||
progress: percent,
|
||||
eta: null,
|
||||
statusText: `Mediainfo ${i + 1}/${mediaFiles.length} analysiert: ${path.basename(file.path)}`,
|
||||
context: {
|
||||
jobId,
|
||||
rawPath,
|
||||
inputPath: partialReview?.encodeInputPath || null,
|
||||
hasEncodableTitle: Boolean(partialReview?.encodeInputPath),
|
||||
reviewConfirmed: false,
|
||||
mode: options.mode || 'rip',
|
||||
mediaProfile,
|
||||
sourceJobId: options.sourceJobId || null,
|
||||
mediaInfoReview: partialReview,
|
||||
selectedMetadata
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const review = buildMediainfoReview({
|
||||
mediaFiles,
|
||||
mediaFiles: effectiveMediaFiles,
|
||||
mediaInfoByPath,
|
||||
settings,
|
||||
presetProfile,
|
||||
@@ -8279,8 +8383,8 @@ class PipelineService extends EventEmitter {
|
||||
sourceJobId: options.sourceJobId || null,
|
||||
reviewConfirmed: false,
|
||||
partial: false,
|
||||
processedFiles: mediaFiles.length,
|
||||
totalFiles: mediaFiles.length
|
||||
processedFiles: effectiveMediaFiles.length,
|
||||
totalFiles: effectiveMediaFiles.length
|
||||
};
|
||||
const reviewPrefillResult = applyPreviousSelectionDefaultsToReviewPlan(
|
||||
enrichedReview,
|
||||
@@ -8295,7 +8399,10 @@ class PipelineService extends EventEmitter {
|
||||
|| rawMedia.source === 'dvd_image'
|
||||
|| rawMedia.source === 'single_extensionless';
|
||||
if (needsHandBrakeTitleScan) {
|
||||
const dvdScanInputPath = rawMedia.source === 'dvd' ? rawPath : mediaFiles[0].path;
|
||||
// Use the same path that was used for the DVD HandBrake scan above, or fall back
|
||||
// to the existing logic for cases where the scan was not yet performed.
|
||||
const dvdScanInputPath = dvdHandBrakeScanInputPath
|
||||
|| (rawMedia.source === 'dvd' ? rawPath : mediaFiles[0].path);
|
||||
const preSelectedHandBrakeTitleId = normalizeReviewTitleId(options?.selectedHandBrakeTitleId);
|
||||
if (preSelectedHandBrakeTitleId) {
|
||||
enrichedReview = {
|
||||
@@ -8309,33 +8416,38 @@ class PipelineService extends EventEmitter {
|
||||
`HandBrake Titel-Auswahl (manuell) übernommen: -t ${preSelectedHandBrakeTitleId}`
|
||||
);
|
||||
} else {
|
||||
await historyService.appendLog(
|
||||
jobId,
|
||||
'SYSTEM',
|
||||
'HandBrake Titel-Scan für DVD-Struktur wird gestartet...'
|
||||
);
|
||||
await this.updateProgress('MEDIAINFO_CHECK', 100, null, 'HandBrake DVD Titel-Scan läuft', jobId);
|
||||
const dvdTitleScanLines = [];
|
||||
const dvdTitleScanConfig = await settingsService.buildHandBrakeScanConfigForInput(dvdScanInputPath, {
|
||||
mediaProfile,
|
||||
settingsMap: settings
|
||||
});
|
||||
logger.info('mediainfo:review:dvd-title-scan:command', {
|
||||
jobId,
|
||||
cmd: dvdTitleScanConfig.cmd,
|
||||
args: dvdTitleScanConfig.args,
|
||||
dvdScanInputPath
|
||||
});
|
||||
await this.runCommand({
|
||||
jobId,
|
||||
stage: 'MEDIAINFO_CHECK',
|
||||
source: 'HANDBRAKE_SCAN_DVD_TITLES',
|
||||
cmd: dvdTitleScanConfig.cmd,
|
||||
args: dvdTitleScanConfig.args,
|
||||
collectLines: dvdTitleScanLines,
|
||||
collectStderrLines: false
|
||||
});
|
||||
const dvdTitleScanJson = parseMediainfoJsonOutput(dvdTitleScanLines.join('\n'));
|
||||
// Reuse the HandBrake scan result from the DVD analysis step when available,
|
||||
// otherwise run a fresh scan for title selection.
|
||||
let dvdTitleScanJson = dvdHandBrakeScanJson || null;
|
||||
if (!dvdTitleScanJson) {
|
||||
await historyService.appendLog(
|
||||
jobId,
|
||||
'SYSTEM',
|
||||
'HandBrake Titel-Scan für DVD-Struktur wird gestartet...'
|
||||
);
|
||||
await this.updateProgress('MEDIAINFO_CHECK', 100, null, 'HandBrake DVD Titel-Scan läuft', jobId);
|
||||
const dvdTitleScanLines = [];
|
||||
const dvdTitleScanConfig = await settingsService.buildHandBrakeScanConfigForInput(dvdScanInputPath, {
|
||||
mediaProfile,
|
||||
settingsMap: settings
|
||||
});
|
||||
logger.info('mediainfo:review:dvd-title-scan:command', {
|
||||
jobId,
|
||||
cmd: dvdTitleScanConfig.cmd,
|
||||
args: dvdTitleScanConfig.args,
|
||||
dvdScanInputPath
|
||||
});
|
||||
await this.runCommand({
|
||||
jobId,
|
||||
stage: 'MEDIAINFO_CHECK',
|
||||
source: 'HANDBRAKE_SCAN_DVD_TITLES',
|
||||
cmd: dvdTitleScanConfig.cmd,
|
||||
args: dvdTitleScanConfig.args,
|
||||
collectLines: dvdTitleScanLines,
|
||||
collectStderrLines: false
|
||||
});
|
||||
dvdTitleScanJson = parseMediainfoJsonOutput(dvdTitleScanLines.join('\n'));
|
||||
}
|
||||
if (dvdTitleScanJson) {
|
||||
const allTitles = parseHandBrakeTitleList(dvdTitleScanJson);
|
||||
const minLengthMinutes = Number(settings?.makemkv_min_length_minutes || 0);
|
||||
|
||||
Reference in New Issue
Block a user