0.15.0-2 Misc Fixes
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user