Fix extraction failure on long paths (>260 chars) with \?\ prefix
Some checks are pending
Build and Release / build (push) Waiting to run
Some checks are pending
Build and Release / build (push) Waiting to run
Add longPathForWindows() helper that prefixes extract target directories with \?\ on Windows, bypassing the 260-char MAX_PATH limit. Applied to both WinRAR/UnRAR and 7z arguments. Fixes "Die Syntax für den Dateinamen, Verzeichnisnamen" errors when archive internal directories create deeply nested output paths. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a22a90adf3
commit
2ae22f942e
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "real-debrid-downloader",
|
"name": "real-debrid-downloader",
|
||||||
"version": "1.5.15",
|
"version": "1.5.16",
|
||||||
"description": "Real-Debrid Downloader Desktop (Electron + React + TypeScript)",
|
"description": "Real-Debrid Downloader Desktop (Electron + React + TypeScript)",
|
||||||
"main": "build/main/main/main.js",
|
"main": "build/main/main/main.js",
|
||||||
"author": "Sucukdeluxe",
|
"author": "Sucukdeluxe",
|
||||||
|
|||||||
@ -8,6 +8,21 @@ import { logger } from "./logger";
|
|||||||
import { removeDownloadLinkArtifacts, removeSampleArtifacts } from "./cleanup";
|
import { removeDownloadLinkArtifacts, removeSampleArtifacts } from "./cleanup";
|
||||||
|
|
||||||
const DEFAULT_ARCHIVE_PASSWORDS = ["", "serienfans.org", "serienjunkies.org"];
|
const DEFAULT_ARCHIVE_PASSWORDS = ["", "serienfans.org", "serienjunkies.org"];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On Windows, prefix an absolute path with \\?\ to bypass the 260-char MAX_PATH limit.
|
||||||
|
* WinRAR 5.x+ and 7-Zip support this prefix for long output paths.
|
||||||
|
*/
|
||||||
|
function longPathForWindows(p: string): string {
|
||||||
|
if (process.platform !== "win32") return p;
|
||||||
|
const resolved = path.resolve(p);
|
||||||
|
if (resolved.startsWith("\\\\?\\")) return resolved;
|
||||||
|
if (resolved.startsWith("\\\\")) {
|
||||||
|
// UNC path \\server\share -> \\?\UNC\server\share
|
||||||
|
return "\\\\?\\UNC\\" + resolved.slice(2);
|
||||||
|
}
|
||||||
|
return "\\\\?\\" + resolved;
|
||||||
|
}
|
||||||
const NO_EXTRACTOR_MESSAGE = "WinRAR/UnRAR nicht gefunden. Bitte WinRAR installieren.";
|
const NO_EXTRACTOR_MESSAGE = "WinRAR/UnRAR nicht gefunden. Bitte WinRAR installieren.";
|
||||||
|
|
||||||
let resolvedExtractorCommand: string | null = null;
|
let resolvedExtractorCommand: string | null = null;
|
||||||
@ -606,13 +621,14 @@ export function buildExternalExtractArgs(
|
|||||||
const perfArgs = usePerformanceFlags && shouldUseExtractorPerformanceFlags()
|
const perfArgs = usePerformanceFlags && shouldUseExtractorPerformanceFlags()
|
||||||
? ["-idc", extractorThreadSwitch()]
|
? ["-idc", extractorThreadSwitch()]
|
||||||
: [];
|
: [];
|
||||||
return ["x", overwrite, pass, "-y", ...perfArgs, archivePath, `${targetDir}${path.sep}`];
|
const longTarget = longPathForWindows(targetDir);
|
||||||
|
return ["x", overwrite, pass, "-y", ...perfArgs, archivePath, `${longTarget}${path.sep}`];
|
||||||
}
|
}
|
||||||
|
|
||||||
const overwrite = mode === "overwrite" ? "-aoa" : mode === "rename" ? "-aou" : "-aos";
|
const overwrite = mode === "overwrite" ? "-aoa" : mode === "rename" ? "-aou" : "-aos";
|
||||||
// NOTE: Same password-in-args limitation as above applies to 7z as well.
|
// NOTE: Same password-in-args limitation as above applies to 7z as well.
|
||||||
const pass = password ? `-p${password}` : "-p";
|
const pass = password ? `-p${password}` : "-p";
|
||||||
return ["x", "-y", overwrite, pass, archivePath, `-o${targetDir}`];
|
return ["x", "-y", overwrite, pass, archivePath, `-o${longPathForWindows(targetDir)}`];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resolveExtractorCommandInternal(): Promise<string> {
|
async function resolveExtractorCommandInternal(): Promise<string> {
|
||||||
|
|||||||
@ -20,14 +20,15 @@ describe("extractor", () => {
|
|||||||
expect(overwriteArgs).toContain("-idc");
|
expect(overwriteArgs).toContain("-idc");
|
||||||
expect(overwriteArgs.some((value) => /^-mt\d+$/i.test(value))).toBe(true);
|
expect(overwriteArgs.some((value) => /^-mt\d+$/i.test(value))).toBe(true);
|
||||||
expect(overwriteArgs[overwriteArgs.length - 2]).toBe("archive.rar");
|
expect(overwriteArgs[overwriteArgs.length - 2]).toBe("archive.rar");
|
||||||
expect(overwriteArgs[overwriteArgs.length - 1]).toBe("C:\\target\\");
|
const winrarTarget = process.platform === "win32" ? "\\\\?\\C:\\target\\" : "C:\\target\\";
|
||||||
|
expect(overwriteArgs[overwriteArgs.length - 1]).toBe(winrarTarget);
|
||||||
|
|
||||||
const askArgs = buildExternalExtractArgs("WinRAR.exe", "archive.rar", "C:\\target", "ask", "serienfans.org");
|
const askArgs = buildExternalExtractArgs("WinRAR.exe", "archive.rar", "C:\\target", "ask", "serienfans.org");
|
||||||
expect(askArgs.slice(0, 4)).toEqual(["x", "-o-", "-pserienfans.org", "-y"]);
|
expect(askArgs.slice(0, 4)).toEqual(["x", "-o-", "-pserienfans.org", "-y"]);
|
||||||
expect(askArgs).toContain("-idc");
|
expect(askArgs).toContain("-idc");
|
||||||
expect(askArgs.some((value) => /^-mt\d+$/i.test(value))).toBe(true);
|
expect(askArgs.some((value) => /^-mt\d+$/i.test(value))).toBe(true);
|
||||||
expect(askArgs[askArgs.length - 2]).toBe("archive.rar");
|
expect(askArgs[askArgs.length - 2]).toBe("archive.rar");
|
||||||
expect(askArgs[askArgs.length - 1]).toBe("C:\\target\\");
|
expect(askArgs[askArgs.length - 1]).toBe(winrarTarget);
|
||||||
|
|
||||||
const compatibilityArgs = buildExternalExtractArgs("WinRAR.exe", "archive.rar", "C:\\target", "overwrite", "", false);
|
const compatibilityArgs = buildExternalExtractArgs("WinRAR.exe", "archive.rar", "C:\\target", "overwrite", "", false);
|
||||||
expect(compatibilityArgs).not.toContain("-idc");
|
expect(compatibilityArgs).not.toContain("-idc");
|
||||||
@ -455,7 +456,8 @@ describe("extractor", () => {
|
|||||||
expect(args7z).toContain("-aoa");
|
expect(args7z).toContain("-aoa");
|
||||||
expect(args7z).toContain("-p");
|
expect(args7z).toContain("-p");
|
||||||
expect(args7z).toContain("archive.7z");
|
expect(args7z).toContain("archive.7z");
|
||||||
expect(args7z).toContain("-oC:\\target");
|
const sevenZTarget = process.platform === "win32" ? "-o\\\\?\\C:\\target" : "-oC:\\target";
|
||||||
|
expect(args7z).toContain(sevenZTarget);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("builds 7z args with skip conflict mode", () => {
|
it("builds 7z args with skip conflict mode", () => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user