0.13.1 DVD Series rc1

This commit is contained in:
2026-04-11 21:26:28 +00:00
parent 1f8e6d3ce8
commit bc193db50a
14 changed files with 387 additions and 30 deletions
+63 -4
View File
@@ -1159,6 +1159,29 @@ function normalizePositiveIntegerOrNull(value) {
return Math.trunc(parsed);
}
function isSeriesBatchEpisodeSubJobPlan(encodePlan = null) {
const plan = encodePlan && typeof encodePlan === 'object' ? encodePlan : {};
if (Boolean(plan?.seriesBatchChild) || Boolean(plan?.seriesBatchVirtualEpisode)) {
return true;
}
const parentJobId = normalizePositiveIntegerOrNull(plan?.seriesBatchParentJobId);
const titleId = normalizePositiveIntegerOrNull(plan?.seriesBatchTitleId);
const childIndex = normalizePositiveIntegerOrNull(plan?.seriesBatchChildIndex);
const childCount = normalizePositiveIntegerOrNull(plan?.seriesBatchChildCount);
if (parentJobId && (titleId || childIndex || childCount)) {
return true;
}
return false;
}
function isSeriesBatchEpisodeSubJobRow(row = null) {
const source = row && typeof row === 'object' ? row : {};
const plan = source?.encodePlan && typeof source.encodePlan === 'object'
? source.encodePlan
: parseJsonSafe(source?.encode_plan_json, null);
return isSeriesBatchEpisodeSubJobPlan(plan);
}
function extractSeasonNumberFromText(value) {
const text = String(value || '').trim();
if (!text) {
@@ -3675,8 +3698,24 @@ class HistoryService {
}
const allContainerChildRows = [...childRows, ...nestedChildRows];
const directDiskRowsByContainerId = new Map();
const directEpisodeRowsByContainerId = new Map();
const directChildRowById = new Map();
for (const row of childRows) {
const containerId = normalizeJobIdValue(row?.parent_job_id);
const isEpisodeSubJob = isSeriesBatchEpisodeSubJobRow(row);
if (containerId) {
const map = isEpisodeSubJob
? directEpisodeRowsByContainerId
: directDiskRowsByContainerId;
if (!map.has(containerId)) {
map.set(containerId, []);
}
map.get(containerId).push(row);
}
if (isEpisodeSubJob) {
continue;
}
const childId = normalizeJobIdValue(row?.id);
if (childId) {
directChildRowById.set(childId, row);
@@ -3737,8 +3776,9 @@ class HistoryService {
const episodeCount = Number(selectedMetadata?.episodeCount || 0);
const episodesLength = Array.isArray(selectedMetadata?.episodes) ? selectedMetadata.episodes.length : 0;
const expectedTotal = episodeCount > 0 ? episodeCount : (episodesLength > 0 ? episodesLength : 0);
const childIds = childRows
.filter((row) => normalizeJobIdValue(row?.parent_job_id) === containerId)
const containerChildRows = directDiskRowsByContainerId.get(containerId) || [];
const containerEpisodeRows = directEpisodeRowsByContainerId.get(containerId) || [];
const childIds = containerChildRows
.map((row) => normalizeJobIdValue(row?.id))
.filter(Boolean);
let rawExistsAny = false;
@@ -3851,6 +3891,22 @@ class HistoryService {
}
}
}
for (const episodeRow of containerEpisodeRows) {
const episodeRowId = normalizeJobIdValue(episodeRow?.id);
if (!episodeRowId) {
continue;
}
const outputs = Array.from(childOutputMap.get(episodeRowId) || []);
for (const outputPath of outputs) {
if (!outputPath || seenOutputs.has(outputPath)) {
continue;
}
seenOutputs.add(outputPath);
if (!includeFsChecks || fs.existsSync(outputPath)) {
existingCount += 1;
}
}
}
if (existingCountFromSeriesBatch > existingCount) {
existingCount = existingCountFromSeriesBatch;
}
@@ -4319,6 +4375,10 @@ class HistoryService {
const childJobs = await Promise.all(
childRows.map((row) => this.repairImportedOrphanJobClassification(row, settings))
);
const isSeriesContainer = String(job?.job_kind || '').trim().toLowerCase() === 'dvd_series_container';
const detailChildJobs = isSeriesContainer
? childJobs.filter((child) => !isSeriesBatchEpisodeSubJobRow(child))
: childJobs;
const parsedTail = Number(options.logTailLines);
const logTailLines = Number.isFinite(parsedTail) && parsedTail > 0
? Math.trunc(parsedTail)
@@ -4330,7 +4390,7 @@ class HistoryService {
const hasProcessLog = (!shouldLoadLogs && includeFsChecks) ? hasProcessLogFile(jobId) : false;
const baseLogCount = hasProcessLog ? 1 : 0;
const enrichedChildren = await Promise.all(
childJobs.map(async (child) => {
detailChildJobs.map(async (child) => {
const base = {
...enrichJobRow(child, settings, { includeFsChecks }),
log_count: includeFsChecks ? (hasProcessLogFile(child.id) ? 1 : 0) : 0
@@ -4365,7 +4425,6 @@ class HistoryService {
})
);
const isSeriesContainer = String(job?.job_kind || '').trim().toLowerCase() === 'dvd_series_container';
let seriesChildSummary = null;
if (isSeriesContainer && enrichedChildren.length > 0) {
const total = enrichedChildren.length;