fix(extraction): make manual archive workflows deterministic
Scope selected item runs and stop mutations precisely, attach hybrid work to the active run, retain password progress, and report finalization with real percentages. Drain stale post-processing before manual extraction, allow package and child extraction with open siblings, retry nonterminal hybrid archive attempts, and replace misleading aggregate retry counts with actionable conversion status.
This commit is contained in:
+25
-11
@@ -1452,11 +1452,23 @@ function parseProgressPercent(chunk: string): number | null {
|
||||
return latest;
|
||||
}
|
||||
|
||||
function nextArchivePercent(previous: number, incoming: number): number {
|
||||
function nextArchivePercent(previous: number, incoming: number): number {
|
||||
const prev = Math.max(0, Math.min(100, Math.floor(Number(previous) || 0)));
|
||||
const next = Math.max(0, Math.min(100, Math.floor(Number(incoming) || 0)));
|
||||
return next >= prev ? next : prev;
|
||||
}
|
||||
return next >= prev ? next : prev;
|
||||
}
|
||||
|
||||
type ExtractPasswordProgress = Pick<ExtractProgressUpdate, "passwordAttempt" | "passwordTotal" | "passwordFound">;
|
||||
|
||||
export function mergeExtractPasswordProgress(
|
||||
current: ExtractPasswordProgress | undefined,
|
||||
update: ExtractPasswordProgress | undefined
|
||||
): ExtractPasswordProgress | undefined {
|
||||
if (update?.passwordFound || (update?.passwordAttempt && update?.passwordTotal)) {
|
||||
return { ...update };
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
function runExtractCommand(
|
||||
command: string,
|
||||
@@ -4088,12 +4100,13 @@ export async function extractPackageArchives(options: ExtractOptions): Promise<E
|
||||
if (lastArchiveFinishedAt !== null) {
|
||||
logger.info(`Extract-Trace Gap: before=${archiveName}, prevDoneToStartMs=${archiveStartedAt - lastArchiveFinishedAt}, progress=${startedCurrent}/${candidates.length}`);
|
||||
}
|
||||
let archivePercent = 0;
|
||||
let reached99At: number | null = null;
|
||||
let archiveOutcome: "success" | "failed" | "skipped" = "failed";
|
||||
let archivePercent = 0;
|
||||
let reached99At: number | null = null;
|
||||
let archiveOutcome: "success" | "failed" | "skipped" = "failed";
|
||||
let activePasswordProgress: ExtractPasswordProgress | undefined;
|
||||
emitProgress(extracted + failed, archiveName, "extracting", archivePercent, 0, undefined, undefined, archivePath);
|
||||
const pulseTimer = setInterval(() => {
|
||||
emitProgress(extracted + failed, archiveName, "extracting", archivePercent, Date.now() - archiveStartedAt, undefined, undefined, archivePath);
|
||||
const pulseTimer = setInterval(() => {
|
||||
emitProgress(extracted + failed, archiveName, "extracting", archivePercent, Date.now() - archiveStartedAt, activePasswordProgress, undefined, archivePath);
|
||||
}, 1100);
|
||||
const hybrid = Boolean(options.hybridMode);
|
||||
const filenamePasswords = archiveFilenamePasswords(archiveName);
|
||||
@@ -4110,7 +4123,7 @@ export async function extractPackageArchives(options: ExtractOptions): Promise<E
|
||||
reached99At = Date.now();
|
||||
logger.info(`Extract-Trace 99%: archive=${archiveName}, elapsedMs=${reached99At - archiveStartedAt}`);
|
||||
}
|
||||
emitProgress(extracted + failed, archiveName, "extracting", archivePercent, Date.now() - archiveStartedAt, undefined, undefined, archivePath);
|
||||
emitProgress(extracted + failed, archiveName, "extracting", archivePercent, Date.now() - archiveStartedAt, activePasswordProgress, undefined, archivePath);
|
||||
};
|
||||
|
||||
const isGenericSplit = /\.\d{3}$/i.test(archiveName) && !/\.(zip|7z)\.\d{3}$/i.test(archiveName);
|
||||
@@ -4139,8 +4152,9 @@ export async function extractPackageArchives(options: ExtractOptions): Promise<E
|
||||
emitProgress(extracted + failed, archiveName, "extracting", 0, 0, { passwordAttempt: 0, passwordTotal: archivePasswordCandidates.length }, undefined, archivePath);
|
||||
}
|
||||
const onPwAttempt = hasManyPasswords
|
||||
? (attempt: number, total: number) => {
|
||||
emitProgress(extracted + failed, archiveName, "extracting", archivePercent, Date.now() - archiveStartedAt, { passwordAttempt: attempt, passwordTotal: total }, undefined, archivePath);
|
||||
? (attempt: number, total: number) => {
|
||||
activePasswordProgress = mergeExtractPasswordProgress(activePasswordProgress, { passwordAttempt: attempt, passwordTotal: total });
|
||||
emitProgress(extracted + failed, archiveName, "extracting", archivePercent, Date.now() - archiveStartedAt, activePasswordProgress, undefined, archivePath);
|
||||
options.onLog?.("INFO", `Passwort-Versuch ${attempt}/${total}: archive=${archiveName}, password=<redacted>`);
|
||||
}
|
||||
: undefined;
|
||||
|
||||
Reference in New Issue
Block a user