0.13.1-7 Fix Series

This commit is contained in:
2026-04-14 13:28:03 +00:00
parent 9297a84eea
commit c89a93ed05
17 changed files with 174 additions and 63 deletions
+63 -15
View File
@@ -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