Release v1.6.20

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sucukdeluxe 2026-03-04 18:09:13 +01:00
parent b8bbc9c32f
commit 729aa30253
4 changed files with 31 additions and 14 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "real-debrid-downloader", "name": "real-debrid-downloader",
"version": "1.6.19", "version": "1.6.20",
"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",

View File

@ -4191,7 +4191,11 @@ export class DownloadManager extends EventEmitter {
return; return;
} }
// Only increment when not already inside an active reconnect window to avoid
// inflating the backoff counter when multiple parallel downloads hit 429/503.
if (this.session.reconnectUntil <= nowMs()) {
this.consecutiveReconnects += 1; this.consecutiveReconnects += 1;
}
const backoffMultiplier = Math.min(this.consecutiveReconnects, 5); const backoffMultiplier = Math.min(this.consecutiveReconnects, 5);
const waitMs = this.settings.reconnectWaitSeconds * 1000 * backoffMultiplier; const waitMs = this.settings.reconnectWaitSeconds * 1000 * backoffMultiplier;
const maxWaitMs = this.settings.reconnectWaitSeconds * 2 * 1000; const maxWaitMs = this.settings.reconnectWaitSeconds * 2 * 1000;
@ -5037,10 +5041,6 @@ export class DownloadManager extends EventEmitter {
if (responseText && responseText !== "Unbekannter Fehler" && !/(^|\b)http\s*\d{3}\b/i.test(responseText)) { if (responseText && responseText !== "Unbekannter Fehler" && !/(^|\b)http\s*\d{3}\b/i.test(responseText)) {
lastError = `HTTP ${response.status}: ${responseText}`; lastError = `HTTP ${response.status}: ${responseText}`;
} }
if (this.settings.autoReconnect && [429, 503].includes(response.status)) {
this.requestReconnect(`HTTP ${response.status}`);
throw new Error(lastError);
}
if (attempt < maxAttempts) { if (attempt < maxAttempts) {
item.retries += 1; item.retries += 1;
item.fullStatus = `Serverfehler ${response.status}, retry ${attempt}/${retryDisplayLimit}`; item.fullStatus = `Serverfehler ${response.status}, retry ${attempt}/${retryDisplayLimit}`;
@ -5048,6 +5048,10 @@ export class DownloadManager extends EventEmitter {
await sleep(retryDelayWithJitter(attempt, 250)); await sleep(retryDelayWithJitter(attempt, 250));
continue; continue;
} }
if (this.settings.autoReconnect && [429, 503].includes(response.status)) {
this.requestReconnect(`HTTP ${response.status}`);
throw new Error(lastError);
}
throw new Error(lastError); throw new Error(lastError);
} }

View File

@ -466,7 +466,7 @@ export function classifyExtractionError(errorText: string): ExtractErrorCategory
function isExtractAbortError(errorText: string): boolean { function isExtractAbortError(errorText: string): boolean {
const text = String(errorText || "").toLowerCase(); const text = String(errorText || "").toLowerCase();
return text.includes("aborted:extract") || text.includes("extract_aborted"); return text.includes("aborted:extract") || text.includes("extract_aborted") || text.includes("noextractor:skipped");
} }
export function archiveFilenamePasswords(archiveName: string): string[] { export function archiveFilenamePasswords(archiveName: string): string[] {
@ -872,11 +872,18 @@ function resolveJvmExtractorRootCandidates(): string[] {
} }
let cachedJvmLayout: JvmExtractorLayout | null | undefined; let cachedJvmLayout: JvmExtractorLayout | null | undefined;
let cachedJvmLayoutNullSince = 0;
const JVM_LAYOUT_NULL_TTL_MS = 5 * 60 * 1000;
function resolveJvmExtractorLayout(): JvmExtractorLayout | null { function resolveJvmExtractorLayout(): JvmExtractorLayout | null {
if (cachedJvmLayout !== undefined) { if (cachedJvmLayout !== undefined) {
// Don't cache null permanently — retry after TTL in case Java was installed
if (cachedJvmLayout === null && Date.now() - cachedJvmLayoutNullSince > JVM_LAYOUT_NULL_TTL_MS) {
cachedJvmLayout = undefined;
} else {
return cachedJvmLayout; return cachedJvmLayout;
} }
}
const javaCandidates = resolveJavaCommandCandidates(); const javaCandidates = resolveJavaCommandCandidates();
const javaCommand = javaCandidates.find((candidate) => { const javaCommand = javaCandidates.find((candidate) => {
if (!candidate) { if (!candidate) {
@ -908,6 +915,7 @@ function resolveJvmExtractorLayout(): JvmExtractorLayout | null {
} }
cachedJvmLayout = null; cachedJvmLayout = null;
cachedJvmLayoutNullSince = Date.now();
return null; return null;
} }
@ -1958,9 +1966,12 @@ export async function extractPackageArchives(options: ExtractOptions): Promise<{
let noExtractorEncountered = false; let noExtractorEncountered = false;
const extractSingleArchive = async (archivePath: string): Promise<void> => { const extractSingleArchive = async (archivePath: string): Promise<void> => {
if (options.signal?.aborted || noExtractorEncountered) { if (options.signal?.aborted) {
throw new Error("aborted:extract"); throw new Error("aborted:extract");
} }
if (noExtractorEncountered) {
throw new Error("noextractor:skipped");
}
const archiveName = path.basename(archivePath); const archiveName = path.basename(archivePath);
const archiveResumeKey = archiveNameKey(archiveName); const archiveResumeKey = archiveNameKey(archiveName);
const archiveStartedAt = Date.now(); const archiveStartedAt = Date.now();
@ -2057,11 +2068,11 @@ export async function extractPackageArchives(options: ExtractOptions): Promise<{
emitProgress(extracted + failed, archiveName, "extracting", archivePercent, Date.now() - archiveStartedAt); emitProgress(extracted + failed, archiveName, "extracting", archivePercent, Date.now() - archiveStartedAt);
} }
} catch (error) { } catch (error) {
failed += 1;
const errorText = String(error); const errorText = String(error);
if (isExtractAbortError(errorText)) { if (isExtractAbortError(errorText)) {
throw error; throw error;
} }
failed += 1;
lastError = errorText; lastError = errorText;
const errorCategory = classifyExtractionError(errorText); const errorCategory = classifyExtractionError(errorText);
logger.error(`Entpack-Fehler ${path.basename(archivePath)} [${errorCategory}]: ${errorText}`); logger.error(`Entpack-Fehler ${path.basename(archivePath)} [${errorCategory}]: ${errorText}`);

View File

@ -97,6 +97,7 @@ const providerLabels: Record<DebridProvider, string> = {
}; };
function formatDateTime(ts: number): string { function formatDateTime(ts: number): string {
if (!ts) return "";
const d = new Date(ts); const d = new Date(ts);
const dd = String(d.getDate()).padStart(2, "0"); const dd = String(d.getDate()).padStart(2, "0");
const mm = String(d.getMonth() + 1).padStart(2, "0"); const mm = String(d.getMonth() + 1).padStart(2, "0");
@ -1163,7 +1164,7 @@ export function App(): ReactElement {
const active = collectorTabsRef.current.find((t) => t.id === activeId) ?? collectorTabsRef.current[0]; const active = collectorTabsRef.current.find((t) => t.id === activeId) ?? collectorTabsRef.current[0];
const rawText = active?.text ?? ""; const rawText = active?.text ?? "";
const persisted = await persistDraftSettings(); const persisted = await persistDraftSettings();
const existingIds = new Set(Object.keys(snapshot.session.packages)); const existingIds = new Set(Object.keys(snapshotRef.current.session.packages));
const result = await window.rd.addLinks({ rawText, packageName: persisted.packageName }); const result = await window.rd.addLinks({ rawText, packageName: persisted.packageName });
if (result.addedLinks > 0) { if (result.addedLinks > 0) {
showToast(`${result.addedPackages} Paket(e), ${result.addedLinks} Link(s) hinzugefügt`); showToast(`${result.addedPackages} Paket(e), ${result.addedLinks} Link(s) hinzugefügt`);
@ -1182,7 +1183,7 @@ export function App(): ReactElement {
const files = await window.rd.pickContainers(); const files = await window.rd.pickContainers();
if (files.length === 0) { return; } if (files.length === 0) { return; }
await persistDraftSettings(); await persistDraftSettings();
const existingIds = new Set(Object.keys(snapshot.session.packages)); const existingIds = new Set(Object.keys(snapshotRef.current.session.packages));
const result = await window.rd.addContainers(files); const result = await window.rd.addContainers(files);
if (result.addedLinks > 0) { if (result.addedLinks > 0) {
showToast(`DLC importiert: ${result.addedPackages} Paket(e), ${result.addedLinks} Link(s)`); showToast(`DLC importiert: ${result.addedPackages} Paket(e), ${result.addedLinks} Link(s)`);
@ -1209,7 +1210,7 @@ export function App(): ReactElement {
if (dlc.length > 0) { if (dlc.length > 0) {
await performQuickAction(async () => { await performQuickAction(async () => {
await persistDraftSettings(); await persistDraftSettings();
const existingIds = new Set(Object.keys(snapshot.session.packages)); const existingIds = new Set(Object.keys(snapshotRef.current.session.packages));
const result = await window.rd.addContainers(dlc); const result = await window.rd.addContainers(dlc);
if (result.addedLinks > 0) { if (result.addedLinks > 0) {
showToast(`Drag-and-Drop: ${result.addedPackages} Paket(e), ${result.addedLinks} Link(s)`); showToast(`Drag-and-Drop: ${result.addedPackages} Paket(e), ${result.addedLinks} Link(s)`);
@ -3408,7 +3409,8 @@ const PackageCard = memo(function PackageCard({ pkg, items, packageSpeed, isFirs
|| a.speedBps !== b.speedBps || a.speedBps !== b.speedBps
|| a.retries !== b.retries || a.retries !== b.retries
|| a.provider !== b.provider || a.provider !== b.provider
|| a.fullStatus !== b.fullStatus) { || a.fullStatus !== b.fullStatus
|| a.onlineStatus !== b.onlineStatus) {
return false; return false;
} }
} }