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",
|
"name": "real-debrid-downloader",
|
||||||
"version": "1.7.43",
|
"version": "1.7.44",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "real-debrid-downloader",
|
"name": "real-debrid-downloader",
|
||||||
"version": "1.7.43",
|
"version": "1.7.44",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"adm-zip": "^0.5.16",
|
"adm-zip": "^0.5.16",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "real-debrid-downloader",
|
"name": "real-debrid-downloader",
|
||||||
"version": "1.7.43",
|
"version": "1.7.44",
|
||||||
"description": "Desktop downloader",
|
"description": "Desktop downloader",
|
||||||
"main": "build/main/main/main.js",
|
"main": "build/main/main/main.js",
|
||||||
"author": "Sucukdeluxe",
|
"author": "Sucukdeluxe",
|
||||||
|
|||||||
@ -679,7 +679,7 @@ function prioritizePassword(passwords: string[], successful: string): string[] {
|
|||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
function nativeExtractorCandidates(): string[] {
|
function nativeExtractorCandidates(archivePath = ""): string[] {
|
||||||
const programFiles = process.env.ProgramFiles || "C:\\Program Files";
|
const programFiles = process.env.ProgramFiles || "C:\\Program Files";
|
||||||
const programFilesX86 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
|
const programFilesX86 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
|
||||||
const localAppData = process.env.LOCALAPPDATA || "";
|
const localAppData = process.env.LOCALAPPDATA || "";
|
||||||
@ -694,11 +694,14 @@ function nativeExtractorCandidates(): string[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const winRarInstalled = [
|
const winRarInstalled = [
|
||||||
|
path.join(programFiles, "WinRAR", "WinRAR.exe"),
|
||||||
|
path.join(programFilesX86, "WinRAR", "WinRAR.exe"),
|
||||||
path.join(programFiles, "WinRAR", "UnRAR.exe"),
|
path.join(programFiles, "WinRAR", "UnRAR.exe"),
|
||||||
path.join(programFilesX86, "WinRAR", "UnRAR.exe")
|
path.join(programFilesX86, "WinRAR", "UnRAR.exe")
|
||||||
];
|
];
|
||||||
|
|
||||||
if (localAppData) {
|
if (localAppData) {
|
||||||
|
winRarInstalled.push(path.join(localAppData, "Programs", "WinRAR", "WinRAR.exe"));
|
||||||
winRarInstalled.push(path.join(localAppData, "Programs", "WinRAR", "UnRAR.exe"));
|
winRarInstalled.push(path.join(localAppData, "Programs", "WinRAR", "UnRAR.exe"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -711,6 +714,8 @@ function nativeExtractorCandidates(): string[] {
|
|||||||
"7za.exe",
|
"7za.exe",
|
||||||
"7za",
|
"7za",
|
||||||
...winRarInstalled,
|
...winRarInstalled,
|
||||||
|
"WinRAR.exe",
|
||||||
|
"winrar",
|
||||||
"UnRAR.exe",
|
"UnRAR.exe",
|
||||||
"unrar"
|
"unrar"
|
||||||
]
|
]
|
||||||
@ -721,10 +726,12 @@ function nativeExtractorCandidates(): string[] {
|
|||||||
"7za.exe",
|
"7za.exe",
|
||||||
"7za",
|
"7za",
|
||||||
...winRarInstalled,
|
...winRarInstalled,
|
||||||
|
"WinRAR.exe",
|
||||||
|
"winrar",
|
||||||
"UnRAR.exe",
|
"UnRAR.exe",
|
||||||
"unrar"
|
"unrar"
|
||||||
];
|
];
|
||||||
return Array.from(new Set(ordered.filter(Boolean)));
|
return orderExtractorCandidatesForArchive(ordered, archivePath, resolvedExtractorCommand || "");
|
||||||
}
|
}
|
||||||
|
|
||||||
function isAbsoluteCommand(command: string): boolean {
|
function isAbsoluteCommand(command: string): boolean {
|
||||||
@ -1786,7 +1793,7 @@ export function buildExternalExtractArgs(
|
|||||||
return ["x", "-y", overwrite, pass, archivePath, `-o${targetDir}`];
|
return ["x", "-y", overwrite, pass, archivePath, `-o${targetDir}`];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resolveExtractorCommandInternal(): Promise<string> {
|
async function resolveExtractorCommandInternal(archivePath = ""): Promise<string> {
|
||||||
if (resolvedExtractorCommand) {
|
if (resolvedExtractorCommand) {
|
||||||
return resolvedExtractorCommand;
|
return resolvedExtractorCommand;
|
||||||
}
|
}
|
||||||
@ -1799,7 +1806,7 @@ async function resolveExtractorCommandInternal(): Promise<string> {
|
|||||||
resolveFailureAt = 0;
|
resolveFailureAt = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const candidates = nativeExtractorCandidates();
|
const candidates = nativeExtractorCandidates(archivePath);
|
||||||
for (const command of candidates) {
|
for (const command of candidates) {
|
||||||
if (isAbsoluteCommand(command) && !fs.existsSync(command)) {
|
if (isAbsoluteCommand(command) && !fs.existsSync(command)) {
|
||||||
continue;
|
continue;
|
||||||
@ -1821,15 +1828,15 @@ async function resolveExtractorCommandInternal(): Promise<string> {
|
|||||||
throw new Error(resolveFailureReason);
|
throw new Error(resolveFailureReason);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resolveExtractorCommand(): Promise<string> {
|
async function resolveExtractorCommand(archivePath = ""): Promise<string> {
|
||||||
if (resolvedExtractorCommand) {
|
if (resolvedExtractorCommand && cachedExtractorFitsArchive(resolvedExtractorCommand, archivePath)) {
|
||||||
return resolvedExtractorCommand;
|
return resolvedExtractorCommand;
|
||||||
}
|
}
|
||||||
if (resolveExtractorCommandInFlight) {
|
if (resolveExtractorCommandInFlight) {
|
||||||
return resolveExtractorCommandInFlight;
|
return resolveExtractorCommandInFlight;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pending = resolveExtractorCommandInternal();
|
const pending = resolveExtractorCommandInternal(archivePath);
|
||||||
resolveExtractorCommandInFlight = pending;
|
resolveExtractorCommandInFlight = pending;
|
||||||
try {
|
try {
|
||||||
return await pending;
|
return await pending;
|
||||||
@ -1850,8 +1857,71 @@ function isUnrarCommand(command: string): boolean {
|
|||||||
return lower.includes("unrar") || lower.includes("winrar");
|
return lower.includes("unrar") || lower.includes("winrar");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findAlternativeExtractor(currentCommand: string): Promise<string | null> {
|
type ExtractorCommandKind = "rar_native" | "seven_zip" | "other";
|
||||||
const candidates = nativeExtractorCandidates();
|
|
||||||
|
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);
|
const currentIs7z = is7zCommand(currentCommand);
|
||||||
for (const candidate of candidates) {
|
for (const candidate of candidates) {
|
||||||
if (candidate === currentCommand) continue;
|
if (candidate === currentCommand) continue;
|
||||||
@ -1971,7 +2041,7 @@ async function runExternalExtract(
|
|||||||
subst = createSubstMapping(targetDir);
|
subst = createSubstMapping(targetDir);
|
||||||
const effectiveTargetDir = subst ? `${subst.drive}:\\` : targetDir;
|
const effectiveTargetDir = subst ? `${subst.drive}:\\` : targetDir;
|
||||||
|
|
||||||
const command = await resolveExtractorCommand();
|
const command = await resolveExtractorCommand(archivePath);
|
||||||
const legacyStartedAt = Date.now();
|
const legacyStartedAt = Date.now();
|
||||||
let password: string;
|
let password: string;
|
||||||
let usedCommand = command;
|
let usedCommand = command;
|
||||||
@ -1999,7 +2069,7 @@ async function runExternalExtract(
|
|||||||
const errText = String((primaryError as Error)?.message || primaryError || "");
|
const errText = String((primaryError as Error)?.message || primaryError || "");
|
||||||
const isPasswordOrCorrupt = /wrong.password|checksum error|corrupt/i.test(errText);
|
const isPasswordOrCorrupt = /wrong.password|checksum error|corrupt/i.test(errText);
|
||||||
if (isRar && isPasswordOrCorrupt && !signal?.aborted) {
|
if (isRar && isPasswordOrCorrupt && !signal?.aborted) {
|
||||||
const alt = await findAlternativeExtractor(command);
|
const alt = await findAlternativeExtractor(command, archivePath);
|
||||||
if (alt) {
|
if (alt) {
|
||||||
const altName = path.basename(alt).replace(/\.exe$/i, "");
|
const altName = path.basename(alt).replace(/\.exe$/i, "");
|
||||||
onLog?.("INFO", `Legacy-Fallback: primary=${path.basename(command)}, alternative=${altName}, archive=${archiveName}`);
|
onLog?.("INFO", `Legacy-Fallback: primary=${path.basename(command)}, alternative=${altName}, archive=${archiveName}`);
|
||||||
@ -2121,10 +2191,11 @@ async function runExternalExtractInner(
|
|||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const passwords = passwordCandidates;
|
const passwords = passwordCandidates;
|
||||||
let lastError = "";
|
let lastError = "";
|
||||||
|
const extractorName = path.basename(command).replace(/\.exe$/i, "") || command;
|
||||||
|
|
||||||
const quotedPasswords = passwords.map((p) => p === "" ? '""' : `"${p}"`);
|
const quotedPasswords = passwords.map((p) => p === "" ? '""' : `"${p}"`);
|
||||||
onLog?.("INFO", `Legacy-Extractor Start: archive=${path.basename(archivePath)}, passwordCount=${passwords.length}, forceFlatMode=${forceFlatMode}, targetDir=${targetDir}`);
|
onLog?.("INFO", `Legacy-Extractor Start: archive=${path.basename(archivePath)}, extractor=${extractorName}, passwordCount=${passwords.length}, forceFlatMode=${forceFlatMode}, targetDir=${targetDir}`);
|
||||||
logger.info(`Legacy-Extractor: ${path.basename(archivePath)}, ${passwords.length} Passwörter: [${quotedPasswords.join(", ")}]${forceFlatMode ? " (flat-mode cached)" : ""}`);
|
logger.info(`Legacy-Extractor (${extractorName}): ${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;
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import {
|
|||||||
detectArchiveSignature,
|
detectArchiveSignature,
|
||||||
classifyExtractionError,
|
classifyExtractionError,
|
||||||
findArchiveCandidates,
|
findArchiveCandidates,
|
||||||
|
orderExtractorCandidatesForArchive,
|
||||||
resolveExtractorBackendMode,
|
resolveExtractorBackendMode,
|
||||||
} from "../src/main/extractor";
|
} from "../src/main/extractor";
|
||||||
|
|
||||||
@ -1171,4 +1172,33 @@ describe("extractor", () => {
|
|||||||
expect(resolveExtractorBackendMode("auto", false)).toBe("auto");
|
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