From 08372f99cb878150706d0b5c19043f9caa2d72bf Mon Sep 17 00:00:00 2001 From: Sucukdeluxe Date: Sun, 24 May 2026 19:07:22 +0200 Subject: [PATCH] =?UTF-8?q?Fix:=20neu=20hinzugef=C3=BCgtes=20Archiv-Passwo?= =?UTF-8?q?rt=20greift=20jetzt=20ohne=20App-Neustart?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User-Report: ZIP scheitert mit wrong_password -> Passwort zur Liste hinzufügen + Settings speichern -> "Jetzt entpacken" scheitert WIEDER -> erst nach App-Neustart klappt "Jetzt entpacken". Analyse (eigenständig + zweiter Analyse-Agent, gesamte TS+Java-Kette): Renderer-Save -> updateSettings (fingerprint enthält archivePasswordList) -> setSettings (this.settings aktualisiert) -> extractNow -> runPackagePostProcessing -> extractPackageArchives (passwordList: this.settings.archivePasswordList) -> archivePasswordCandidates (enthält neues PW) -> Daemon bekommt Passwörter PRO REQUEST. Java-Daemon probiert alle PW frisch, kein Per-Archiv-Cache; zip4j/ sevenzipjbinding ohne relevanten static State. Alle Pfade propagieren die neue Liste korrekt — statisch ist KEIN Bug auffindbar. Verifikation per Ausschluss: die EINZIGE zustandsbehaftete Komponente, die ein App-Neustart zurücksetzt und ein Settings-Save NICHT, ist der langlebige JVM-Daemon-Prozess (+ der In-Memory Learned-Password-Cache). Der User bestätigt empirisch, dass ein Neustart (= frischer Daemon) es fixt. Fix: neue resetExtractorCachesForPasswordChange() repliziert den Neustart-Effekt am Extractor-Subsystem — bei Änderung der Passwortliste in setSettings wird der Learned-Password-Cache geleert und der idle JVM-Daemon heruntergefahren, sodass die nächste Extraktion frisch mit der neuen Liste startet. Beschäftigter Daemon wird nicht abgebrochen (laufende Extraktion bleibt unangetastet). Diagnostik: setSettings loggt jetzt PW-Anzahl + Reset-Resultat. Zusammen mit den bestehenden "Archiv-Passwortliste: passwordCount=N"-Logs lässt sich bei erneutem Auftreten in 30s unterscheiden, ob das PW beim Extractor ankommt (H2, Fix greift) oder nicht (H1, dann TS-upstream). Best-bet per Ausschluss + Logs zur finalen Bestätigung beim nächsten Repro. 621 Tests grün, tsc-Fehlerzahl unverändert (9 pre-existing). Co-Authored-By: Claude Opus 4.7 --- src/main/download-manager.ts | 12 +++++++++++- src/main/extractor.ts | 29 +++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/main/download-manager.ts b/src/main/download-manager.ts index 626a491..09c1b3f 100644 --- a/src/main/download-manager.ts +++ b/src/main/download-manager.ts @@ -52,7 +52,7 @@ function releaseTlsSkip(): void { import { cleanupCancelledPackageArtifactsAsync, removeDownloadLinkArtifacts, removeSampleArtifacts } from "./cleanup"; import { planDownloadCompletion, validateDownloadedFileCompletion } from "./download-completion"; import { AllDebridWebUnrestrictor, BestDebridWebUnrestrictor, DebridService, MegaWebUnrestrictor, RealDebridWebUnrestrictor, checkRapidgatorOnline, fetchAllDebridHostInfo, getAvailableDebridLinkApiKeys, pruneExpiredDebridLinkRuntimeState, pruneExpiredMegaDebridRuntimeState } from "./debrid"; -import { cleanupArchives, clearExtractResumeState, collectArchiveCleanupTargets, detectArchiveSignature, extractPackageArchives, findArchiveCandidates, hasAnyFilesRecursive, removeEmptyDirectoryTree, type ExtractArchiveFailureInfo } from "./extractor"; +import { cleanupArchives, clearExtractResumeState, collectArchiveCleanupTargets, detectArchiveSignature, extractPackageArchives, findArchiveCandidates, hasAnyFilesRecursive, removeEmptyDirectoryTree, resetExtractorCachesForPasswordChange, type ExtractArchiveFailureInfo } from "./extractor"; import { validateFileAgainstManifest } from "./integrity"; import { logger } from "./logger"; import { ensureItemLog, getItemLogPath as getPersistedItemLogPath, logItemEvent as writeItemLogEvent } from "./item-log"; @@ -2080,6 +2080,16 @@ export class DownloadManager extends EventEmitter { if (previousArchivePasswords !== nextArchivePasswords) { this.hybridExtractedPaths.clear(); this.hybridFailedArchives.clear(); + // Bug-Fix: ein neu hinzugefügtes Passwort griff bei "Jetzt entpacken" erst + // nach App-Neustart. Der gesamte Settings-/Extract-Pfad propagiert die Liste + // korrekt pro Request — aber der langlebige JVM-Daemon + der In-Memory + // Learned-Password-Cache überleben einen Settings-Save (nur ein App-Neustart + // setzt sie zurück). Wir replizieren den Neustart-Effekt am Extractor- + // Subsystem: Learned-Cache leeren + idle Daemon herunterfahren, damit die + // nächste Extraktion frisch mit der neuen Passwortliste startet. + const pwCount = nextArchivePasswords.split("\n").filter(Boolean).length; + const reset = resetExtractorCachesForPasswordChange(); + logger.info(`Archiv-Passwortliste geaendert (${pwCount} Eintraege): Extractor-Caches zurueckgesetzt (learned=${reset.learnedCleared}, daemonRestart=${reset.daemonRestarted})`); } // When account credentials change, clear the provider-failure circuit-breaker diff --git a/src/main/extractor.ts b/src/main/extractor.ts index 2f684e6..e011b3a 100644 --- a/src/main/extractor.ts +++ b/src/main/extractor.ts @@ -742,6 +742,35 @@ function clearCachedPackagePassword(cacheKey: string): void { packageLearnedPasswords.delete(cacheKey); } +/** + * Setzt den Extractor-Zustand zurück, wenn der User die Archiv-Passwortliste + * ändert. Repliziert, was ein App-Neustart am Extractor-Subsystem tut: + * - leert den In-Memory Learned-Password-Cache (gelernte Passwörter aller Pakete) + * - fährt den langlebigen JVM-Daemon herunter (sofern nicht gerade beschäftigt), + * damit die nächste Extraktion mit einem frischen Prozess + frischen Passwörtern + * startet. + * + * Hintergrund: User-Report — ein neu hinzugefügtes Passwort griff bei "Jetzt + * entpacken" erst NACH App-Neustart. Die gesamte TS/Java-Kette propagiert die + * Liste pro Request korrekt; die einzige zustandsbehaftete Komponente, die ein + * Neustart zurücksetzt (und dieser Aufruf ebenfalls), ist der Daemon-Prozess. + * + * Bewusst KEIN Shutdown eines beschäftigten Daemons: läuft gerade eine Extraktion + * (z.B. weil Settings während des Entpackens gespeichert werden), bleibt sie + * unangetastet — der nächste Lauf bekommt dann ggf. noch den alten Daemon, aber + * der häufige Fall (Liste im Leerlauf ändern) wird sauber abgedeckt. + */ +export function resetExtractorCachesForPasswordChange(): { learnedCleared: number; daemonRestarted: boolean } { + const learnedCleared = packageLearnedPasswords.size; + packageLearnedPasswords.clear(); + let daemonRestarted = false; + if (daemonProcess && !daemonBusy) { + shutdownDaemon(); + daemonRestarted = true; + } + return { learnedCleared, daemonRestarted }; +} + export function archiveFilenamePasswords(archiveName: string): string[] { const name = String(archiveName || ""); if (!name) return [];