Fix cleanup after partial extraction failures

This commit is contained in:
Sucukdeluxe 2026-03-10 19:57:26 +01:00
parent 56aaf99464
commit fbae8a1496
2 changed files with 86 additions and 2 deletions

View File

@ -3694,6 +3694,7 @@ export class DownloadManager extends EventEmitter {
let skipped = 0;
let failed = 0;
let sourceArtifactsChanged = false;
let sourceCleanupRelevant = false;
for (const sourcePath of mkvFiles) {
if (shouldAbort?.()) {
@ -3744,6 +3745,7 @@ export class DownloadManager extends EventEmitter {
} catch {
/* ignore */
}
sourceCleanupRelevant = true;
skipped += 1;
continue;
}
@ -3761,6 +3763,7 @@ export class DownloadManager extends EventEmitter {
await this.moveFileWithExdevFallback(sourcePath, targetPath);
moved += 1;
sourceArtifactsChanged = true;
sourceCleanupRelevant = true;
this.logPackageForPackage(pkg, "INFO", "MKV verschoben", {
sourcePath,
targetPath,
@ -3790,7 +3793,7 @@ export class DownloadManager extends EventEmitter {
}
}
if (sourceArtifactsChanged && await this.existsAsync(sourceDir)) {
if ((sourceArtifactsChanged || sourceCleanupRelevant) && await this.existsAsync(sourceDir)) {
const removedResidual = await this.cleanupNonMkvResidualFiles(sourceDir, targetDir);
if (removedResidual > 0) {
logger.info(`MKV-Sammelordner entfernte Restdateien: pkg=${pkg.name}, entfernt=${removedResidual}`);
@ -10585,7 +10588,7 @@ export class DownloadManager extends EventEmitter {
}
// ── Link/Sample artifact removal ──
if ((extractedCount > 0 || alreadyMarkedExtracted) && failed === 0) {
if (extractedCount > 0 || alreadyMarkedExtracted) {
throwIfAborted();
if (this.settings.removeLinkFilesAfterExtract) {
const removedLinks = await removeDownloadLinkArtifacts(pkg.extractDir, { shouldAbort });

View File

@ -7827,6 +7827,87 @@ describe("download manager", () => {
expect(remainingPackage?.outputDir).toBe(outputDir);
});
it("removes link and sample artifacts from extracted output even when deferred post-processing has failures", async () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
tempDirs.push(root);
const packageName = "Failed Cleanup";
const outputDir = path.join(root, "downloads", packageName);
const extractDir = path.join(root, "extract", packageName);
fs.mkdirSync(outputDir, { recursive: true });
fs.mkdirSync(path.join(extractDir, "sample"), { recursive: true });
fs.writeFileSync(path.join(extractDir, "episode.links.txt"), "https://example.com/file", "utf8");
fs.writeFileSync(path.join(extractDir, "sample", "sample.mkv"), "sample-video", "utf8");
const session = emptySession();
const packageId = "failed-cleanup-pkg";
const itemId = "failed-cleanup-item";
const createdAt = Date.now() - 20_000;
session.packageOrder = [packageId];
session.packages[packageId] = {
id: packageId,
name: packageName,
outputDir,
extractDir,
status: "failed",
itemIds: [itemId],
cancelled: false,
enabled: true,
createdAt,
updatedAt: createdAt
};
session.items[itemId] = {
id: itemId,
packageId,
url: "https://dummy/failed-cleanup",
provider: "realdebrid",
status: "completed",
retries: 0,
speedBps: 0,
downloadedBytes: 100,
totalBytes: 100,
progressPercent: 100,
fileName: "episode.zip",
targetPath: path.join(outputDir, "episode.zip"),
resumable: true,
attempts: 1,
lastError: "",
fullStatus: "Entpackt - Done (1s)",
createdAt,
updatedAt: createdAt
};
const manager = new DownloadManager(
{
...defaultSettings(),
token: "rd-token",
outputDir: path.join(root, "downloads"),
extractDir: path.join(root, "extract"),
autoExtract: true,
autoRename4sf4sj: false,
collectMkvToLibrary: false,
removeLinkFilesAfterExtract: true,
removeSamplesAfterExtract: true,
enableIntegrityCheck: false,
cleanupMode: "delete"
},
session,
createStoragePaths(path.join(root, "state"))
);
await (manager as any).runDeferredPostExtraction(
packageId,
(manager as any).session.packages[packageId],
1,
1,
true,
1
);
expect(fs.existsSync(path.join(extractDir, "episode.links.txt"))).toBe(false);
expect(fs.existsSync(path.join(extractDir, "sample", "sample.mkv"))).toBe(false);
});
it("does not delete startup archives when any completed item has an extract error", async () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
tempDirs.push(root);