Add archive password fallback and release v1.1.28
Some checks are pending
Build and Release / build (push) Waiting to run
Some checks are pending
Build and Release / build (push) Waiting to run
This commit is contained in:
parent
88eb6dff5d
commit
741a0d67cc
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "real-debrid-downloader",
|
"name": "real-debrid-downloader",
|
||||||
"version": "1.1.27",
|
"version": "1.1.28",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "real-debrid-downloader",
|
"name": "real-debrid-downloader",
|
||||||
"version": "1.1.27",
|
"version": "1.1.28",
|
||||||
"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.1.27",
|
"version": "1.1.28",
|
||||||
"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",
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import os from "node:os";
|
|||||||
import { AppSettings } from "../shared/types";
|
import { AppSettings } from "../shared/types";
|
||||||
|
|
||||||
export const APP_NAME = "Debrid Download Manager";
|
export const APP_NAME = "Debrid Download Manager";
|
||||||
export const APP_VERSION = "1.1.27";
|
export const APP_VERSION = "1.1.28";
|
||||||
export const API_BASE_URL = "https://api.real-debrid.com/rest/1.0";
|
export const API_BASE_URL = "https://api.real-debrid.com/rest/1.0";
|
||||||
|
|
||||||
export const DCRYPT_UPLOAD_URL = "https://dcrypt.it/decrypt/upload";
|
export const DCRYPT_UPLOAD_URL = "https://dcrypt.it/decrypt/upload";
|
||||||
|
|||||||
@ -1241,8 +1241,9 @@ export class DownloadManager extends EventEmitter {
|
|||||||
removeSamples: this.settings.removeSamplesAfterExtract
|
removeSamples: this.settings.removeSamplesAfterExtract
|
||||||
});
|
});
|
||||||
if (result.failed > 0) {
|
if (result.failed > 0) {
|
||||||
|
const reason = compactErrorText(result.lastError || "Entpacken fehlgeschlagen");
|
||||||
for (const entry of completedItems) {
|
for (const entry of completedItems) {
|
||||||
entry.fullStatus = "Entpack-Fehler";
|
entry.fullStatus = `Entpack-Fehler: ${reason}`;
|
||||||
entry.updatedAt = nowMs();
|
entry.updatedAt = nowMs();
|
||||||
}
|
}
|
||||||
pkg.status = "failed";
|
pkg.status = "failed";
|
||||||
|
|||||||
@ -6,6 +6,8 @@ import { CleanupMode, ConflictMode } from "../shared/types";
|
|||||||
import { logger } from "./logger";
|
import { logger } from "./logger";
|
||||||
import { removeDownloadLinkArtifacts, removeSampleArtifacts } from "./cleanup";
|
import { removeDownloadLinkArtifacts, removeSampleArtifacts } from "./cleanup";
|
||||||
|
|
||||||
|
const DEFAULT_ARCHIVE_PASSWORDS = ["", "serienfans.org", "serienjunkies.org"];
|
||||||
|
|
||||||
export interface ExtractOptions {
|
export interface ExtractOptions {
|
||||||
packageDir: string;
|
packageDir: string;
|
||||||
targetDir: string;
|
targetDir: string;
|
||||||
@ -39,39 +41,74 @@ function effectiveConflictMode(conflictMode: ConflictMode): "overwrite" | "skip"
|
|||||||
return "skip";
|
return "skip";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildExternalExtractArgs(command: string, archivePath: string, targetDir: string, conflictMode: ConflictMode): string[] {
|
export function buildExternalExtractArgs(
|
||||||
|
command: string,
|
||||||
|
archivePath: string,
|
||||||
|
targetDir: string,
|
||||||
|
conflictMode: ConflictMode,
|
||||||
|
password = ""
|
||||||
|
): string[] {
|
||||||
const mode = effectiveConflictMode(conflictMode);
|
const mode = effectiveConflictMode(conflictMode);
|
||||||
const lower = command.toLowerCase();
|
const lower = command.toLowerCase();
|
||||||
if (lower.includes("unrar")) {
|
if (lower.includes("unrar")) {
|
||||||
const overwrite = mode === "overwrite" ? "-o+" : mode === "rename" ? "-or" : "-o-";
|
const overwrite = mode === "overwrite" ? "-o+" : mode === "rename" ? "-or" : "-o-";
|
||||||
return ["x", overwrite, archivePath, `${targetDir}${path.sep}`];
|
const pass = password ? `-p${password}` : "-p-";
|
||||||
|
return ["x", overwrite, pass, archivePath, `${targetDir}${path.sep}`];
|
||||||
}
|
}
|
||||||
|
|
||||||
const overwrite = mode === "overwrite" ? "-aoa" : mode === "rename" ? "-aou" : "-aos";
|
const overwrite = mode === "overwrite" ? "-aoa" : mode === "rename" ? "-aou" : "-aos";
|
||||||
return ["x", "-y", overwrite, archivePath, `-o${targetDir}`];
|
const pass = password ? `-p${password}` : "-p";
|
||||||
|
return ["x", "-y", overwrite, pass, archivePath, `-o${targetDir}`];
|
||||||
}
|
}
|
||||||
|
|
||||||
function runExternalExtract(archivePath: string, targetDir: string, conflictMode: ConflictMode): Promise<void> {
|
function runExternalExtract(archivePath: string, targetDir: string, conflictMode: ConflictMode): Promise<void> {
|
||||||
const candidates = ["7z", "C:\\Program Files\\7-Zip\\7z.exe", "C:\\Program Files (x86)\\7-Zip\\7z.exe", "unrar"];
|
const candidates = ["7z", "C:\\Program Files\\7-Zip\\7z.exe", "C:\\Program Files (x86)\\7-Zip\\7z.exe", "unrar"];
|
||||||
|
const passwords = Array.from(new Set(DEFAULT_ARCHIVE_PASSWORDS));
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const tryExec = (idx: number): void => {
|
let lastError = "";
|
||||||
if (idx >= candidates.length) {
|
|
||||||
reject(new Error("Kein 7z/unrar gefunden"));
|
const cleanErrorText = (text: string): string => text.replace(/\s+/g, " ").trim().slice(0, 240);
|
||||||
|
|
||||||
|
const tryExec = (cmdIdx: number, passwordIdx: number): void => {
|
||||||
|
if (cmdIdx >= candidates.length) {
|
||||||
|
reject(new Error(lastError || "Kein 7z/unrar gefunden"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const cmd = candidates[idx];
|
fs.mkdirSync(targetDir, { recursive: true });
|
||||||
const args = buildExternalExtractArgs(cmd, archivePath, targetDir, conflictMode);
|
const cmd = candidates[cmdIdx];
|
||||||
|
const password = passwords[passwordIdx] || "";
|
||||||
|
const args = buildExternalExtractArgs(cmd, archivePath, targetDir, conflictMode, password);
|
||||||
const child = spawn(cmd, args, { windowsHide: true });
|
const child = spawn(cmd, args, { windowsHide: true });
|
||||||
child.on("error", () => tryExec(idx + 1));
|
let output = "";
|
||||||
|
child.stdout.on("data", (chunk) => {
|
||||||
|
output += String(chunk || "");
|
||||||
|
});
|
||||||
|
child.stderr.on("data", (chunk) => {
|
||||||
|
output += String(chunk || "");
|
||||||
|
});
|
||||||
|
child.on("error", (error) => {
|
||||||
|
lastError = cleanErrorText(String(error));
|
||||||
|
tryExec(cmdIdx + 1, 0);
|
||||||
|
});
|
||||||
child.on("close", (code) => {
|
child.on("close", (code) => {
|
||||||
if (code === 0 || code === 1) {
|
if (code === 0 || code === 1) {
|
||||||
resolve();
|
resolve();
|
||||||
} else {
|
} else {
|
||||||
tryExec(idx + 1);
|
const cleaned = cleanErrorText(output);
|
||||||
|
if (cleaned) {
|
||||||
|
lastError = cleaned;
|
||||||
|
} else {
|
||||||
|
lastError = `Exit Code ${String(code ?? "?")}`;
|
||||||
|
}
|
||||||
|
if (passwordIdx + 1 < passwords.length) {
|
||||||
|
tryExec(cmdIdx, passwordIdx + 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tryExec(cmdIdx + 1, 0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
tryExec(0);
|
tryExec(0, 0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,20 +156,25 @@ function cleanupArchives(sourceFiles: string[], cleanupMode: CleanupMode): void
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function extractPackageArchives(options: ExtractOptions): Promise<{ extracted: number; failed: number }> {
|
export async function extractPackageArchives(options: ExtractOptions): Promise<{ extracted: number; failed: number; lastError: string }> {
|
||||||
const candidates = findArchiveCandidates(options.packageDir);
|
const candidates = findArchiveCandidates(options.packageDir);
|
||||||
if (candidates.length === 0) {
|
if (candidates.length === 0) {
|
||||||
return { extracted: 0, failed: 0 };
|
return { extracted: 0, failed: 0, lastError: "" };
|
||||||
}
|
}
|
||||||
|
|
||||||
let extracted = 0;
|
let extracted = 0;
|
||||||
let failed = 0;
|
let failed = 0;
|
||||||
|
let lastError = "";
|
||||||
const extractedArchives: string[] = [];
|
const extractedArchives: string[] = [];
|
||||||
for (const archivePath of candidates) {
|
for (const archivePath of candidates) {
|
||||||
try {
|
try {
|
||||||
const ext = path.extname(archivePath).toLowerCase();
|
const ext = path.extname(archivePath).toLowerCase();
|
||||||
if (ext === ".zip") {
|
if (ext === ".zip") {
|
||||||
extractZipArchive(archivePath, options.targetDir, options.conflictMode);
|
try {
|
||||||
|
extractZipArchive(archivePath, options.targetDir, options.conflictMode);
|
||||||
|
} catch {
|
||||||
|
await runExternalExtract(archivePath, options.targetDir, options.conflictMode);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
await runExternalExtract(archivePath, options.targetDir, options.conflictMode);
|
await runExternalExtract(archivePath, options.targetDir, options.conflictMode);
|
||||||
}
|
}
|
||||||
@ -140,7 +182,9 @@ export async function extractPackageArchives(options: ExtractOptions): Promise<{
|
|||||||
extractedArchives.push(archivePath);
|
extractedArchives.push(archivePath);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
failed += 1;
|
failed += 1;
|
||||||
logger.error(`Entpack-Fehler ${path.basename(archivePath)}: ${String(error)}`);
|
const errorText = String(error);
|
||||||
|
lastError = errorText;
|
||||||
|
logger.error(`Entpack-Fehler ${path.basename(archivePath)}: ${errorText}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,5 +206,5 @@ export async function extractPackageArchives(options: ExtractOptions): Promise<{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { extracted, failed };
|
return { extracted, failed, lastError };
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,7 @@ describe("extractor", () => {
|
|||||||
"x",
|
"x",
|
||||||
"-y",
|
"-y",
|
||||||
"-aoa",
|
"-aoa",
|
||||||
|
"-p",
|
||||||
"archive.rar",
|
"archive.rar",
|
||||||
"-oC:\\target"
|
"-oC:\\target"
|
||||||
]);
|
]);
|
||||||
@ -26,6 +27,15 @@ describe("extractor", () => {
|
|||||||
"x",
|
"x",
|
||||||
"-y",
|
"-y",
|
||||||
"-aos",
|
"-aos",
|
||||||
|
"-p",
|
||||||
|
"archive.rar",
|
||||||
|
"-oC:\\target"
|
||||||
|
]);
|
||||||
|
expect(buildExternalExtractArgs("7z", "archive.rar", "C:\\target", "ask", "serienfans.org")).toEqual([
|
||||||
|
"x",
|
||||||
|
"-y",
|
||||||
|
"-aos",
|
||||||
|
"-pserienfans.org",
|
||||||
"archive.rar",
|
"archive.rar",
|
||||||
"-oC:\\target"
|
"-oC:\\target"
|
||||||
]);
|
]);
|
||||||
@ -33,7 +43,8 @@ describe("extractor", () => {
|
|||||||
const unrarRename = buildExternalExtractArgs("unrar", "archive.rar", "C:\\target", "rename");
|
const unrarRename = buildExternalExtractArgs("unrar", "archive.rar", "C:\\target", "rename");
|
||||||
expect(unrarRename[0]).toBe("x");
|
expect(unrarRename[0]).toBe("x");
|
||||||
expect(unrarRename[1]).toBe("-or");
|
expect(unrarRename[1]).toBe("-or");
|
||||||
expect(unrarRename[2]).toBe("archive.rar");
|
expect(unrarRename[2]).toBe("-p-");
|
||||||
|
expect(unrarRename[3]).toBe("archive.rar");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("deletes only successfully extracted archives", async () => {
|
it("deletes only successfully extracted archives", async () => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user