Fix: Provider-Cooldown-Fallback fuehrt die Kette jetzt wirklich mit dem Ersatz-Provider an (Routing-Slice)
Wenn ein Provider in den Manager-Cooldown laeuft (>=20 Fehler in Folge) und auto-Fallback aktiv ist, berechnete der Manager zwar einen Ersatz-Provider (findFallbackProviderNotInCooldown), warf ihn dann aber weg: der unrestrictLink- Aufruf bekam keinen Hint, also baute debrid.ts die Provider-Kette frisch aus providerOrder und fuehrte erneut mit dem ausgebremsten Provider 1 an — ein meist in den 60s-Timeout laufender, verschwendeter Versuch, bevor ueberhaupt der gesunde Provider drankam. Jetzt wird der Ersatz-Provider als bevorzugter Lead durchgereicht. Die Kette wird UMSORTIERT, nicht beschnitten: der ausgebremste Provider bleibt als letzter Notnagel in der Reihenfolge, sodass kein Link gestrandet werden kann. Sind ALLE Provider im Cooldown, erreicht der Aufruf unrestrictLink ohnehin nicht (der else-Zweig queued einen Retry). Greift damit nur, wenn ein Provider bereits nachweislich degradiert ist — strikt besser, wenn aktiv, und ohne Timeout-, Cancel- oder Budget-Vertrag anzufassen. - debrid.ts: reine leadProviderChainWith(order, preferred) (export) + optionaler 4. Param preferredLeadProvider an unrestrictLink; Reorder direkt nach Aufbau der Reihenfolge. - download-manager.ts: Ersatz-Provider in preferredLeadProvider festhalten und an den unrestrictLink-Aufruf durchreichen. - Tests: Integration (ohne Hint fuehrt realdebrid, mit Hint fuehrt debridlink) + drei reine Helper-Tests fuer die No-Stranding-Invariante. Zwei verbleibende HIGH-Punkte sind bewusst NICHT autonom geaendert, sondern als Produkt-/UI-Entscheidung in tasks/entscheidungen-offen.md vorgelegt (globaler 60s-Failover-Timeout; gespiegelter Mega API/Web-Schalter).
This commit is contained in:
+10
-2
@@ -1321,6 +1321,13 @@ function toProviderOrder(primary: DebridProvider, secondary: DebridFallbackProvi
|
||||
return uniqueProviderOrder(order);
|
||||
}
|
||||
|
||||
export function leadProviderChainWith(order: readonly DebridProvider[], preferred: DebridProvider | null | undefined): DebridProvider[] {
|
||||
if (!preferred || !order.includes(preferred)) {
|
||||
return [...order];
|
||||
}
|
||||
return [preferred, ...order.filter((provider) => provider !== preferred)];
|
||||
}
|
||||
|
||||
function isRapidgatorLink(link: string): boolean {
|
||||
try {
|
||||
const hostname = new URL(link).hostname.toLowerCase();
|
||||
@@ -3700,7 +3707,7 @@ export class DebridService {
|
||||
return `${PROVIDER_LABELS[effectiveProvider]} Tageslimit erreicht`;
|
||||
}
|
||||
|
||||
public async unrestrictLink(link: string, signal?: AbortSignal, settingsSnapshot?: AppSettings): Promise<ProviderUnrestrictedLink> {
|
||||
public async unrestrictLink(link: string, signal?: AbortSignal, settingsSnapshot?: AppSettings, preferredLeadProvider?: DebridProvider | null): Promise<ProviderUnrestrictedLink> {
|
||||
const settings = settingsSnapshot ? cloneSettings(settingsSnapshot) : cloneSettings(this.settings);
|
||||
|
||||
const routing = settings.hosterRouting || {};
|
||||
@@ -3777,9 +3784,10 @@ export class DebridService {
|
||||
}
|
||||
}
|
||||
|
||||
const order: DebridProvider[] = (settings.providerOrder && settings.providerOrder.length > 0)
|
||||
const baseOrder: DebridProvider[] = (settings.providerOrder && settings.providerOrder.length > 0)
|
||||
? uniqueProviderOrder(settings.providerOrder)
|
||||
: toProviderOrder(settings.providerPrimary, settings.providerSecondary, settings.providerTertiary);
|
||||
const order = leadProviderChainWith(baseOrder, preferredLeadProvider);
|
||||
|
||||
const primary = order[0];
|
||||
if (!settings.autoProviderFallback) {
|
||||
|
||||
@@ -8771,6 +8771,7 @@ export class DownloadManager extends EventEmitter {
|
||||
}
|
||||
const cooldownProvider = this.getProviderFailureKeyForItem(item);
|
||||
const cooldownMs = this.getProviderCooldownRemaining(cooldownProvider);
|
||||
let preferredLeadProvider: DebridProvider | null = null;
|
||||
if (cooldownMs > 0) {
|
||||
if (this.settings.autoProviderFallback) {
|
||||
const fallback = this.findFallbackProviderNotInCooldown(item);
|
||||
@@ -8782,6 +8783,7 @@ export class DownloadManager extends EventEmitter {
|
||||
fallback
|
||||
});
|
||||
item.provider = null;
|
||||
preferredLeadProvider = fallback;
|
||||
} else {
|
||||
this.logPackageForItem(item, "WARN", "Provider-Cooldown blockiert Unrestrict", {
|
||||
provider: cooldownProvider,
|
||||
@@ -8825,7 +8827,7 @@ export class DownloadManager extends EventEmitter {
|
||||
traceConversionNote("slots", this.describeSlotOccupancy());
|
||||
traceConversionNote("retry", Number(active.unrestrictRetries || 0));
|
||||
try {
|
||||
return await this.debridService.unrestrictLink(item.url, unrestrictedSignal);
|
||||
return await this.debridService.unrestrictLink(item.url, unrestrictedSignal, undefined, preferredLeadProvider);
|
||||
} catch (innerError) {
|
||||
if (!active.abortController.signal.aborted && unrestrictTimeoutSignal.aborted) {
|
||||
traceConversionPhase({
|
||||
|
||||
Reference in New Issue
Block a user