0.13.1-7 Fix Series
This commit is contained in:
@@ -2356,6 +2356,14 @@ function normalizeJobIdValue(value) {
|
||||
return Math.trunc(parsed);
|
||||
}
|
||||
|
||||
function isSeriesContainerRow(row) {
|
||||
return String(row?.job_kind || '').trim().toLowerCase() === 'dvd_series_container';
|
||||
}
|
||||
|
||||
function isSeriesChildRow(row) {
|
||||
return String(row?.job_kind || '').trim().toLowerCase() === 'dvd_series_child';
|
||||
}
|
||||
|
||||
function normalizeArchiveTarget(value) {
|
||||
const raw = String(value || '').trim().toLowerCase();
|
||||
if (raw === 'raw') {
|
||||
@@ -4771,6 +4779,11 @@ class HistoryService {
|
||||
if (!entry.isDirectory() || isHiddenDirectoryName(entry.name)) {
|
||||
continue;
|
||||
}
|
||||
if (/^incomplete_/i.test(String(entry.name || '').trim())) {
|
||||
// Incomplete RAW folders represent unfinished rips and are intentionally
|
||||
// not re-importable from /database.
|
||||
continue;
|
||||
}
|
||||
|
||||
const rawPath = path.join(rawDir, entry.name);
|
||||
const normalizedPath = normalizeComparablePath(rawPath);
|
||||
@@ -5765,6 +5778,12 @@ class HistoryService {
|
||||
|
||||
const pending = [normalizedJobId];
|
||||
const visited = new Set();
|
||||
const parentRow = normalizeJobIdValue(primary?.parent_job_id) !== null
|
||||
? byId.get(normalizeJobIdValue(primary?.parent_job_id))
|
||||
: null;
|
||||
const isPrimarySeriesChild = isSeriesChildRow(primary)
|
||||
|| (parentRow && isSeriesContainerRow(parentRow));
|
||||
const isPrimarySeriesContainer = isSeriesContainerRow(primary);
|
||||
const enqueue = (value) => {
|
||||
const id = normalizeJobIdValue(value);
|
||||
if (!id || visited.has(id)) {
|
||||
@@ -5785,8 +5804,10 @@ class HistoryService {
|
||||
continue;
|
||||
}
|
||||
|
||||
enqueue(row.parent_job_id);
|
||||
enqueue(parseSourceJobIdFromPlan(row.encode_plan_json));
|
||||
if (!(isPrimarySeriesChild || isPrimarySeriesContainer)) {
|
||||
enqueue(row.parent_job_id);
|
||||
enqueue(parseSourceJobIdFromPlan(row.encode_plan_json));
|
||||
}
|
||||
|
||||
for (const childId of (childrenByParent.get(currentId) || [])) {
|
||||
enqueue(childId);
|
||||
@@ -5795,7 +5816,7 @@ class HistoryService {
|
||||
enqueue(childId);
|
||||
}
|
||||
|
||||
if (includeLogLinks) {
|
||||
if (includeLogLinks && !(isPrimarySeriesChild || isPrimarySeriesContainer)) {
|
||||
try {
|
||||
const processLog = await this.readProcessLogLines(currentId, { includeAll: true });
|
||||
const linkedJobIds = parseRetryLinkedJobIdsFromLogLines(processLog.lines);
|
||||
@@ -5808,6 +5829,25 @@ class HistoryService {
|
||||
}
|
||||
}
|
||||
|
||||
if (isPrimarySeriesChild && parentRow && isSeriesContainerRow(parentRow)) {
|
||||
const parentId = normalizeJobIdValue(parentRow?.id);
|
||||
if (parentId) {
|
||||
const remainingChildren = rows.filter((row) => {
|
||||
const rowId = normalizeJobIdValue(row?.id);
|
||||
if (!rowId) {
|
||||
return false;
|
||||
}
|
||||
if (normalizeJobIdValue(row?.parent_job_id) !== parentId) {
|
||||
return false;
|
||||
}
|
||||
return !visited.has(rowId);
|
||||
});
|
||||
if (remainingChildren.length === 0) {
|
||||
visited.add(parentId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(visited)
|
||||
.map((id) => byId.get(id))
|
||||
.filter(Boolean)
|
||||
@@ -6849,16 +6889,23 @@ class HistoryService {
|
||||
|
||||
const includeRelated = Boolean(options?.includeRelated);
|
||||
if (!includeRelated) {
|
||||
const existing = await this.getJobById(jobId);
|
||||
const resolved = await this.getJobByIdOrReplacement(jobId);
|
||||
const existing = resolved?.job || null;
|
||||
const normalizedJobId = normalizeJobIdValue(resolved?.resolvedJobId || jobId);
|
||||
if (!existing) {
|
||||
const error = new Error('Job nicht gefunden.');
|
||||
error.statusCode = 404;
|
||||
throw error;
|
||||
}
|
||||
if (!normalizedJobId) {
|
||||
const error = new Error('Ungültige Job-ID.');
|
||||
error.statusCode = 400;
|
||||
throw error;
|
||||
}
|
||||
|
||||
let fileSummary = null;
|
||||
if (fileTarget !== 'none') {
|
||||
const preview = await this.getJobDeletePreview(jobId, { includeRelated: false });
|
||||
const preview = await this.getJobDeletePreview(normalizedJobId, { includeRelated: false });
|
||||
fileSummary = this._deletePathsFromPreview(preview, fileTarget, {
|
||||
selectedMoviePaths: options?.selectedMoviePaths
|
||||
});
|
||||
@@ -6869,7 +6916,7 @@ class HistoryService {
|
||||
'SELECT state, active_job_id FROM pipeline_state WHERE id = 1'
|
||||
);
|
||||
|
||||
const isActivePipelineJob = Number(pipelineRow?.active_job_id || 0) === Number(jobId);
|
||||
const isActivePipelineJob = Number(pipelineRow?.active_job_id || 0) === Number(normalizedJobId);
|
||||
const runningStates = new Set(['ANALYZING', 'RIPPING', 'MEDIAINFO_CHECK', 'ENCODING', 'CD_ANALYZING', 'CD_RIPPING', 'CD_ENCODING']);
|
||||
|
||||
if (isActivePipelineJob && runningStates.has(String(pipelineRow?.state || ''))) {
|
||||
@@ -6904,23 +6951,23 @@ class HistoryService {
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = 1 AND active_job_id = ?
|
||||
`,
|
||||
[jobId]
|
||||
[normalizedJobId]
|
||||
);
|
||||
}
|
||||
|
||||
await db.run('DELETE FROM jobs WHERE id = ?', [jobId]);
|
||||
await db.run('DELETE FROM jobs WHERE id = ?', [normalizedJobId]);
|
||||
await db.exec('COMMIT');
|
||||
} catch (error) {
|
||||
await db.exec('ROLLBACK');
|
||||
throw error;
|
||||
}
|
||||
|
||||
await this.closeProcessLog(jobId);
|
||||
this._deleteProcessLogFile(jobId);
|
||||
thumbnailService.deleteThumbnail(jobId);
|
||||
await this.closeProcessLog(normalizedJobId);
|
||||
this._deleteProcessLogFile(normalizedJobId);
|
||||
thumbnailService.deleteThumbnail(normalizedJobId);
|
||||
|
||||
logger.warn('job:deleted', {
|
||||
jobId,
|
||||
jobId: normalizedJobId,
|
||||
fileTarget,
|
||||
includeRelated: false,
|
||||
pipelineStateReset: isActivePipelineJob,
|
||||
@@ -6934,15 +6981,16 @@ class HistoryService {
|
||||
|
||||
return {
|
||||
deleted: true,
|
||||
jobId,
|
||||
jobId: normalizedJobId,
|
||||
fileTarget,
|
||||
includeRelated: false,
|
||||
deletedJobIds: [Number(jobId)],
|
||||
deletedJobIds: [Number(normalizedJobId)],
|
||||
fileSummary
|
||||
};
|
||||
}
|
||||
|
||||
const normalizedJobId = normalizeJobIdValue(jobId);
|
||||
const resolved = await this.getJobByIdOrReplacement(jobId);
|
||||
const normalizedJobId = normalizeJobIdValue(resolved?.resolvedJobId || jobId);
|
||||
const preview = await this.getJobDeletePreview(normalizedJobId, { includeRelated: true });
|
||||
const deleteJobIds = Array.isArray(preview?.relatedJobs)
|
||||
? preview.relatedJobs
|
||||
|
||||
@@ -20984,7 +20984,7 @@ class PipelineService extends EventEmitter {
|
||||
|
||||
const shouldAutoRecoverEncode = normalizedStage === 'ENCODING'
|
||||
&& hasConfirmedPlan
|
||||
&& (!isCancelled || resolvedMediaProfile === 'converter');
|
||||
&& resolvedMediaProfile !== 'audiobook';
|
||||
if (shouldAutoRecoverEncode) {
|
||||
try {
|
||||
const recoveryReasonLabel = isCancelled ? 'Abbruch' : 'Fehler';
|
||||
@@ -22782,7 +22782,13 @@ class PipelineService extends EventEmitter {
|
||||
);
|
||||
newRawPath = nextRaw.rawDir;
|
||||
} else {
|
||||
const rawBaseDir = path.dirname(currentRawPath);
|
||||
const rawStorage = resolveSeriesAwareRawStorage(
|
||||
settings || {},
|
||||
mediaProfile,
|
||||
selectedMetadata,
|
||||
mkInfo?.analyzeContext || null
|
||||
);
|
||||
const rawBaseDir = String(rawStorage?.rawBaseDir || '').trim() || path.dirname(currentRawPath);
|
||||
const newMetadataBase = buildRawMetadataBase({
|
||||
title: job.title || job.detected_title || null,
|
||||
year: job.year || null,
|
||||
|
||||
Reference in New Issue
Block a user