Release v1.4.6 with extraction resume safety and smoother runtime
Build and Release / build (push) Has been cancelled

This commit is contained in:
Sucukdeluxe
2026-02-27 18:59:04 +01:00
parent 05a75d0ac5
commit dbf1c34282
7 changed files with 588 additions and 56 deletions
+63
View File
@@ -2807,6 +2807,69 @@ describe("download manager", () => {
}
});
it("marks extracting items as resumable extraction on shutdown", () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
tempDirs.push(root);
const session = emptySession();
const packageId = "extract-stop-pkg";
const itemId = "extract-stop-item";
const createdAt = Date.now() - 20_000;
const outputDir = path.join(root, "downloads", "extract-stop");
const extractDir = path.join(root, "extract", "extract-stop");
session.packageOrder = [packageId];
session.packages[packageId] = {
id: packageId,
name: "extract-stop",
outputDir,
extractDir,
status: "extracting",
itemIds: [itemId],
cancelled: false,
enabled: true,
createdAt,
updatedAt: createdAt
};
session.items[itemId] = {
id: itemId,
packageId,
url: "https://dummy/extract-stop",
provider: "realdebrid",
status: "completed",
retries: 0,
speedBps: 0,
downloadedBytes: 123,
totalBytes: 123,
progressPercent: 100,
fileName: "extract-stop.part01.rar",
targetPath: path.join(outputDir, "extract-stop.part01.rar"),
resumable: true,
attempts: 1,
lastError: "",
fullStatus: "Entpacken 40%",
createdAt,
updatedAt: createdAt
};
const manager = new DownloadManager(
{
...defaultSettings(),
token: "rd-token",
outputDir: path.join(root, "downloads"),
extractDir: path.join(root, "extract"),
autoExtract: true
},
session,
createStoragePaths(path.join(root, "state"))
);
manager.prepareForShutdown();
const snapshot = manager.getSnapshot();
expect(snapshot.session.packages[packageId]?.status).toBe("queued");
expect(snapshot.session.items[itemId]?.fullStatus).toBe("Entpacken abgebrochen (wird fortgesetzt)");
});
it("recovers pending extraction on startup for completed package", async () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
tempDirs.push(root);
+57
View File
@@ -274,4 +274,61 @@ describe("extractor", () => {
expect(result.failed).toBe(1);
expect(fs.existsSync(targetDir)).toBe(false);
});
it("resumes extraction from persisted progress file", async () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-extract-"));
tempDirs.push(root);
const packageDir = path.join(root, "pkg");
const targetDir = path.join(root, "out");
fs.mkdirSync(packageDir, { recursive: true });
const zipA = new AdmZip();
zipA.addFile("a.txt", Buffer.from("a"));
zipA.writeZip(path.join(packageDir, "a.zip"));
const zipB = new AdmZip();
zipB.addFile("b.txt", Buffer.from("b"));
zipB.writeZip(path.join(packageDir, "b.zip"));
fs.writeFileSync(path.join(packageDir, ".rd_extract_progress.json"), JSON.stringify({ completedArchives: ["a.zip"] }), "utf8");
const result = await extractPackageArchives({
packageDir,
targetDir,
cleanupMode: "none",
conflictMode: "overwrite",
removeLinks: false,
removeSamples: false
});
expect(result.extracted).toBe(2);
expect(result.failed).toBe(0);
expect(fs.existsSync(path.join(targetDir, "b.txt"))).toBe(true);
expect(fs.existsSync(path.join(packageDir, ".rd_extract_progress.json"))).toBe(false);
});
it("aborts extraction immediately when abort signal is set", async () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-extract-"));
tempDirs.push(root);
const packageDir = path.join(root, "pkg");
const targetDir = path.join(root, "out");
fs.mkdirSync(packageDir, { recursive: true });
const zip = new AdmZip();
zip.addFile("file.txt", Buffer.from("x"));
zip.writeZip(path.join(packageDir, "file.zip"));
const controller = new AbortController();
controller.abort();
await expect(extractPackageArchives({
packageDir,
targetDir,
cleanupMode: "none",
conflictMode: "overwrite",
removeLinks: false,
removeSamples: false,
signal: controller.signal
})).rejects.toThrow("aborted:extract");
});
});