Cache flat-mode flag per package to skip redundant password cycles
Archives with absolute internal paths (e.g. scene groups storing full Windows paths) fail all password attempts in normal mode at ~98%, then succeed only after the flat-mode fallback kicks in. Previously every archive in such a package wasted all password cycles before discovering flat mode was needed again. Now the first successful flat-mode extraction sets a package-level flag so subsequent archives skip the normal loop entirely and go straight to flat-mode extraction, saving ~4x password attempts per archive. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8cb3640057
commit
5fbcdc1722
@ -1726,7 +1726,9 @@ async function runExternalExtract(
|
|||||||
onArchiveProgress?: (percent: number) => void,
|
onArchiveProgress?: (percent: number) => void,
|
||||||
signal?: AbortSignal,
|
signal?: AbortSignal,
|
||||||
hybridMode = false,
|
hybridMode = false,
|
||||||
onPasswordAttempt?: (attempt: number, total: number) => void
|
onPasswordAttempt?: (attempt: number, total: number) => void,
|
||||||
|
forceFlatMode = false,
|
||||||
|
flatModeResult?: { needed: boolean }
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const timeoutMs = await computeExtractTimeoutMs(archivePath);
|
const timeoutMs = await computeExtractTimeoutMs(archivePath);
|
||||||
const backendMode = extractorBackendMode();
|
const backendMode = extractorBackendMode();
|
||||||
@ -1823,7 +1825,9 @@ async function runExternalExtract(
|
|||||||
signal,
|
signal,
|
||||||
timeoutMs,
|
timeoutMs,
|
||||||
hybridMode,
|
hybridMode,
|
||||||
onPasswordAttempt
|
onPasswordAttempt,
|
||||||
|
forceFlatMode,
|
||||||
|
flatModeResult
|
||||||
);
|
);
|
||||||
const legacyMs = Date.now() - legacyStartedAt;
|
const legacyMs = Date.now() - legacyStartedAt;
|
||||||
const extractorName = path.basename(command).replace(/\.exe$/i, "");
|
const extractorName = path.basename(command).replace(/\.exe$/i, "");
|
||||||
@ -1849,19 +1853,45 @@ async function runExternalExtractInner(
|
|||||||
signal: AbortSignal | undefined,
|
signal: AbortSignal | undefined,
|
||||||
timeoutMs: number,
|
timeoutMs: number,
|
||||||
hybridMode = false,
|
hybridMode = false,
|
||||||
onPasswordAttempt?: (attempt: number, total: number) => void
|
onPasswordAttempt?: (attempt: number, total: number) => void,
|
||||||
|
forceFlatMode = false,
|
||||||
|
flatModeResult?: { needed: boolean }
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const passwords = passwordCandidates;
|
const passwords = passwordCandidates;
|
||||||
let lastError = "";
|
let lastError = "";
|
||||||
|
|
||||||
const quotedPasswords = passwords.map((p) => p === "" ? '""' : `"${p}"`);
|
const quotedPasswords = passwords.map((p) => p === "" ? '""' : `"${p}"`);
|
||||||
logger.info(`Legacy-Extractor: ${path.basename(archivePath)}, ${passwords.length} Passwörter: [${quotedPasswords.join(", ")}]`);
|
logger.info(`Legacy-Extractor: ${path.basename(archivePath)}, ${passwords.length} Passwörter: [${quotedPasswords.join(", ")}]${forceFlatMode ? " (flat-mode cached)" : ""}`);
|
||||||
|
|
||||||
let announcedStart = false;
|
let announcedStart = false;
|
||||||
let bestPercent = 0;
|
let bestPercent = 0;
|
||||||
let passwordAttempt = 0;
|
let passwordAttempt = 0;
|
||||||
let usePerformanceFlags = externalExtractorSupportsPerfFlags && shouldUseExtractorPerformanceFlags();
|
let usePerformanceFlags = externalExtractorSupportsPerfFlags && shouldUseExtractorPerformanceFlags();
|
||||||
|
|
||||||
|
// Skip normal extraction loop if flat mode is already known to be needed for this package
|
||||||
|
if (forceFlatMode) {
|
||||||
|
logger.info(`Flat-Modus direkt (gespeichert vom vorherigen Archiv): ${path.basename(archivePath)}`);
|
||||||
|
for (const password of passwords) {
|
||||||
|
if (signal?.aborted) throw new Error("aborted:extract");
|
||||||
|
passwordAttempt += 1;
|
||||||
|
const quotedPw = password === "" ? '""' : `"${password}"`;
|
||||||
|
logger.info(`Flach-Extraktion Versuch ${passwordAttempt}/${passwords.length} für ${path.basename(archivePath)}: ${quotedPw}`);
|
||||||
|
const args = buildExternalExtractArgs(command, archivePath, targetDir, conflictMode, password, usePerformanceFlags, hybridMode, true);
|
||||||
|
const result = await runExtractCommand(command, args, (chunk) => {
|
||||||
|
const parsed = parseProgressPercent(chunk);
|
||||||
|
if (parsed === null) return;
|
||||||
|
const next = nextArchivePercent(bestPercent, parsed);
|
||||||
|
if (next !== bestPercent) { bestPercent = next; onArchiveProgress?.(bestPercent); }
|
||||||
|
}, signal, timeoutMs);
|
||||||
|
logger.info(`Flach-Extraktion Versuch ${passwordAttempt}/${passwords.length}: ok=${result.ok}, bestPercent=${bestPercent}`);
|
||||||
|
if (result.ok) { if (flatModeResult) flatModeResult.needed = true; onArchiveProgress?.(100); return password; }
|
||||||
|
if (result.aborted) throw new Error("aborted:extract");
|
||||||
|
if (result.timedOut || result.missingCommand) break;
|
||||||
|
lastError = result.errorText;
|
||||||
|
}
|
||||||
|
throw new Error(lastError || "Entpacken fehlgeschlagen (flat-mode)");
|
||||||
|
}
|
||||||
|
|
||||||
for (const password of passwords) {
|
for (const password of passwords) {
|
||||||
if (signal?.aborted) {
|
if (signal?.aborted) {
|
||||||
throw new Error("aborted:extract");
|
throw new Error("aborted:extract");
|
||||||
@ -1958,7 +1988,7 @@ async function runExternalExtractInner(
|
|||||||
if (next !== bestPercent) { bestPercent = next; onArchiveProgress?.(bestPercent); }
|
if (next !== bestPercent) { bestPercent = next; onArchiveProgress?.(bestPercent); }
|
||||||
}, signal, timeoutMs);
|
}, signal, timeoutMs);
|
||||||
logger.info(`Flach-Extraktion Versuch ${passwordAttempt}/${passwords.length}: ok=${result.ok}, bestPercent=${bestPercent}`);
|
logger.info(`Flach-Extraktion Versuch ${passwordAttempt}/${passwords.length}: ok=${result.ok}, bestPercent=${bestPercent}`);
|
||||||
if (result.ok) { onArchiveProgress?.(100); return password; }
|
if (result.ok) { if (flatModeResult) flatModeResult.needed = true; onArchiveProgress?.(100); return password; }
|
||||||
if (result.aborted) throw new Error("aborted:extract");
|
if (result.aborted) throw new Error("aborted:extract");
|
||||||
if (result.timedOut || result.missingCommand) break;
|
if (result.timedOut || result.missingCommand) break;
|
||||||
lastError = result.errorText;
|
lastError = result.errorText;
|
||||||
@ -2435,6 +2465,7 @@ export async function extractPackageArchives(options: ExtractOptions): Promise<{
|
|||||||
let failed = 0;
|
let failed = 0;
|
||||||
let lastError = "";
|
let lastError = "";
|
||||||
let learnedPassword = cachedPackagePassword;
|
let learnedPassword = cachedPackagePassword;
|
||||||
|
let packageNeedsFlatMode = false;
|
||||||
const extractedArchives = new Set<string>();
|
const extractedArchives = new Set<string>();
|
||||||
for (const archivePath of candidates) {
|
for (const archivePath of candidates) {
|
||||||
if (resumeCompleted.has(archiveNameKey(path.basename(archivePath)))) {
|
if (resumeCompleted.has(archiveNameKey(path.basename(archivePath)))) {
|
||||||
@ -2620,10 +2651,12 @@ export async function extractPackageArchives(options: ExtractOptions): Promise<{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
const flatResult = { needed: false };
|
||||||
const usedPassword = await runExternalExtract(archivePath, options.targetDir, options.conflictMode, archivePasswordCandidates, (value) => {
|
const usedPassword = await runExternalExtract(archivePath, options.targetDir, options.conflictMode, archivePasswordCandidates, (value) => {
|
||||||
reportArchiveProgress(value);
|
reportArchiveProgress(value);
|
||||||
}, options.signal, hybrid, onPwAttempt);
|
}, options.signal, hybrid, onPwAttempt, packageNeedsFlatMode, flatResult);
|
||||||
rememberLearnedPassword(usedPassword);
|
rememberLearnedPassword(usedPassword);
|
||||||
|
if (flatResult.needed) packageNeedsFlatMode = true;
|
||||||
}
|
}
|
||||||
extracted += 1;
|
extracted += 1;
|
||||||
extractedArchives.add(archivePath);
|
extractedArchives.add(archivePath);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user