0.12.0-18 Tests and Converter Fixes
This commit is contained in:
@@ -339,7 +339,13 @@ class ConverterPlugin extends SourcePlugin {
|
||||
cmd: handBrakeConfig.cmd, args: handBrakeConfig.args,
|
||||
jobId: job?.id,
|
||||
progressParser: parseHandBrakeProgress,
|
||||
onProgress: (pct, statusText) => ctx.emitProgress(Math.round(pct), statusText || `Encoding ${pct}%`),
|
||||
onProgress: (pct, statusText, eta) => ctx.emitProgress(
|
||||
Math.round(pct),
|
||||
statusText || `Encoding ${pct}%`,
|
||||
eta ?? null
|
||||
),
|
||||
appendLogLine: ctx?.extra?.appendLogLine,
|
||||
logSource: 'HANDBRAKE',
|
||||
isCancelled,
|
||||
onProcessHandle,
|
||||
context: { jobId: job?.id, source: 'ConverterPlugin.encode', stage: 'ENCODING' }
|
||||
@@ -451,6 +457,8 @@ class ConverterPlugin extends SourcePlugin {
|
||||
await _spawnAndWait({
|
||||
cmd: encodeConfig.cmd, args: encodeConfig.args,
|
||||
jobId: job?.id,
|
||||
appendLogLine: ctx?.extra?.appendLogLine,
|
||||
logSource: 'FFMPEG',
|
||||
isCancelled,
|
||||
onProcessHandle,
|
||||
context: { jobId: job?.id, source: 'ConverterPlugin.encode', stage: 'ENCODING' }
|
||||
@@ -504,6 +512,8 @@ class ConverterPlugin extends SourcePlugin {
|
||||
await _spawnAndWait({
|
||||
cmd: encodeConfig.cmd, args: encodeConfig.args,
|
||||
jobId: job?.id,
|
||||
appendLogLine: ctx?.extra?.appendLogLine,
|
||||
logSource: 'FFMPEG',
|
||||
isCancelled,
|
||||
onProcessHandle,
|
||||
context: { jobId: job?.id, source: 'ConverterPlugin.encode', stage: 'ENCODING' }
|
||||
@@ -536,6 +546,8 @@ class ConverterPlugin extends SourcePlugin {
|
||||
await _spawnAndWait({
|
||||
cmd: encodeConfig.cmd, args: encodeConfig.args,
|
||||
jobId: job?.id,
|
||||
appendLogLine: ctx?.extra?.appendLogLine,
|
||||
logSource: 'FFMPEG',
|
||||
isCancelled,
|
||||
onProcessHandle,
|
||||
context: { jobId: job?.id, source: 'ConverterPlugin.encode', stage: 'ENCODING' }
|
||||
@@ -591,25 +603,45 @@ class ConverterPlugin extends SourcePlugin {
|
||||
}
|
||||
}
|
||||
|
||||
async function _spawnAndWait({ cmd, args, jobId, progressParser, onProgress, isCancelled, onProcessHandle, context }) {
|
||||
async function _spawnAndWait({
|
||||
cmd,
|
||||
args,
|
||||
jobId,
|
||||
progressParser,
|
||||
onProgress,
|
||||
appendLogLine,
|
||||
logSource = 'PROCESS',
|
||||
isCancelled,
|
||||
onProcessHandle,
|
||||
context
|
||||
}) {
|
||||
if (typeof isCancelled === 'function' && isCancelled()) {
|
||||
const err = new Error('Job wurde vom Benutzer abgebrochen.');
|
||||
err.statusCode = 409;
|
||||
throw err;
|
||||
}
|
||||
|
||||
const handleLine = (stream, line) => {
|
||||
if (progressParser && typeof onProgress === 'function') {
|
||||
const progress = progressParser(line);
|
||||
if (progress?.percent != null) {
|
||||
onProgress(progress.percent, progress.detail || null, progress.eta ?? null);
|
||||
}
|
||||
}
|
||||
if (typeof appendLogLine === 'function') {
|
||||
try {
|
||||
appendLogLine(logSource, line, stream);
|
||||
} catch (_error) {
|
||||
// ignore log append errors in process stream
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handle = spawnTrackedProcess({
|
||||
cmd,
|
||||
args,
|
||||
onStdoutLine: () => {},
|
||||
onStderrLine: (line) => {
|
||||
if (progressParser && typeof onProgress === 'function') {
|
||||
const progress = progressParser(line);
|
||||
if (progress?.percent != null) {
|
||||
onProgress(progress.percent, progress.detail || null);
|
||||
}
|
||||
}
|
||||
},
|
||||
onStdoutLine: (line) => handleLine('stdout', line),
|
||||
onStderrLine: (line) => handleLine('stderr', line),
|
||||
context: context || { jobId }
|
||||
});
|
||||
|
||||
|
||||
@@ -280,6 +280,7 @@ router.post(
|
||||
const selectedPreEncodeChainIds = req.body?.selectedPreEncodeChainIds;
|
||||
const skipPipelineStateUpdate = Boolean(req.body?.skipPipelineStateUpdate);
|
||||
const selectedUserPresetId = req.body?.selectedUserPresetId ?? null;
|
||||
const selectedHandBrakePreset = req.body?.selectedHandBrakePreset ?? null;
|
||||
logger.info('post:confirm-encode', {
|
||||
reqId: req.reqId,
|
||||
jobId,
|
||||
@@ -287,6 +288,7 @@ router.post(
|
||||
selectedTrackSelectionProvided: Boolean(selectedTrackSelection),
|
||||
skipPipelineStateUpdate,
|
||||
selectedUserPresetId,
|
||||
selectedHandBrakePreset,
|
||||
selectedPostEncodeScriptIdsCount: Array.isArray(selectedPostEncodeScriptIds)
|
||||
? selectedPostEncodeScriptIds.length
|
||||
: 0,
|
||||
@@ -308,7 +310,8 @@ router.post(
|
||||
selectedPostEncodeChainIds,
|
||||
selectedPreEncodeChainIds,
|
||||
skipPipelineStateUpdate,
|
||||
selectedUserPresetId
|
||||
selectedUserPresetId,
|
||||
selectedHandBrakePreset
|
||||
});
|
||||
res.json({ job });
|
||||
})
|
||||
|
||||
@@ -22,7 +22,7 @@ function parseJsonSafe(raw, fallback = null) {
|
||||
|
||||
const PROCESS_LOG_TAIL_MAX_BYTES = 1024 * 1024;
|
||||
const processLogStreams = new Map();
|
||||
const PROFILE_PATH_SUFFIXES = ['bluray', 'dvd', 'cd', 'audiobook', 'other'];
|
||||
const PROFILE_PATH_SUFFIXES = ['bluray', 'dvd', 'cd', 'audiobook', 'converter', 'other'];
|
||||
const RAW_INCOMPLETE_PREFIX = 'Incomplete_';
|
||||
const RAW_RIP_COMPLETE_PREFIX = 'Rip_Complete_';
|
||||
const PROCESS_LOG_FILE_PATTERN = /^job-(\d+)\.process\.log$/i;
|
||||
@@ -329,7 +329,27 @@ function inferMediaType(job, makemkvInfo, mediainfoInfo, encodePlan, handbrakeIn
|
||||
// Converter jobs: detected via media_type field (plan.mediaProfile = 'converter')
|
||||
const rawMediaType = normalizeMediaTypeValue(job?.media_type);
|
||||
const rawPlanProfile = String(plan?.mediaProfile || '').trim().toLowerCase();
|
||||
if (rawPlanProfile === 'converter' || rawMediaType === 'converter') {
|
||||
const converterMediaTypeHint = String(plan?.converterMediaType || '').trim().toLowerCase();
|
||||
const hasConverterPathHint = (value) => {
|
||||
const normalized = String(value || '')
|
||||
.trim()
|
||||
.replace(/\\/g, '/')
|
||||
.toLowerCase();
|
||||
if (!normalized) {
|
||||
return false;
|
||||
}
|
||||
return /(^|\/)converter(\/|$)/.test(normalized);
|
||||
};
|
||||
if (
|
||||
rawPlanProfile === 'converter'
|
||||
|| rawMediaType === 'converter'
|
||||
|| converterMediaTypeHint === 'video'
|
||||
|| converterMediaTypeHint === 'audio'
|
||||
|| converterMediaTypeHint === 'iso'
|
||||
|| hasConverterPathHint(rawPath)
|
||||
|| hasConverterPathHint(encodeInputPath)
|
||||
|| hasConverterPathHint(plan?.inputPath)
|
||||
) {
|
||||
return 'converter';
|
||||
}
|
||||
|
||||
@@ -630,12 +650,17 @@ function enrichJobRow(job, settings = null, options = {}) {
|
||||
const mediainfoInfo = resolvedPaths.mediainfoInfo;
|
||||
const encodePlan = resolvedPaths.encodePlan;
|
||||
const mediaType = resolvedPaths.mediaType;
|
||||
const normalizedJobStatus = String(job?.status || '').trim().toUpperCase();
|
||||
const ripSuccessful = Number(job?.rip_successful || 0) === 1
|
||||
|| String(makemkvInfo?.status || '').trim().toUpperCase() === 'SUCCESS';
|
||||
const backupSuccess = ripSuccessful;
|
||||
const finishedWithOutput = normalizedJobStatus === 'FINISHED'
|
||||
&& (includeFsChecks ? Boolean(outputStatus?.exists) : true);
|
||||
const encodeSuccess = directoryLikeOutput
|
||||
? (String(job?.status || '').trim().toUpperCase() === 'FINISHED' && Boolean(outputStatus?.exists))
|
||||
: String(handbrakeInfo?.status || '').trim().toUpperCase() === 'SUCCESS';
|
||||
? finishedWithOutput
|
||||
: (mediaType === 'converter'
|
||||
? finishedWithOutput
|
||||
: String(handbrakeInfo?.status || '').trim().toUpperCase() === 'SUCCESS');
|
||||
|
||||
return {
|
||||
...job,
|
||||
@@ -3067,7 +3092,33 @@ class HistoryService {
|
||||
`
|
||||
SELECT *
|
||||
FROM jobs
|
||||
WHERE status IN ('ANALYZING', 'RIPPING', 'ENCODING', 'MEDIAINFO_CHECK', 'READY_TO_ENCODE', 'CD_ANALYZING', 'CD_READY_TO_RIP', 'CD_RIPPING', 'CD_ENCODING')
|
||||
WHERE status IN ('ANALYZING', 'RIPPING', 'ENCODING', 'MEDIAINFO_CHECK', 'CD_ANALYZING', 'CD_RIPPING', 'CD_ENCODING')
|
||||
ORDER BY updated_at ASC, id ASC
|
||||
`
|
||||
),
|
||||
settingsService.getSettingsMap()
|
||||
]);
|
||||
return rows.map((job) => ({
|
||||
...enrichJobRow(job, settings),
|
||||
log_count: hasProcessLogFile(job.id) ? 1 : 0
|
||||
}));
|
||||
}
|
||||
|
||||
async getQueueIdleJobs() {
|
||||
const db = await getDb();
|
||||
const [rows, settings] = await Promise.all([
|
||||
db.all(
|
||||
`
|
||||
SELECT *
|
||||
FROM jobs
|
||||
WHERE status IN (
|
||||
'METADATA_SELECTION',
|
||||
'WAITING_FOR_USER_DECISION',
|
||||
'READY_TO_START',
|
||||
'READY_TO_ENCODE',
|
||||
'CD_METADATA_SELECTION',
|
||||
'CD_READY_TO_RIP'
|
||||
)
|
||||
ORDER BY updated_at ASC, id ASC
|
||||
`
|
||||
),
|
||||
@@ -4041,6 +4092,7 @@ class HistoryService {
|
||||
}
|
||||
|
||||
const artifactMoviePathSet = new Set(artifactMoviePaths);
|
||||
const includeMovieParentCandidate = resolvedPaths?.mediaType !== 'converter';
|
||||
for (const outputPath of explicitMoviePaths) {
|
||||
addCandidate(
|
||||
movieCandidates,
|
||||
@@ -4050,7 +4102,7 @@ class HistoryService {
|
||||
movieAllowedPaths
|
||||
);
|
||||
const parentDir = toNormalizedPath(path.dirname(outputPath));
|
||||
if (parentDir && !movieRoots.includes(parentDir)) {
|
||||
if (includeMovieParentCandidate && parentDir && !movieRoots.includes(parentDir)) {
|
||||
addCandidate(
|
||||
movieCandidates,
|
||||
'movie',
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user