0.13.1 DVD Series rc1
This commit is contained in:
@@ -485,6 +485,77 @@ function normalizePositiveInt(value) {
|
||||
return Math.trunc(parsed);
|
||||
}
|
||||
|
||||
function normalizeEpisodeReference(entry = {}) {
|
||||
const source = entry && typeof entry === 'object' ? entry : {};
|
||||
const episodeId = normalizePositiveInt(source?.episodeId ?? source?.id ?? null);
|
||||
const episodeNumber = normalizePositiveInt(source?.episodeNumber ?? source?.number ?? null);
|
||||
const seasonNumber = normalizePositiveInt(source?.seasonNumber ?? source?.season ?? null);
|
||||
if (!episodeId && !episodeNumber) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
episodeId,
|
||||
episodeNumber,
|
||||
seasonNumber
|
||||
};
|
||||
}
|
||||
|
||||
function buildEpisodeReferenceKey(entry = {}) {
|
||||
const normalized = normalizeEpisodeReference(entry);
|
||||
if (!normalized) {
|
||||
return null;
|
||||
}
|
||||
if (normalized.episodeId) {
|
||||
return `id:${normalized.episodeId}`;
|
||||
}
|
||||
if (normalized.seasonNumber && normalized.episodeNumber) {
|
||||
return `se:${normalized.seasonNumber}:${normalized.episodeNumber}`;
|
||||
}
|
||||
return normalized.episodeNumber ? `ep:${normalized.episodeNumber}` : null;
|
||||
}
|
||||
|
||||
function isSeriesBatchEpisodePlan(plan = null) {
|
||||
const source = plan && typeof plan === 'object' ? plan : {};
|
||||
if (Boolean(source?.seriesBatchChild) || Boolean(source?.seriesBatchVirtualEpisode)) {
|
||||
return true;
|
||||
}
|
||||
const parentJobId = normalizePositiveInt(source?.seriesBatchParentJobId);
|
||||
const childIndex = normalizePositiveInt(source?.seriesBatchChildIndex);
|
||||
const childCount = normalizePositiveInt(source?.seriesBatchChildCount);
|
||||
const titleId = normalizeTitleId(source?.seriesBatchTitleId);
|
||||
return Boolean(parentJobId && (childIndex || childCount || titleId));
|
||||
}
|
||||
|
||||
function collectEpisodeReferencesFromPlan(plan = null) {
|
||||
const source = plan && typeof plan === 'object' ? plan : {};
|
||||
const refs = [];
|
||||
const assignments = source?.episodeAssignments && typeof source.episodeAssignments === 'object'
|
||||
? Object.values(source.episodeAssignments)
|
||||
: [];
|
||||
for (const assignment of assignments) {
|
||||
const normalized = normalizeEpisodeReference(assignment);
|
||||
if (normalized) {
|
||||
refs.push(normalized);
|
||||
}
|
||||
}
|
||||
|
||||
const titles = Array.isArray(source?.titles) ? source.titles : [];
|
||||
for (const title of titles) {
|
||||
if (!Boolean(title?.selectedForEncode) && !Boolean(title?.encodeInput)) {
|
||||
continue;
|
||||
}
|
||||
const normalized = normalizeEpisodeReference({
|
||||
episodeId: title?.episodeId,
|
||||
episodeNumber: title?.episodeNumber,
|
||||
seasonNumber: title?.seasonNumber
|
||||
});
|
||||
if (normalized) {
|
||||
refs.push(normalized);
|
||||
}
|
||||
}
|
||||
return refs;
|
||||
}
|
||||
|
||||
function normalizeSeriesLanguage(value) {
|
||||
const raw = String(value || '').trim();
|
||||
if (!raw) {
|
||||
@@ -730,6 +801,7 @@ export default function PipelineStatusCard({
|
||||
const [trackSelectionByTitle, setTrackSelectionByTitle] = useState({});
|
||||
const [episodeAssignmentsByTitle, setEpisodeAssignmentsByTitle] = useState({});
|
||||
const [selectedEpisodeFillStart, setSelectedEpisodeFillStart] = useState(null);
|
||||
const [containerUsedEpisodeKeys, setContainerUsedEpisodeKeys] = useState([]);
|
||||
const [settingsMap, setSettingsMap] = useState({});
|
||||
const [presetDisplayMap, setPresetDisplayMap] = useState({});
|
||||
const [scriptCatalog, setScriptCatalog] = useState([]);
|
||||
@@ -748,7 +820,7 @@ export default function PipelineStatusCard({
|
||||
const converterConfigSaveTimeoutRef = useRef(null);
|
||||
const selectedMetadataEpisodes = useMemo(() => {
|
||||
const rows = Array.isArray(selectedMetadata?.episodes) ? selectedMetadata.episodes : [];
|
||||
return rows
|
||||
const normalizedRows = rows
|
||||
.map((item) => {
|
||||
const episodeId = Number(item?.id || 0);
|
||||
const episodeNumber = Number(item?.number ?? item?.episodeNumber ?? 0);
|
||||
@@ -777,7 +849,15 @@ export default function PipelineStatusCard({
|
||||
const bId = Number.isFinite(b.episodeId) ? b.episodeId : Number.MAX_SAFE_INTEGER;
|
||||
return aId - bId;
|
||||
});
|
||||
}, [selectedMetadata?.episodes]);
|
||||
if (!Array.isArray(containerUsedEpisodeKeys) || containerUsedEpisodeKeys.length === 0) {
|
||||
return normalizedRows;
|
||||
}
|
||||
const usedKeySet = new Set(containerUsedEpisodeKeys);
|
||||
return normalizedRows.filter((episode) => {
|
||||
const key = buildEpisodeReferenceKey(episode);
|
||||
return !key || !usedKeySet.has(key);
|
||||
});
|
||||
}, [selectedMetadata?.episodes, containerUsedEpisodeKeys]);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
@@ -1333,6 +1413,102 @@ export default function PipelineStatusCard({
|
||||
const metadataProvider = String(selectedMetadata?.metadataProvider || '').trim().toLowerCase();
|
||||
return hasSeasonNumber || hasEpisodeList || metadataProvider === 'tmdb' || metadataProvider === 'themoviedb';
|
||||
}, [jobMediaProfile, selectedMetadata?.seasonNumber, selectedMetadata?.episodes, selectedMetadata?.metadataProvider]);
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
||||
const loadContainerUsedEpisodes = async () => {
|
||||
if (!isSeriesDvdReview || !retryJobId) {
|
||||
if (!cancelled) {
|
||||
setContainerUsedEpisodeKeys([]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const currentResponse = await api.getJob(retryJobId, { lite: true, forceRefresh: true });
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
const currentJob = currentResponse?.job && typeof currentResponse.job === 'object'
|
||||
? currentResponse.job
|
||||
: null;
|
||||
const currentJobId = normalizeJobId(currentJob?.id || retryJobId);
|
||||
const normalizedCurrentKind = String(currentJob?.job_kind || '').trim().toLowerCase();
|
||||
const containerJobId = normalizedCurrentKind === 'dvd_series_container'
|
||||
? normalizeJobId(currentJob?.id)
|
||||
: normalizeJobId(currentJob?.parent_job_id);
|
||||
|
||||
if (!containerJobId) {
|
||||
if (!cancelled) {
|
||||
setContainerUsedEpisodeKeys([]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const containerResponse = await api.getJob(containerJobId, { lite: true, forceRefresh: true });
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
const containerJob = containerResponse?.job && typeof containerResponse.job === 'object'
|
||||
? containerResponse.job
|
||||
: null;
|
||||
const children = Array.isArray(containerJob?.children) ? containerJob.children : [];
|
||||
const usedKeys = new Set();
|
||||
|
||||
for (const child of children) {
|
||||
const childId = normalizeJobId(child?.id);
|
||||
if (currentJobId && childId && currentJobId === childId) {
|
||||
continue;
|
||||
}
|
||||
if (isSeriesBatchEpisodePlan(child?.encodePlan)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const refs = collectEpisodeReferencesFromPlan(child?.encodePlan);
|
||||
for (const ref of refs) {
|
||||
const key = buildEpisodeReferenceKey(ref);
|
||||
if (key) {
|
||||
usedKeys.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
const batchEpisodes = Array.isArray(child?.handbrakeInfo?.seriesBatch?.episodes)
|
||||
? child.handbrakeInfo.seriesBatch.episodes
|
||||
: [];
|
||||
for (const episode of batchEpisodes) {
|
||||
const key = buildEpisodeReferenceKey({
|
||||
episodeId: episode?.episodeId,
|
||||
episodeNumber: episode?.episodeNumber,
|
||||
seasonNumber: episode?.seasonNumber
|
||||
});
|
||||
if (key) {
|
||||
usedKeys.add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!cancelled) {
|
||||
setContainerUsedEpisodeKeys(Array.from(usedKeys));
|
||||
}
|
||||
} catch (_error) {
|
||||
if (!cancelled) {
|
||||
setContainerUsedEpisodeKeys([]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void loadContainerUsedEpisodes();
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [
|
||||
isSeriesDvdReview,
|
||||
retryJobId,
|
||||
mediaInfoReview?.generatedAt,
|
||||
mediaInfoReview?.reviewConfirmedAt,
|
||||
selectedMetadata?.tmdbId,
|
||||
selectedMetadata?.seasonNumber
|
||||
]);
|
||||
const seriesBatchContext = useMemo(() => {
|
||||
const raw = pipeline?.context?.seriesBatch;
|
||||
if (!raw || typeof raw !== 'object') {
|
||||
|
||||
Reference in New Issue
Block a user