0.15.0-2 Misc Fixes

This commit is contained in:
2026-04-24 10:17:55 +00:00
parent 3e5840f329
commit 730bc50715
19 changed files with 1481 additions and 139 deletions
+2
View File
@@ -770,6 +770,8 @@ async function migrateOutputTemplates(db) {
async function removeDeprecatedSettings(db) {
const deprecatedKeys = [
'pushover_notify_disc_detected',
'pushover_notify_cron_success',
'pushover_notify_cron_error',
'mediainfo_extra_args',
'makemkv_rip_mode',
'makemkv_analyze_extra_args',
+86 -4
View File
@@ -591,6 +591,32 @@ function toProcessLogPath(jobId) {
return path.join(getJobLogDir(), `job-${Math.trunc(normalizedId)}.process.log`);
}
function buildArchivedProcessLogPath(sourceJobId, options = {}) {
const normalizedId = Number(sourceJobId);
if (!Number.isFinite(normalizedId) || normalizedId <= 0) {
return null;
}
const targetJobId = Number(options?.replacementJobId);
const safeReason = String(options?.reason || 'retired')
.trim()
.toLowerCase()
.replace(/[^a-z0-9_-]+/g, '-')
.replace(/^-+|-+$/g, '')
|| 'retired';
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const archiveDir = path.join(getJobLogDir(), 'archived');
const targetSuffix = Number.isFinite(targetJobId) && targetJobId > 0
? `.to-${Math.trunc(targetJobId)}`
: '';
return {
archiveDir,
archivePath: path.join(
archiveDir,
`job-${Math.trunc(normalizedId)}.process.${safeReason}${targetSuffix}.${timestamp}.log`
)
};
}
function hasProcessLogFile(jobId) {
const filePath = toProcessLogPath(jobId);
return Boolean(filePath && fs.existsSync(filePath));
@@ -2944,7 +2970,17 @@ class HistoryService {
}
await this.closeProcessLog(fromJobId);
this._deleteProcessLogFile(fromJobId);
const archivedProcessLogPath = this._archiveProcessLogFile(fromJobId, {
replacementJobId: toJobId,
reason
});
if (archivedProcessLogPath) {
this.appendProcessLog(
toJobId,
'SYSTEM',
`Vorheriger Prozess-Log von Job #${fromJobId} archiviert: ${archivedProcessLogPath}`
);
}
logger.warn('job:retired', {
sourceJobId: fromJobId,
@@ -2957,7 +2993,8 @@ class HistoryService {
retired: true,
sourceJobId: fromJobId,
replacementJobId: toJobId,
reason
reason,
archivedProcessLogPath: archivedProcessLogPath || null
};
}
@@ -6708,6 +6745,33 @@ class HistoryService {
}
}
_archiveProcessLogFile(jobId, options = {}) {
const processLogPath = toProcessLogPath(jobId);
if (!processLogPath || !fs.existsSync(processLogPath)) {
return null;
}
const archiveTarget = buildArchivedProcessLogPath(jobId, options);
if (!archiveTarget?.archivePath) {
return null;
}
try {
fs.mkdirSync(archiveTarget.archiveDir, { recursive: true });
fs.renameSync(processLogPath, archiveTarget.archivePath);
return archiveTarget.archivePath;
} catch (error) {
logger.warn('job:process-log:archive-failed', {
jobId,
fromPath: processLogPath,
toPath: archiveTarget.archivePath,
reason: String(options?.reason || '').trim() || null,
replacementJobId: Number(options?.replacementJobId) || null,
error: error?.message || String(error)
});
return null;
}
}
async deleteJobFiles(jobId, target = 'both', options = {}) {
const allowedTargets = new Set(['raw', 'movie', 'both']);
if (!allowedTargets.has(target)) {
@@ -6992,9 +7056,12 @@ class HistoryService {
movieCandidatePathForTracking = outputPath;
} else {
const parentDir = normalizeComparablePath(path.dirname(outputPath));
const parentDirName = String(path.basename(parentDir || '') || '').trim();
const isIncompleteMergeParentDir = /^incomplete_merge_.+_job_\d+\s*$/i.test(parentDirName);
// Converter jobs output a single file — never delete the parent dir
const isConverterJob = resolvedPaths.mediaType === 'converter';
const canDeleteParentDir = !isConverterJob
&& !isIncompleteMergeParentDir
&& parentDir
&& parentDir !== movieRoot
&& isPathInside(movieRoot, parentDir)
@@ -7388,11 +7455,26 @@ class HistoryService {
};
const collectIncompleteMoviePathsFromPreview = (preview) => {
const rows = Array.isArray(preview?.pathCandidates?.movie) ? preview.pathCandidates.movie : [];
const incompleteJobPattern = /(^|[\\/])incomplete_job-\d+([\\/]|$)/i;
const incompleteMergePattern = /(^|[\\/])incomplete_merge_[^\\/]+_job_\d+([\\/]|$)/i;
return rows
.filter((row) => Boolean(row?.exists))
.filter((row) => {
const candidatePath = String(row?.path || '').trim();
if (!candidatePath) {
return false;
}
if (incompleteJobPattern.test(candidatePath)) {
return true;
}
if (incompleteMergePattern.test(candidatePath)) {
// Shared multipart merge folders must never be auto-selected as a whole directory.
return Boolean(row?.isFile);
}
return false;
})
.map((row) => String(row?.path || '').trim())
.filter((candidatePath) => Boolean(candidatePath))
.filter((candidatePath) => /(^|[\\/])incomplete_job-\d+([\\/]|$)/i.test(candidatePath));
.filter(Boolean);
};
const cleanupIncompleteMovieFoldersForJobs = async (jobRows = [], ownerJobIds = []) => {
const normalizedOwnerJobIds = Array.from(new Set(
File diff suppressed because it is too large Load Diff