From ccf4dc2e08ccdd5c3388457ef6429e05edb82b83 Mon Sep 17 00:00:00 2001 From: Sucukdeluxe Date: Sat, 7 Mar 2026 17:14:38 +0100 Subject: [PATCH] Fix hosterNotAvailable skipping provider cooldown in inner catch The inner unrestrict error handler still called recordProviderFailure() for hosterNotAvailable errors, causing provider-level cooldown escalation (up to 180s) even though the issue is hoster-side, not provider-side. This made auto-retry stall while manual reset worked instantly. Co-Authored-By: Claude Opus 4.6 --- src/main/download-manager.ts | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/main/download-manager.ts b/src/main/download-manager.ts index 97353cb..3a10dc6 100644 --- a/src/main/download-manager.ts +++ b/src/main/download-manager.ts @@ -336,6 +336,10 @@ function isProviderBusyUnrestrictError(errorText: string): boolean { || text.includes("zu viele downloads"); } +function isHosterUnavailableError(errorText: string): boolean { + return String(errorText || "").toLowerCase().includes("hosternotavailable"); +} + function isTemporaryUnrestrictError(errorText: string): boolean { const text = String(errorText || "").toLowerCase(); return text.includes("server error") @@ -5290,7 +5294,7 @@ export class DownloadManager extends EventEmitter { } // Record failure for the provider that errored const errText = compactErrorText(unrestrictError); - if (isUnrestrictFailure(errText)) { + if (isUnrestrictFailure(errText) && !isHosterUnavailableError(errText)) { this.recordProviderFailure(cooldownProvider); if (isProviderBusyUnrestrictError(errText) || isTemporaryUnrestrictError(errText)) { const busyCooldownMs = isTemporaryUnrestrictError(errText) @@ -5655,6 +5659,33 @@ export class DownloadManager extends EventEmitter { return; } + // hosterNotAvailable: hoster issue, not provider issue — reset provider + // and retry quickly with fresh provider selection (like manual reset) + if (isHosterUnavailableError(errorText) && active.unrestrictRetries < maxUnrestrictRetries) { + active.unrestrictRetries += 1; + item.retries += 1; + item.provider = null; // fresh provider selection on next attempt + // Cap backoff at 30s — hoster issues often resolve quickly + const hosterDelayMs = Math.min(30000, Math.floor(5000 * Math.pow(1.5, Math.min(active.unrestrictRetries - 1, 5)))); + logger.warn(`Hoster nicht verfügbar: item=${item.fileName || item.id}, retry=${active.unrestrictRetries}/${retryDisplayLimit}, delay=${hosterDelayMs}ms, link=${item.url.slice(0, 80)}`); + if (item.downloadedBytes > 0) { + const targetFile = this.claimedTargetPathByItem.get(item.id) || ""; + if (targetFile) { + try { fs.rmSync(targetFile, { force: true }); } catch { /* ignore */ } + } + this.releaseTargetPath(item.id); + item.downloadedBytes = 0; + item.progressPercent = 0; + item.totalBytes = null; + this.dropItemContribution(item.id); + } + this.queueRetry(item, active, hosterDelayMs, `Hoster nicht verfügbar, Retry ${active.unrestrictRetries}/${retryDisplayLimit} (${Math.ceil(hosterDelayMs / 1000)}s)`); + item.lastError = errorText; + this.persistSoon(); + this.emitState(); + return; + } + if (isUnrestrictFailure(errorText) && active.unrestrictRetries < maxUnrestrictRetries) { active.unrestrictRetries += 1; item.retries += 1;