From 0f61b0be08021d3969966c2a742638cdd6d2f507 Mon Sep 17 00:00:00 2001 From: Sucukdeluxe Date: Fri, 27 Feb 2026 11:53:14 +0100 Subject: [PATCH] Reduce cancel lag with non-blocking cleanup in v1.1.25 --- package-lock.json | 4 +-- package.json | 2 +- src/main/cleanup.ts | 47 ++++++++++++++++++++++++++++++++++++ src/main/constants.ts | 2 +- src/main/download-manager.ts | 17 ++++++++++--- tests/self-check.ts | 4 ++- 6 files changed, 68 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 765d460..a6470db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "real-debrid-downloader", - "version": "1.1.24", + "version": "1.1.25", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "real-debrid-downloader", - "version": "1.1.24", + "version": "1.1.25", "license": "MIT", "dependencies": { "adm-zip": "^0.5.16", diff --git a/package.json b/package.json index 57c488d..d19aa89 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "real-debrid-downloader", - "version": "1.1.24", + "version": "1.1.25", "description": "Real-Debrid Downloader Desktop (Electron + React + TypeScript)", "main": "build/main/main/main.js", "author": "Sucukdeluxe", diff --git a/src/main/cleanup.ts b/src/main/cleanup.ts index 70ef869..265f89e 100644 --- a/src/main/cleanup.ts +++ b/src/main/cleanup.ts @@ -2,6 +2,12 @@ import fs from "node:fs"; import path from "node:path"; import { ARCHIVE_TEMP_EXTENSIONS, LINK_ARTIFACT_EXTENSIONS, RAR_SPLIT_RE, SAMPLE_DIR_NAMES, SAMPLE_TOKEN_RE, SAMPLE_VIDEO_EXTENSIONS } from "./constants"; +async function yieldToLoop(): Promise { + await new Promise((resolve) => { + setTimeout(resolve, 0); + }); +} + export function isArchiveOrTempFile(filePath: string): boolean { const lower = filePath.toLowerCase(); const ext = path.extname(lower); @@ -39,6 +45,47 @@ export function cleanupCancelledPackageArtifacts(packageDir: string): number { return removed; } +export async function cleanupCancelledPackageArtifactsAsync(packageDir: string): Promise { + try { + await fs.promises.access(packageDir, fs.constants.F_OK); + } catch { + return 0; + } + + let removed = 0; + let touched = 0; + const stack = [packageDir]; + while (stack.length > 0) { + const current = stack.pop() as string; + let entries: fs.Dirent[] = []; + try { + entries = await fs.promises.readdir(current, { withFileTypes: true }); + } catch { + continue; + } + + for (const entry of entries) { + const full = path.join(current, entry.name); + if (entry.isDirectory()) { + stack.push(full); + } else if (entry.isFile() && isArchiveOrTempFile(full)) { + try { + await fs.promises.rm(full, { force: true }); + removed += 1; + } catch { + // ignore + } + } + + touched += 1; + if (touched % 80 === 0) { + await yieldToLoop(); + } + } + } + return removed; +} + export function removeDownloadLinkArtifacts(extractDir: string): number { if (!fs.existsSync(extractDir)) { return 0; diff --git a/src/main/constants.ts b/src/main/constants.ts index dc366ce..2d1929b 100644 --- a/src/main/constants.ts +++ b/src/main/constants.ts @@ -3,7 +3,7 @@ import os from "node:os"; import { AppSettings } from "../shared/types"; export const APP_NAME = "Debrid Download Manager"; -export const APP_VERSION = "1.1.24"; +export const APP_VERSION = "1.1.25"; export const API_BASE_URL = "https://api.real-debrid.com/rest/1.0"; export const DCRYPT_UPLOAD_URL = "https://dcrypt.it/decrypt/upload"; diff --git a/src/main/download-manager.ts b/src/main/download-manager.ts index 7351306..62f1ffc 100644 --- a/src/main/download-manager.ts +++ b/src/main/download-manager.ts @@ -5,7 +5,7 @@ import { EventEmitter } from "node:events"; import { v4 as uuidv4 } from "uuid"; import { AppSettings, DownloadItem, DownloadSummary, DownloadStatus, PackageEntry, ParsedPackageInput, SessionState, UiSnapshot } from "../shared/types"; import { CHUNK_SIZE, REQUEST_RETRIES } from "./constants"; -import { cleanupCancelledPackageArtifacts, removeDownloadLinkArtifacts, removeSampleArtifacts } from "./cleanup"; +import { cleanupCancelledPackageArtifactsAsync } from "./cleanup"; import { DebridService, MegaWebUnrestrictor } from "./debrid"; import { extractPackageArchives } from "./extractor"; import { validateFileAgainstManifest } from "./integrity"; @@ -112,6 +112,8 @@ export class DownloadManager extends EventEmitter { private speedBytesLastWindow = 0; + private cleanupQueue: Promise = Promise.resolve(); + private reservedTargetPaths = new Map(); private claimedTargetPathByItem = new Map(); @@ -332,6 +334,8 @@ export class DownloadManager extends EventEmitter { if (!pkg) { return; } + const packageName = pkg.name; + const outputDir = pkg.outputDir; const itemIds = [...pkg.itemIds]; for (const itemId of itemIds) { @@ -347,11 +351,18 @@ export class DownloadManager extends EventEmitter { } } - const removed = cleanupCancelledPackageArtifacts(pkg.outputDir); this.removePackageFromSession(packageId, itemIds); - logger.info(`Paket ${pkg.name} abgebrochen, ${removed} Artefakte gelöscht`); this.persistSoon(); this.emitState(true); + + this.cleanupQueue = this.cleanupQueue + .then(async () => { + const removed = await cleanupCancelledPackageArtifactsAsync(outputDir); + logger.info(`Paket ${packageName} abgebrochen, ${removed} Artefakte gelöscht`); + }) + .catch((error) => { + logger.warn(`Cleanup für Paket ${packageName} fehlgeschlagen: ${compactErrorText(error)}`); + }); } public start(): void { diff --git a/tests/self-check.ts b/tests/self-check.ts index 794ba49..61b584c 100644 --- a/tests/self-check.ts +++ b/tests/self-check.ts @@ -193,7 +193,9 @@ async function main(): Promise { assert(cancelItem?.status === "cancelled" || cancelItem?.status === "queued", "Paketabbruch nicht wirksam"); } const packageDir = path.join(path.join(tempRoot, "downloads-cancel"), "cancel"); - assert(!fs.existsSync(path.join(packageDir, "release.part1.rar")), "RAR-Artefakt wurde nicht gelöscht"); + const cancelArtifact = path.join(packageDir, "release.part1.rar"); + await waitFor(() => !fs.existsSync(cancelArtifact), 10000); + assert(!fs.existsSync(cancelArtifact), "RAR-Artefakt wurde nicht gelöscht"); console.log("Node self-check erfolgreich"); } finally {