0.13.1-10 Misc fixes

This commit is contained in:
2026-04-17 08:04:52 +00:00
parent f25b6e1181
commit bebe9eac8c
19 changed files with 979 additions and 2155 deletions
+82 -21
View File
@@ -133,6 +133,16 @@ function parseInfoFromValue(value, fallback = null) {
return parseJsonSafe(value, fallback);
}
function isOrphanRawImportMakemkvInfo(makemkvInfo = null) {
const info = makemkvInfo && typeof makemkvInfo === 'object' ? makemkvInfo : {};
const source = String(
info?.source
|| info?.importRecovery?.source
|| ''
).trim().toLowerCase();
return source === 'orphan_raw_import';
}
function hasBlurayStructure(rawPath) {
const basePath = String(rawPath || '').trim();
if (!basePath) {
@@ -5180,6 +5190,7 @@ class HistoryService {
? Math.trunc(manualEpisodeCountRaw)
: null;
const manualEpisodes = Array.isArray(payload.episodes) ? payload.episodes : null;
const hasExplicitManualEpisodes = Boolean(manualEpisodes && manualEpisodes.length > 0);
const manualMetadataKind = String(payload.metadataKind || '').trim().toLowerCase() || null;
const manualProviderId = String(payload.providerId || '').trim() || null;
@@ -5240,7 +5251,7 @@ class HistoryService {
let episodeCount = manualEpisodeCount !== null
? manualEpisodeCount
: (Number(existingSelectedMetadata?.episodeCount || 0) || 0);
let episodes = manualEpisodes && manualEpisodes.length > 0
let episodes = hasExplicitManualEpisodes
? manualEpisodes
: (Array.isArray(existingSelectedMetadata?.episodes) ? existingSelectedMetadata.episodes : []);
const discNumber = manualDiscNumber !== null
@@ -5280,6 +5291,13 @@ class HistoryService {
? `tmdb:${effectiveTmdbId}:season:${seasonNumber}`
: `tmdb:${effectiveTmdbId}`;
}
let tmdbLanguage = null;
try {
const dvdSettings = await settingsService.getEffectiveSettingsMap('dvd');
tmdbLanguage = String(dvdSettings?.dvd_series_language || '').trim() || null;
} catch (_settingsError) {
tmdbLanguage = null;
}
let tmdbDetails = existingSelectedMetadata?.tmdbDetails && typeof existingSelectedMetadata.tmdbDetails === 'object'
? { ...existingSelectedMetadata.tmdbDetails }
@@ -5287,6 +5305,7 @@ class HistoryService {
if (effectiveTmdbId !== null) {
try {
const seriesDetails = await tmdbService.getSeriesDetails(effectiveTmdbId, {
language: tmdbLanguage,
appendToResponse: ['credits', 'external_ids']
});
if (seriesDetails) {
@@ -5325,12 +5344,16 @@ class HistoryService {
if (effectiveTmdbId !== null && seasonNumber !== null) {
try {
const seasonDetails = await tmdbService.getSeasonDetails(effectiveTmdbId, seasonNumber);
const seasonCredits = await tmdbService.getSeasonCredits(effectiveTmdbId, seasonNumber);
const seasonDetails = await tmdbService.getSeasonDetails(effectiveTmdbId, seasonNumber, {
language: tmdbLanguage
});
const seasonCredits = await tmdbService.getSeasonCredits(effectiveTmdbId, seasonNumber, {
language: tmdbLanguage
});
const seasonSummary = tmdbService.buildSeasonSummary(seasonDetails);
if (seasonSummary) {
const fetchedEpisodes = Array.isArray(seasonSummary.episodes) ? seasonSummary.episodes : [];
if (fetchedEpisodes.length > 0 && (!Array.isArray(episodes) || episodes.length === 0)) {
if (fetchedEpisodes.length > 0 && !hasExplicitManualEpisodes) {
episodes = fetchedEpisodes;
}
const fetchedEpisodeCount = Number(seasonSummary.episodeCount || 0) || 0;
@@ -6177,15 +6200,20 @@ class HistoryService {
lineageArtifactsByJobId,
outputFoldersByJobId
);
const relatedJobs = jobs.map((job) => ({
id: Number(job.id),
parentJobId: normalizeJobIdValue(job.parent_job_id),
title: buildJobDisplayTitle(job),
status: String(job.status || '').trim() || null,
discDevice: String(job.disc_device || '').trim() || null,
isPrimary: Number(job.id) === normalizedJobId,
createdAt: String(job.created_at || '').trim() || null
}));
const relatedJobs = jobs.map((job) => {
const mkInfo = parseJsonSafe(job?.makemkv_info_json, {});
return {
id: Number(job.id),
parentJobId: normalizeJobIdValue(job.parent_job_id),
title: buildJobDisplayTitle(job),
status: String(job.status || '').trim() || null,
discDevice: String(job.disc_device || '').trim() || null,
isPrimary: Number(job.id) === normalizedJobId,
createdAt: String(job.created_at || '').trim() || null,
orphanRawImport: isOrphanRawImportMakemkvInfo(mkInfo)
};
});
const orphanRawImportJobs = relatedJobs.filter((row) => Boolean(row?.orphanRawImport)).length;
const existingRawCandidates = preview.pathCandidates.raw.filter((row) => row.exists).length;
const existingMovieCandidates = preview.pathCandidates.movie.filter((row) => row.exists).length;
@@ -6195,8 +6223,12 @@ class HistoryService {
relatedJobs,
pathCandidates: preview.pathCandidates,
protectedRoots: preview.protectedRoots,
flags: {
containsOrphanRawImportJob: orphanRawImportJobs > 0
},
counts: {
relatedJobs: relatedJobs.length,
orphanRawImportJobs,
rawCandidates: preview.pathCandidates.raw.length,
movieCandidates: preview.pathCandidates.movie.length,
existingRawCandidates,
@@ -6887,6 +6919,28 @@ class HistoryService {
throw error;
}
const preserveRawForImportJobs = Boolean(options?.preserveRawForImportJobs);
const isOrphanRawImportJobRow = (row) => {
if (!row || typeof row !== 'object') {
return false;
}
const mkInfo = parseJsonSafe(row?.makemkv_info_json, {});
return isOrphanRawImportMakemkvInfo(mkInfo);
};
const resolveEffectiveFileTarget = (requestedTarget, row) => {
if (!preserveRawForImportJobs || !isOrphanRawImportJobRow(row)) {
return requestedTarget;
}
const normalizedRequested = String(requestedTarget || 'none').trim().toLowerCase();
if (normalizedRequested === 'raw') {
return 'none';
}
if (normalizedRequested === 'both') {
return 'movie';
}
return normalizedRequested;
};
const includeRelated = Boolean(options?.includeRelated);
if (!includeRelated) {
const resolved = await this.getJobByIdOrReplacement(jobId);
@@ -6903,10 +6957,11 @@ class HistoryService {
throw error;
}
const effectiveFileTarget = resolveEffectiveFileTarget(fileTarget, existing);
let fileSummary = null;
if (fileTarget !== 'none') {
if (effectiveFileTarget !== 'none') {
const preview = await this.getJobDeletePreview(normalizedJobId, { includeRelated: false });
fileSummary = this._deletePathsFromPreview(preview, fileTarget, {
fileSummary = this._deletePathsFromPreview(preview, effectiveFileTarget, {
selectedMoviePaths: options?.selectedMoviePaths
});
}
@@ -6968,7 +7023,8 @@ class HistoryService {
logger.warn('job:deleted', {
jobId: normalizedJobId,
fileTarget,
fileTarget: effectiveFileTarget,
requestedFileTarget: fileTarget,
includeRelated: false,
pipelineStateReset: isActivePipelineJob,
filesDeleted: fileSummary
@@ -6982,7 +7038,8 @@ class HistoryService {
return {
deleted: true,
jobId: normalizedJobId,
fileTarget,
fileTarget: effectiveFileTarget,
requestedFileTarget: fileTarget,
includeRelated: false,
deletedJobIds: [Number(normalizedJobId)],
fileSummary
@@ -6990,6 +7047,7 @@ class HistoryService {
}
const resolved = await this.getJobByIdOrReplacement(jobId);
const existing = resolved?.job || null;
const normalizedJobId = normalizeJobIdValue(resolved?.resolvedJobId || jobId);
const preview = await this.getJobDeletePreview(normalizedJobId, { includeRelated: true });
const deleteJobIds = Array.isArray(preview?.relatedJobs)
@@ -7014,9 +7072,10 @@ class HistoryService {
throw error;
}
const effectiveFileTarget = resolveEffectiveFileTarget(fileTarget, existing);
let fileSummary = null;
if (fileTarget !== 'none') {
fileSummary = this._deletePathsFromPreview(preview, fileTarget, {
if (effectiveFileTarget !== 'none') {
fileSummary = this._deletePathsFromPreview(preview, effectiveFileTarget, {
selectedMoviePaths: options?.selectedMoviePaths
});
}
@@ -7068,7 +7127,8 @@ class HistoryService {
logger.warn('job:deleted', {
jobId: normalizedJobId,
fileTarget,
fileTarget: effectiveFileTarget,
requestedFileTarget: fileTarget,
includeRelated: true,
deletedJobIds: deleteJobIds,
deletedJobCount: deleteJobIds.length,
@@ -7084,7 +7144,8 @@ class HistoryService {
return {
deleted: true,
jobId: normalizedJobId,
fileTarget,
fileTarget: effectiveFileTarget,
requestedFileTarget: fileTarget,
includeRelated: true,
deletedJobIds: deleteJobIds,
deletedJobs: preview.relatedJobs,