0.12.0-3 Plugin Integration

This commit is contained in:
2026-03-21 15:58:57 +00:00
parent d9969dfbe5
commit e99cdf1895
20 changed files with 928 additions and 115 deletions
+86 -4
View File
@@ -4059,10 +4059,25 @@ class HistoryService {
}
}
const hasTrackedOutputSource = (sources = []) => {
const normalizedSources = Array.isArray(sources)
? sources
.map((source) => String(source || '').trim().toLowerCase())
.filter(Boolean)
: [];
return normalizedSources.includes('output_path') || normalizedSources.includes('lineage_output_path');
};
const buildList = (target) => Array.from(candidateMap.values())
.filter((row) => row.target === target)
.map((row) => {
const inspection = inspectDeletionPath(row.path);
const sources = Array.from(row.sources).sort((left, right) => left.localeCompare(right));
// Do not expose stale output file paths from DB/lineage in delete preview.
// They cannot be deleted anyway and show up as "ghost paths" in UI/QA checks.
if (target === 'movie' && !inspection.exists && hasTrackedOutputSource(sources)) {
return null;
}
return {
target,
path: row.path,
@@ -4070,9 +4085,10 @@ class HistoryService {
isDirectory: Boolean(inspection.isDirectory),
isFile: Boolean(inspection.isFile),
jobIds: Array.from(row.jobIds).sort((left, right) => left - right),
sources: Array.from(row.sources).sort((left, right) => left.localeCompare(right))
sources
};
})
.filter(Boolean)
.sort((left, right) => String(left.path || '').localeCompare(String(right.path || ''), 'de'));
return {
@@ -4289,6 +4305,8 @@ class HistoryService {
raw: { attempted: false, deleted: false, filesDeleted: 0, dirsRemoved: 0, reason: null },
movie: { attempted: false, deleted: false, filesDeleted: 0, dirsRemoved: 0, reason: null }
};
let movieDeletedPathForTracking = null;
let movieCandidatePathForTracking = null;
if (target === 'raw' || target === 'both') {
summary.raw.attempted = true;
@@ -4328,7 +4346,59 @@ class HistoryService {
error.statusCode = 400;
throw error;
} else if (!fs.existsSync(effectiveOutputPath)) {
summary.movie.reason = 'Movie-Datei/Pfad existiert nicht.';
const movieRoot = normalizeComparablePath(effectiveMovieDir);
const trackedFolders = await this.getJobOutputFolders(jobId);
const trackedExistingPaths = Array.from(new Set(
trackedFolders
.map((row) => normalizeComparablePath(row?.output_path))
.filter(Boolean)
.filter((candidatePath) => isPathInside(effectiveMovieDir, candidatePath))
.filter((candidatePath) => candidatePath !== movieRoot)
.filter((candidatePath) => fs.existsSync(candidatePath))
));
if (trackedExistingPaths.length === 1) {
const trackedPath = trackedExistingPaths[0];
const stat = fs.lstatSync(trackedPath);
if (stat.isDirectory()) {
const keepRoot = trackedPath === movieRoot;
const result = deleteFilesRecursively(trackedPath, keepRoot);
summary.movie.deleted = true;
summary.movie.filesDeleted = result.filesDeleted;
summary.movie.dirsRemoved = result.dirsRemoved;
movieDeletedPathForTracking = trackedPath;
movieCandidatePathForTracking = trackedPath;
summary.movie.reason = null;
} else {
const parentDir = normalizeComparablePath(path.dirname(trackedPath));
const canDeleteParentDir = parentDir
&& parentDir !== movieRoot
&& isPathInside(movieRoot, parentDir)
&& fs.existsSync(parentDir)
&& fs.lstatSync(parentDir).isDirectory();
if (canDeleteParentDir) {
const result = deleteFilesRecursively(parentDir, false);
summary.movie.deleted = true;
summary.movie.filesDeleted = result.filesDeleted;
summary.movie.dirsRemoved = result.dirsRemoved;
movieDeletedPathForTracking = parentDir;
movieCandidatePathForTracking = trackedPath;
} else {
fs.unlinkSync(trackedPath);
summary.movie.deleted = true;
summary.movie.filesDeleted = 1;
summary.movie.dirsRemoved = 0;
movieDeletedPathForTracking = trackedPath;
movieCandidatePathForTracking = trackedPath;
}
summary.movie.reason = null;
}
} else if (trackedExistingPaths.length > 1) {
summary.movie.reason = 'Mehrere bekannte Output-Ordner gefunden. Bitte gezielt über die Ordnerauswahl löschen.';
} else {
summary.movie.reason = 'Movie-Datei/Pfad existiert nicht.';
}
} else {
const outputPath = normalizeComparablePath(effectiveOutputPath);
const movieRoot = normalizeComparablePath(effectiveMovieDir);
@@ -4339,6 +4409,8 @@ class HistoryService {
summary.movie.deleted = true;
summary.movie.filesDeleted = result.filesDeleted;
summary.movie.dirsRemoved = result.dirsRemoved;
movieDeletedPathForTracking = outputPath;
movieCandidatePathForTracking = outputPath;
} else {
const parentDir = normalizeComparablePath(path.dirname(outputPath));
const canDeleteParentDir = parentDir
@@ -4352,19 +4424,29 @@ class HistoryService {
summary.movie.deleted = true;
summary.movie.filesDeleted = result.filesDeleted;
summary.movie.dirsRemoved = result.dirsRemoved;
movieDeletedPathForTracking = parentDir;
movieCandidatePathForTracking = outputPath;
} else {
fs.unlinkSync(outputPath);
summary.movie.deleted = true;
summary.movie.filesDeleted = 1;
summary.movie.dirsRemoved = 0;
movieDeletedPathForTracking = outputPath;
movieCandidatePathForTracking = outputPath;
}
}
}
}
// Remove deleted movie paths from output folders tracking
if (summary.movie?.deleted && effectiveOutputPath) {
await this.removeJobOutputFolder(jobId, effectiveOutputPath);
if (summary.movie?.deleted) {
const trackingCleanupPaths = Array.from(new Set([
movieCandidatePathForTracking,
movieDeletedPathForTracking
].filter(Boolean)));
for (const candidatePath of trackingCleanupPaths) {
await this.removeJobOutputFolder(jobId, candidatePath);
}
}
await this.appendLog(