Fix hybrid-extract multi-part archive + extractor CRC handling
- findReadyArchiveSets: for .part1.rar, require ALL package items to be terminal before allowing extraction (prevents premature extraction when later parts have no targetPath/fileName yet) - JVM extractor: remove CRCERROR from isPasswordFailure() — only DATAERROR indicates wrong password. CRCERROR on archives where 7z-JBinding falsely reports encrypted no longer triggers password cycling. - looksLikeWrongPassword: remove CRC text matching, keep only explicit "data error" for encrypted archives. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
0cf5ebe5e9
commit
1fde0a9951
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -356,7 +356,10 @@ public final class JBindExtractorMain {
|
|||||||
if (!encrypted || result == null) {
|
if (!encrypted || result == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return result == ExtractOperationResult.CRCERROR || result == ExtractOperationResult.DATAERROR;
|
// Only DATAERROR reliably indicates wrong password. CRCERROR can also mean
|
||||||
|
// a genuinely corrupt or incomplete archive, and 7z-JBinding sometimes
|
||||||
|
// falsely reports encrypted=true for non-encrypted RAR files.
|
||||||
|
return result == ExtractOperationResult.DATAERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean looksLikeWrongPassword(Throwable error, boolean encrypted) {
|
private static boolean looksLikeWrongPassword(Throwable error, boolean encrypted) {
|
||||||
@ -367,7 +370,9 @@ public final class JBindExtractorMain {
|
|||||||
if (text.contains("wrong password") || text.contains("falsches passwort")) {
|
if (text.contains("wrong password") || text.contains("falsches passwort")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return encrypted && (text.contains("crc") || text.contains("data error") || text.contains("checksum"));
|
// Only "data error" suggests wrong password. CRC errors can also mean
|
||||||
|
// corrupt/incomplete archives, so we don't treat them as password failures.
|
||||||
|
return encrypted && text.contains("data error");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean shouldUseZip4j(File archiveFile) {
|
private static boolean shouldUseZip4j(File archiveFile) {
|
||||||
|
|||||||
@ -4988,11 +4988,26 @@ export class DownloadManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pre-compute: does the package still have any non-terminal items?
|
||||||
|
const packageHasPendingItems = pkg.itemIds.some((itemId) => {
|
||||||
|
const item = this.session.items[itemId];
|
||||||
|
return item != null && item.status !== "completed" && item.status !== "failed" && item.status !== "cancelled";
|
||||||
|
});
|
||||||
|
|
||||||
for (const candidate of candidates) {
|
for (const candidate of candidates) {
|
||||||
const partsOnDisk = collectArchiveCleanupTargets(candidate, dirFiles);
|
const partsOnDisk = collectArchiveCleanupTargets(candidate, dirFiles);
|
||||||
const allPartsCompleted = partsOnDisk.every((part) => completedPaths.has(pathKey(part)));
|
const allPartsCompleted = partsOnDisk.every((part) => completedPaths.has(pathKey(part)));
|
||||||
if (allPartsCompleted) {
|
if (allPartsCompleted) {
|
||||||
const candidateBase = path.basename(candidate).toLowerCase();
|
const candidateBase = path.basename(candidate).toLowerCase();
|
||||||
|
|
||||||
|
// For multi-part archives (.part1.rar), require ALL package items to be terminal.
|
||||||
|
// partsOnDisk only contains parts found ON DISK — pending parts that haven't been
|
||||||
|
// downloaded yet (no targetPath, no fileName) would slip through the regular checks.
|
||||||
|
if (/\.part0*1\.rar$/i.test(candidateBase) && packageHasPendingItems) {
|
||||||
|
logger.info(`Hybrid-Extract: ${path.basename(candidate)} übersprungen – Paket hat noch ausstehende Items`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const hasUnstartedParts = [...pendingPaths].some((pendingPath) => {
|
const hasUnstartedParts = [...pendingPaths].some((pendingPath) => {
|
||||||
const pendingName = path.basename(pendingPath).toLowerCase();
|
const pendingName = path.basename(pendingPath).toLowerCase();
|
||||||
return this.looksLikeArchivePart(pendingName, candidateBase);
|
return this.looksLikeArchivePart(pendingName, candidateBase);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user