Release v1.7.44
This commit is contained in:
parent
2a51c443b8
commit
28113f57f3
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "real-debrid-downloader",
|
||||
"version": "1.7.43",
|
||||
"version": "1.7.44",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "real-debrid-downloader",
|
||||
"version": "1.7.43",
|
||||
"version": "1.7.44",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"adm-zip": "^0.5.16",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "real-debrid-downloader",
|
||||
"version": "1.7.43",
|
||||
"version": "1.7.44",
|
||||
"description": "Desktop downloader",
|
||||
"main": "build/main/main/main.js",
|
||||
"author": "Sucukdeluxe",
|
||||
|
||||
@ -679,7 +679,7 @@ function prioritizePassword(passwords: string[], successful: string): string[] {
|
||||
return next;
|
||||
}
|
||||
|
||||
function nativeExtractorCandidates(): string[] {
|
||||
function nativeExtractorCandidates(archivePath = ""): string[] {
|
||||
const programFiles = process.env.ProgramFiles || "C:\\Program Files";
|
||||
const programFilesX86 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
|
||||
const localAppData = process.env.LOCALAPPDATA || "";
|
||||
@ -694,11 +694,14 @@ function nativeExtractorCandidates(): string[] {
|
||||
}
|
||||
|
||||
const winRarInstalled = [
|
||||
path.join(programFiles, "WinRAR", "WinRAR.exe"),
|
||||
path.join(programFilesX86, "WinRAR", "WinRAR.exe"),
|
||||
path.join(programFiles, "WinRAR", "UnRAR.exe"),
|
||||
path.join(programFilesX86, "WinRAR", "UnRAR.exe")
|
||||
];
|
||||
|
||||
if (localAppData) {
|
||||
winRarInstalled.push(path.join(localAppData, "Programs", "WinRAR", "WinRAR.exe"));
|
||||
winRarInstalled.push(path.join(localAppData, "Programs", "WinRAR", "UnRAR.exe"));
|
||||
}
|
||||
|
||||
@ -711,6 +714,8 @@ function nativeExtractorCandidates(): string[] {
|
||||
"7za.exe",
|
||||
"7za",
|
||||
...winRarInstalled,
|
||||
"WinRAR.exe",
|
||||
"winrar",
|
||||
"UnRAR.exe",
|
||||
"unrar"
|
||||
]
|
||||
@ -721,10 +726,12 @@ function nativeExtractorCandidates(): string[] {
|
||||
"7za.exe",
|
||||
"7za",
|
||||
...winRarInstalled,
|
||||
"WinRAR.exe",
|
||||
"winrar",
|
||||
"UnRAR.exe",
|
||||
"unrar"
|
||||
];
|
||||
return Array.from(new Set(ordered.filter(Boolean)));
|
||||
return orderExtractorCandidatesForArchive(ordered, archivePath, resolvedExtractorCommand || "");
|
||||
}
|
||||
|
||||
function isAbsoluteCommand(command: string): boolean {
|
||||
@ -1786,7 +1793,7 @@ export function buildExternalExtractArgs(
|
||||
return ["x", "-y", overwrite, pass, archivePath, `-o${targetDir}`];
|
||||
}
|
||||
|
||||
async function resolveExtractorCommandInternal(): Promise<string> {
|
||||
async function resolveExtractorCommandInternal(archivePath = ""): Promise<string> {
|
||||
if (resolvedExtractorCommand) {
|
||||
return resolvedExtractorCommand;
|
||||
}
|
||||
@ -1799,7 +1806,7 @@ async function resolveExtractorCommandInternal(): Promise<string> {
|
||||
resolveFailureAt = 0;
|
||||
}
|
||||
|
||||
const candidates = nativeExtractorCandidates();
|
||||
const candidates = nativeExtractorCandidates(archivePath);
|
||||
for (const command of candidates) {
|
||||
if (isAbsoluteCommand(command) && !fs.existsSync(command)) {
|
||||
continue;
|
||||
@ -1821,15 +1828,15 @@ async function resolveExtractorCommandInternal(): Promise<string> {
|
||||
throw new Error(resolveFailureReason);
|
||||
}
|
||||
|
||||
async function resolveExtractorCommand(): Promise<string> {
|
||||
if (resolvedExtractorCommand) {
|
||||
async function resolveExtractorCommand(archivePath = ""): Promise<string> {
|
||||
if (resolvedExtractorCommand && cachedExtractorFitsArchive(resolvedExtractorCommand, archivePath)) {
|
||||
return resolvedExtractorCommand;
|
||||
}
|
||||
if (resolveExtractorCommandInFlight) {
|
||||
return resolveExtractorCommandInFlight;
|
||||
}
|
||||
|
||||
const pending = resolveExtractorCommandInternal();
|
||||
const pending = resolveExtractorCommandInternal(archivePath);
|
||||
resolveExtractorCommandInFlight = pending;
|
||||
try {
|
||||
return await pending;
|
||||
@ -1850,8 +1857,71 @@ function isUnrarCommand(command: string): boolean {
|
||||
return lower.includes("unrar") || lower.includes("winrar");
|
||||
}
|
||||
|
||||
async function findAlternativeExtractor(currentCommand: string): Promise<string | null> {
|
||||
const candidates = nativeExtractorCandidates();
|
||||
type ExtractorCommandKind = "rar_native" | "seven_zip" | "other";
|
||||
|
||||
function extractorCommandKind(command: string): ExtractorCommandKind {
|
||||
if (isUnrarCommand(command)) {
|
||||
return "rar_native";
|
||||
}
|
||||
if (is7zCommand(command)) {
|
||||
return "seven_zip";
|
||||
}
|
||||
return "other";
|
||||
}
|
||||
|
||||
function isRarArchivePath(filePath: string): boolean {
|
||||
return /\.(?:rar|r\d{2,3})$/i.test(String(filePath || ""));
|
||||
}
|
||||
|
||||
function cachedExtractorFitsArchive(command: string, archivePath: string): boolean {
|
||||
if (!archivePath) {
|
||||
return true;
|
||||
}
|
||||
const kind = extractorCommandKind(command);
|
||||
if (isRarArchivePath(archivePath)) {
|
||||
return kind === "rar_native";
|
||||
}
|
||||
return kind === "seven_zip";
|
||||
}
|
||||
|
||||
export function orderExtractorCandidatesForArchive(
|
||||
candidates: string[],
|
||||
archivePath: string,
|
||||
preferredCommand = ""
|
||||
): string[] {
|
||||
const unique = Array.from(new Set(candidates.filter(Boolean)));
|
||||
const preferRarNative = isRarArchivePath(archivePath);
|
||||
const rank = (command: string): number => {
|
||||
const kind = extractorCommandKind(command);
|
||||
if (preferRarNative) {
|
||||
if (kind === "rar_native") return 0;
|
||||
if (kind === "seven_zip") return 1;
|
||||
return 2;
|
||||
}
|
||||
if (kind === "seven_zip") return 0;
|
||||
if (kind === "rar_native") return 1;
|
||||
return 2;
|
||||
};
|
||||
|
||||
return unique
|
||||
.map((command, index) => ({ command, index }))
|
||||
.sort((left, right) => {
|
||||
const rankDiff = rank(left.command) - rank(right.command);
|
||||
if (rankDiff !== 0) {
|
||||
return rankDiff;
|
||||
}
|
||||
const leftPreferred = preferredCommand && left.command === preferredCommand;
|
||||
const rightPreferred = preferredCommand && right.command === preferredCommand;
|
||||
if (leftPreferred !== rightPreferred) {
|
||||
return leftPreferred ? -1 : 1;
|
||||
}
|
||||
return left.index - right.index;
|
||||
})
|
||||
.map((entry) => entry.command);
|
||||
}
|
||||
|
||||
async function findAlternativeExtractor(currentCommand: string, archivePath = ""): Promise<string | null> {
|
||||
const candidates = nativeExtractorCandidates(archivePath);
|
||||
const currentIs7z = is7zCommand(currentCommand);
|
||||
for (const candidate of candidates) {
|
||||
if (candidate === currentCommand) continue;
|
||||
@ -1971,7 +2041,7 @@ async function runExternalExtract(
|
||||
subst = createSubstMapping(targetDir);
|
||||
const effectiveTargetDir = subst ? `${subst.drive}:\\` : targetDir;
|
||||
|
||||
const command = await resolveExtractorCommand();
|
||||
const command = await resolveExtractorCommand(archivePath);
|
||||
const legacyStartedAt = Date.now();
|
||||
let password: string;
|
||||
let usedCommand = command;
|
||||
@ -1999,7 +2069,7 @@ async function runExternalExtract(
|
||||
const errText = String((primaryError as Error)?.message || primaryError || "");
|
||||
const isPasswordOrCorrupt = /wrong.password|checksum error|corrupt/i.test(errText);
|
||||
if (isRar && isPasswordOrCorrupt && !signal?.aborted) {
|
||||
const alt = await findAlternativeExtractor(command);
|
||||
const alt = await findAlternativeExtractor(command, archivePath);
|
||||
if (alt) {
|
||||
const altName = path.basename(alt).replace(/\.exe$/i, "");
|
||||
onLog?.("INFO", `Legacy-Fallback: primary=${path.basename(command)}, alternative=${altName}, archive=${archiveName}`);
|
||||
@ -2121,10 +2191,11 @@ async function runExternalExtractInner(
|
||||
): Promise<string> {
|
||||
const passwords = passwordCandidates;
|
||||
let lastError = "";
|
||||
const extractorName = path.basename(command).replace(/\.exe$/i, "") || command;
|
||||
|
||||
const quotedPasswords = passwords.map((p) => p === "" ? '""' : `"${p}"`);
|
||||
onLog?.("INFO", `Legacy-Extractor Start: archive=${path.basename(archivePath)}, passwordCount=${passwords.length}, forceFlatMode=${forceFlatMode}, targetDir=${targetDir}`);
|
||||
logger.info(`Legacy-Extractor: ${path.basename(archivePath)}, ${passwords.length} Passwörter: [${quotedPasswords.join(", ")}]${forceFlatMode ? " (flat-mode cached)" : ""}`);
|
||||
onLog?.("INFO", `Legacy-Extractor Start: archive=${path.basename(archivePath)}, extractor=${extractorName}, passwordCount=${passwords.length}, forceFlatMode=${forceFlatMode}, targetDir=${targetDir}`);
|
||||
logger.info(`Legacy-Extractor (${extractorName}): ${path.basename(archivePath)}, ${passwords.length} Passwörter: [${quotedPasswords.join(", ")}]${forceFlatMode ? " (flat-mode cached)" : ""}`);
|
||||
|
||||
let announcedStart = false;
|
||||
let bestPercent = 0;
|
||||
|
||||
@ -12,6 +12,7 @@ import {
|
||||
detectArchiveSignature,
|
||||
classifyExtractionError,
|
||||
findArchiveCandidates,
|
||||
orderExtractorCandidatesForArchive,
|
||||
resolveExtractorBackendMode,
|
||||
} from "../src/main/extractor";
|
||||
|
||||
@ -1171,4 +1172,33 @@ describe("extractor", () => {
|
||||
expect(resolveExtractorBackendMode("auto", false)).toBe("auto");
|
||||
});
|
||||
});
|
||||
|
||||
describe("orderExtractorCandidatesForArchive", () => {
|
||||
it("prefers WinRAR/UnRAR over 7-Zip for rar archives", () => {
|
||||
const ordered = orderExtractorCandidatesForArchive(
|
||||
["7z.exe", "UnRAR.exe", "WinRAR.exe"],
|
||||
"C:\\Downloads\\archive.part01.rar"
|
||||
);
|
||||
expect(ordered.slice(0, 2)).toEqual(["UnRAR.exe", "WinRAR.exe"]);
|
||||
expect(ordered[2]).toBe("7z.exe");
|
||||
});
|
||||
|
||||
it("keeps 7-Zip first for non-rar archives", () => {
|
||||
const ordered = orderExtractorCandidatesForArchive(
|
||||
["UnRAR.exe", "7z.exe", "WinRAR.exe"],
|
||||
"C:\\Downloads\\archive.zip"
|
||||
);
|
||||
expect(ordered[0]).toBe("7z.exe");
|
||||
});
|
||||
|
||||
it("prefers the remembered command within the matching archive class", () => {
|
||||
const ordered = orderExtractorCandidatesForArchive(
|
||||
["UnRAR.exe", "WinRAR.exe", "7z.exe"],
|
||||
"C:\\Downloads\\archive.part01.rar",
|
||||
"WinRAR.exe"
|
||||
);
|
||||
expect(ordered[0]).toBe("WinRAR.exe");
|
||||
expect(ordered[1]).toBe("UnRAR.exe");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user