Backfill extracted archive cleanup on startup in v1.3.8
This commit is contained in:
parent
75fc582299
commit
da51e03cef
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "real-debrid-downloader",
|
"name": "real-debrid-downloader",
|
||||||
"version": "1.3.7",
|
"version": "1.3.8",
|
||||||
"description": "Real-Debrid Downloader Desktop (Electron + React + TypeScript)",
|
"description": "Real-Debrid Downloader Desktop (Electron + React + TypeScript)",
|
||||||
"main": "build/main/main/main.js",
|
"main": "build/main/main/main.js",
|
||||||
"author": "Sucukdeluxe",
|
"author": "Sucukdeluxe",
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { AppSettings, DownloadItem, DownloadStats, DownloadSummary, DownloadStat
|
|||||||
import { REQUEST_RETRIES } from "./constants";
|
import { REQUEST_RETRIES } from "./constants";
|
||||||
import { cleanupCancelledPackageArtifactsAsync } from "./cleanup";
|
import { cleanupCancelledPackageArtifactsAsync } from "./cleanup";
|
||||||
import { DebridService, MegaWebUnrestrictor } from "./debrid";
|
import { DebridService, MegaWebUnrestrictor } from "./debrid";
|
||||||
import { extractPackageArchives } from "./extractor";
|
import { collectArchiveCleanupTargets, extractPackageArchives } from "./extractor";
|
||||||
import { validateFileAgainstManifest } from "./integrity";
|
import { validateFileAgainstManifest } from "./integrity";
|
||||||
import { logger } from "./logger";
|
import { logger } from "./logger";
|
||||||
import { StoragePaths, saveSession } from "./storage";
|
import { StoragePaths, saveSession } from "./storage";
|
||||||
@ -179,12 +179,14 @@ export class DownloadManager extends EventEmitter {
|
|||||||
this.normalizeSessionStatuses();
|
this.normalizeSessionStatuses();
|
||||||
this.recoverPostProcessingOnStartup();
|
this.recoverPostProcessingOnStartup();
|
||||||
this.resolveExistingQueuedOpaqueFilenames();
|
this.resolveExistingQueuedOpaqueFilenames();
|
||||||
|
this.cleanupExistingExtractedArchives();
|
||||||
}
|
}
|
||||||
|
|
||||||
public setSettings(next: AppSettings): void {
|
public setSettings(next: AppSettings): void {
|
||||||
this.settings = next;
|
this.settings = next;
|
||||||
this.debridService.setSettings(next);
|
this.debridService.setSettings(next);
|
||||||
this.resolveExistingQueuedOpaqueFilenames();
|
this.resolveExistingQueuedOpaqueFilenames();
|
||||||
|
this.cleanupExistingExtractedArchives();
|
||||||
this.emitState();
|
this.emitState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -580,6 +582,77 @@ export class DownloadManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private cleanupExistingExtractedArchives(): void {
|
||||||
|
if (this.settings.cleanupMode === "none") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cleanupTargetsByPackage = new Map<string, Set<string>>();
|
||||||
|
for (const packageId of this.session.packageOrder) {
|
||||||
|
const pkg = this.session.packages[packageId];
|
||||||
|
if (!pkg || pkg.cancelled || pkg.status !== "completed") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = pkg.itemIds
|
||||||
|
.map((itemId) => this.session.items[itemId])
|
||||||
|
.filter(Boolean) as DownloadItem[];
|
||||||
|
if (items.length === 0 || !items.every((item) => item.status === "completed")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const extractedItems = items.filter((item) => item.fullStatus === "Entpackt");
|
||||||
|
if (extractedItems.length === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const packageTargets = cleanupTargetsByPackage.get(packageId) ?? new Set<string>();
|
||||||
|
for (const item of extractedItems) {
|
||||||
|
const targetPath = String(item.targetPath || "").trim();
|
||||||
|
if (!targetPath) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const cleanupTarget of collectArchiveCleanupTargets(targetPath)) {
|
||||||
|
packageTargets.add(cleanupTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (packageTargets.size > 0) {
|
||||||
|
cleanupTargetsByPackage.set(packageId, packageTargets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cleanupTargetsByPackage.size === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cleanupQueue = this.cleanupQueue
|
||||||
|
.then(async () => {
|
||||||
|
for (const [packageId, targets] of cleanupTargetsByPackage.entries()) {
|
||||||
|
const pkg = this.session.packages[packageId];
|
||||||
|
if (!pkg) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let removed = 0;
|
||||||
|
for (const targetPath of targets) {
|
||||||
|
try {
|
||||||
|
await fs.promises.rm(targetPath, { force: true });
|
||||||
|
removed += 1;
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removed > 0) {
|
||||||
|
logger.info(`Nachtraegliches Archive-Cleanup fuer ${pkg.name}: ${removed} Datei(en) geloescht`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
logger.warn(`Nachtraegliches Archive-Cleanup fehlgeschlagen: ${compactErrorText(error)}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public cancelPackage(packageId: string): void {
|
public cancelPackage(packageId: string): void {
|
||||||
const pkg = this.session.packages[packageId];
|
const pkg = this.session.packages[packageId];
|
||||||
if (!pkg) {
|
if (!pkg) {
|
||||||
|
|||||||
@ -821,6 +821,76 @@ describe("download manager", () => {
|
|||||||
expect(snapshot.canStart).toBe(true);
|
expect(snapshot.canStart).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("cleans leftover split archives on startup for already extracted packages", async () => {
|
||||||
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
|
||||||
|
tempDirs.push(root);
|
||||||
|
|
||||||
|
const packageDir = path.join(root, "downloads", "legacy");
|
||||||
|
fs.mkdirSync(packageDir, { recursive: true });
|
||||||
|
const part1 = path.join(packageDir, "legacy.release.part01.rar");
|
||||||
|
const part2 = path.join(packageDir, "legacy.release.part02.rar");
|
||||||
|
const part3 = path.join(packageDir, "legacy.release.part03.rar");
|
||||||
|
const keep = path.join(packageDir, "keep.txt");
|
||||||
|
fs.writeFileSync(part2, "part2", "utf8");
|
||||||
|
fs.writeFileSync(part3, "part3", "utf8");
|
||||||
|
fs.writeFileSync(keep, "keep", "utf8");
|
||||||
|
|
||||||
|
const session = emptySession();
|
||||||
|
const packageId = "legacy-pkg";
|
||||||
|
const itemId = "legacy-item";
|
||||||
|
const createdAt = Date.now() - 20_000;
|
||||||
|
|
||||||
|
session.packageOrder = [packageId];
|
||||||
|
session.packages[packageId] = {
|
||||||
|
id: packageId,
|
||||||
|
name: "legacy",
|
||||||
|
outputDir: packageDir,
|
||||||
|
extractDir: path.join(root, "extract", "legacy"),
|
||||||
|
status: "completed",
|
||||||
|
itemIds: [itemId],
|
||||||
|
cancelled: false,
|
||||||
|
enabled: true,
|
||||||
|
createdAt,
|
||||||
|
updatedAt: createdAt
|
||||||
|
};
|
||||||
|
session.items[itemId] = {
|
||||||
|
id: itemId,
|
||||||
|
packageId,
|
||||||
|
url: "https://dummy/legacy",
|
||||||
|
provider: "realdebrid",
|
||||||
|
status: "completed",
|
||||||
|
retries: 0,
|
||||||
|
speedBps: 0,
|
||||||
|
downloadedBytes: 123,
|
||||||
|
totalBytes: 123,
|
||||||
|
progressPercent: 100,
|
||||||
|
fileName: path.basename(part1),
|
||||||
|
targetPath: part1,
|
||||||
|
resumable: true,
|
||||||
|
attempts: 1,
|
||||||
|
lastError: "",
|
||||||
|
fullStatus: "Entpackt",
|
||||||
|
createdAt,
|
||||||
|
updatedAt: createdAt
|
||||||
|
};
|
||||||
|
|
||||||
|
new DownloadManager(
|
||||||
|
{
|
||||||
|
...defaultSettings(),
|
||||||
|
token: "rd-token",
|
||||||
|
outputDir: path.join(root, "downloads"),
|
||||||
|
extractDir: path.join(root, "extract"),
|
||||||
|
autoExtract: true,
|
||||||
|
cleanupMode: "delete"
|
||||||
|
},
|
||||||
|
session,
|
||||||
|
createStoragePaths(path.join(root, "state"))
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(() => !fs.existsSync(part2) && !fs.existsSync(part3), 5000);
|
||||||
|
expect(fs.existsSync(keep)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
it("resets run counters and reconnect state on start", async () => {
|
it("resets run counters and reconnect state on start", async () => {
|
||||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
|
||||||
tempDirs.push(root);
|
tempDirs.push(root);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user