0.12.0-16 misc fixes
This commit is contained in:
@@ -2991,6 +2991,75 @@ class HistoryService {
|
||||
}));
|
||||
}
|
||||
|
||||
async findLatestReplacementJobId(sourceJobId) {
|
||||
const normalizedSourceJobId = normalizeJobIdValue(sourceJobId);
|
||||
if (!normalizedSourceJobId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const db = await getDb();
|
||||
const rows = await db.all(
|
||||
`
|
||||
SELECT a.job_id
|
||||
FROM job_lineage_artifacts a
|
||||
JOIN jobs j ON j.id = a.job_id
|
||||
WHERE a.source_job_id = ?
|
||||
ORDER BY a.id DESC
|
||||
`,
|
||||
[normalizedSourceJobId]
|
||||
);
|
||||
|
||||
for (const row of (Array.isArray(rows) ? rows : [])) {
|
||||
const candidateJobId = normalizeJobIdValue(row?.job_id);
|
||||
if (!candidateJobId || candidateJobId === normalizedSourceJobId) {
|
||||
continue;
|
||||
}
|
||||
return candidateJobId;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async getJobByIdOrReplacement(jobId) {
|
||||
const normalizedJobId = normalizeJobIdValue(jobId);
|
||||
if (!normalizedJobId) {
|
||||
return {
|
||||
requestedJobId: null,
|
||||
resolvedJobId: null,
|
||||
replaced: false,
|
||||
job: null
|
||||
};
|
||||
}
|
||||
|
||||
const directJob = await this.getJobById(normalizedJobId);
|
||||
if (directJob) {
|
||||
return {
|
||||
requestedJobId: normalizedJobId,
|
||||
resolvedJobId: normalizedJobId,
|
||||
replaced: false,
|
||||
job: directJob
|
||||
};
|
||||
}
|
||||
|
||||
const replacementJobId = await this.findLatestReplacementJobId(normalizedJobId);
|
||||
if (!replacementJobId) {
|
||||
return {
|
||||
requestedJobId: normalizedJobId,
|
||||
resolvedJobId: normalizedJobId,
|
||||
replaced: false,
|
||||
job: null
|
||||
};
|
||||
}
|
||||
|
||||
const replacementJob = await this.getJobById(replacementJobId);
|
||||
return {
|
||||
requestedJobId: normalizedJobId,
|
||||
resolvedJobId: replacementJobId,
|
||||
replaced: Boolean(replacementJob),
|
||||
job: replacementJob || null
|
||||
};
|
||||
}
|
||||
|
||||
async getRunningJobs() {
|
||||
const db = await getDb();
|
||||
const [rows, settings] = await Promise.all([
|
||||
@@ -4357,21 +4426,72 @@ class HistoryService {
|
||||
const rawRoot = normalizeComparablePath(effectiveRawDir);
|
||||
const stat = fs.lstatSync(rawPath);
|
||||
const isFile = stat.isFile();
|
||||
const plan = resolvedPaths.encodePlan || {};
|
||||
|
||||
// Für Converter-Jobs: Datei liegt in einem Unterordner → Ordner löschen
|
||||
let deletePath = rawPath;
|
||||
if (isFile && resolvedPaths.mediaType === 'converter') {
|
||||
const parentDir = normalizeComparablePath(path.dirname(rawPath));
|
||||
if (parentDir && parentDir !== rawRoot && isPathInside(rawRoot, parentDir)) {
|
||||
deletePath = parentDir;
|
||||
// Für Converter-Jobs: nur die zum Job gehörenden Dateien löschen.
|
||||
// Der übergeordnete Ordner wird nur entfernt, wenn er danach leer ist.
|
||||
// Ausnahme: Ordner-Jobs (isFolder=true) haben einen dedizierten Ordner → komplett löschen.
|
||||
const isConverterFolder = resolvedPaths.mediaType === 'converter' && Boolean(plan.isFolder);
|
||||
const isSharedAudio = resolvedPaths.mediaType === 'converter' && Boolean(plan.isSharedAudio);
|
||||
const isConverterFileJob = resolvedPaths.mediaType === 'converter' && !isConverterFolder;
|
||||
|
||||
if (isConverterFileJob) {
|
||||
// Bestimme die zu löschenden Dateien dieses Jobs
|
||||
let filesToDelete = null;
|
||||
if (isSharedAudio && Array.isArray(plan.inputPaths) && plan.inputPaths.length > 0) {
|
||||
// Shared-Audio-Job: nur die explizit gelisteten Einzeldateien entfernen
|
||||
filesToDelete = plan.inputPaths.map(normalizeComparablePath).filter(Boolean);
|
||||
} else if (isFile) {
|
||||
// Einzeldatei-Job: nur diese eine Datei entfernen
|
||||
filesToDelete = [rawPath];
|
||||
}
|
||||
}
|
||||
|
||||
const keepRoot = deletePath === rawRoot;
|
||||
const result = deleteFilesRecursively(deletePath, keepRoot);
|
||||
summary.raw.deleted = true;
|
||||
summary.raw.filesDeleted = result.filesDeleted;
|
||||
summary.raw.dirsRemoved = result.dirsRemoved;
|
||||
if (filesToDelete) {
|
||||
let filesDeleted = 0;
|
||||
const parentDirs = new Set();
|
||||
for (const filePath of filesToDelete) {
|
||||
if (!isPathInside(rawRoot, filePath)) continue;
|
||||
if (!fs.existsSync(filePath)) continue;
|
||||
const fStat = fs.lstatSync(filePath);
|
||||
if (!fStat.isFile()) continue;
|
||||
fs.unlinkSync(filePath);
|
||||
filesDeleted++;
|
||||
const parentDir = normalizeComparablePath(path.dirname(filePath));
|
||||
if (parentDir && parentDir !== rawRoot && isPathInside(rawRoot, parentDir)) {
|
||||
parentDirs.add(parentDir);
|
||||
}
|
||||
}
|
||||
// Übergeordneten Ordner löschen, wenn er nach dem Löschen leer ist
|
||||
let dirsRemoved = 0;
|
||||
for (const parentDir of parentDirs) {
|
||||
try {
|
||||
if (!fs.existsSync(parentDir)) continue;
|
||||
const remaining = fs.readdirSync(parentDir);
|
||||
if (remaining.length === 0) {
|
||||
fs.rmdirSync(parentDir);
|
||||
dirsRemoved++;
|
||||
}
|
||||
} catch (_err) { /* ignore */ }
|
||||
}
|
||||
summary.raw.deleted = true;
|
||||
summary.raw.filesDeleted = filesDeleted;
|
||||
summary.raw.dirsRemoved = dirsRemoved;
|
||||
} else {
|
||||
// Fallback: rawPath ist ein Verzeichnis ohne explizite Dateiliste
|
||||
const keepRoot = rawPath === rawRoot;
|
||||
const result = deleteFilesRecursively(rawPath, keepRoot);
|
||||
summary.raw.deleted = true;
|
||||
summary.raw.filesDeleted = result.filesDeleted;
|
||||
summary.raw.dirsRemoved = result.dirsRemoved;
|
||||
}
|
||||
} else {
|
||||
// Regulärer Job oder dedizierter Converter-Ordner-Job (isFolder=true): gesamten Pfad löschen
|
||||
const keepRoot = rawPath === rawRoot;
|
||||
const result = deleteFilesRecursively(rawPath, keepRoot);
|
||||
summary.raw.deleted = true;
|
||||
summary.raw.filesDeleted = result.filesDeleted;
|
||||
summary.raw.dirsRemoved = result.dirsRemoved;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user