From 927ff5c21a5d8ad7fc72de01f66e9bc5e973b712 Mon Sep 17 00:00:00 2001 From: Sucukdeluxe Date: Sat, 7 Mar 2026 19:36:30 +0100 Subject: [PATCH] Auto-fix legacy (N) suffix filenames on startup Detect items whose targetPath has a " (N)" suffix from the previous duplicate filename bug and rename them back to the original filename. This fixes extraction failures for RAR split archives that were downloaded with (1) suffix before v1.7.19. Co-Authored-By: Claude Opus 4.6 --- src/main/download-manager.ts | 50 ++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/main/download-manager.ts b/src/main/download-manager.ts index 5bc22a6..715e423 100644 --- a/src/main/download-manager.ts +++ b/src/main/download-manager.ts @@ -3988,6 +3988,56 @@ export class DownloadManager extends EventEmitter { if (restored > 0) { logger.info(`restoreTargetPathReservations: ${restored} Pfade aus Session wiederhergestellt`); } + // Fix legacy (N) suffix files: rename back to original if original path is free + this.fixDuplicateSuffixFiles(); + } + + /** Detect items whose targetPath has a " (N)" suffix from a previous bug and rename + * them back to the original filename if the original path is not claimed by another item. */ + private fixDuplicateSuffixFiles(): void { + const SUFFIX_RE = /^(.+) \(\d+\)(\.[^.]+)$/; + let fixed = 0; + for (const item of Object.values(this.session.items)) { + const tp = String(item.targetPath || "").trim(); + if (!tp) continue; + const parsed = path.parse(tp); + const fullName = parsed.name + parsed.ext; // e.g. "file.part3 (1).rar" + const match = SUFFIX_RE.exec(fullName); + if (!match) continue; + const originalName = match[1] + match[2]; // "file.part3.rar" + const originalPath = path.join(parsed.dir, originalName); + const originalKey = pathKey(originalPath); + const originalOwner = this.reservedTargetPaths.get(originalKey); + // Only rename if original path is not claimed by another item and doesn't exist on disk + if (originalOwner && originalOwner !== item.id) continue; + if (!originalOwner && fs.existsSync(originalPath)) continue; + if (!fs.existsSync(tp)) { + // File with (N) doesn't exist either — just fix the path reference + this.reservedTargetPaths.delete(pathKey(tp)); + this.reservedTargetPaths.set(originalKey, item.id); + this.claimedTargetPathByItem.set(item.id, originalPath); + item.targetPath = originalPath; + item.fileName = originalName; + fixed += 1; + continue; + } + try { + fs.renameSync(tp, originalPath); + this.reservedTargetPaths.delete(pathKey(tp)); + this.reservedTargetPaths.set(originalKey, item.id); + this.claimedTargetPathByItem.set(item.id, originalPath); + item.targetPath = originalPath; + item.fileName = originalName; + fixed += 1; + logger.info(`fixDuplicateSuffix: ${path.basename(tp)} → ${originalName}`); + } catch (err) { + logger.warn(`fixDuplicateSuffix: Umbenennung fehlgeschlagen ${tp}: ${compactErrorText(err)}`); + } + } + if (fixed > 0) { + logger.info(`fixDuplicateSuffixFiles: ${fixed} Dateien korrigiert`); + this.persistSoon(); + } } private assignItemTargetPath(item: DownloadItem, targetPath: string): string {