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).
71 lines
3.8 KiB
Markdown
71 lines
3.8 KiB
Markdown
# Offene Entscheidungen für dich (Audit-Loop 2026-06-17)
|
||
|
||
Diese zwei Punkte habe ich BEWUSST nicht autonom „gefixt", weil jede Lösung
|
||
einen Produkt-/Geschmacks-Kompromiss enthält, den du entscheiden solltest, nicht ich.
|
||
Beide sind verifiziert (Code-Zitat + Szenario), nur die Richtung ist deine Wahl.
|
||
|
||
---
|
||
|
||
## 1. Failover-Kappung durch globalen 60-Sekunden-Timeout (HIGH)
|
||
|
||
**Was passiert (belegt):**
|
||
`download-manager.ts` baut EINEN Timeout fürs gesamte Unrestrict:
|
||
```
|
||
const unrestrictTimeoutSignal = AbortSignal.timeout(getUnrestrictTimeoutMs()); // 60s
|
||
const unrestrictedSignal = AbortSignal.any([active.abortController.signal, unrestrictTimeoutSignal]);
|
||
```
|
||
Dieses EINE Signal geht an die komplette Provider-Kette in `debrid.ts`. Die 60s sind
|
||
also ein Budget für ALLE Provider zusammen, nicht pro Provider. Wenn Provider 1
|
||
(z.B. Mega-Web mit Account-Queue) das Budget verbraucht, dann sieht Provider 2 ein
|
||
bereits abgelaufenes Signal → `debrid.ts` wertet den Abbruch als „kein Failover" und
|
||
wirft, ohne Provider 2 echt zu versuchen.
|
||
|
||
**Aktuell weitgehend latent:** Seit v1.7.214 wird die API zuerst probiert (schneller Pfad),
|
||
Mega-Web ist nur noch Fallback. Das Szenario „langsamer Provider 1 hungert Provider 2 aus"
|
||
trifft in der Produktion derzeit selten. Deshalb dokumentiert statt dringend gefixt.
|
||
|
||
**Deine Entscheidung — zwei Richtungen (gleiche Spannung, andere Seite):**
|
||
|
||
- **A) Pro-Provider-Timeout:** Jeder Provider bekommt sein eigenes frisches 60s-Budget.
|
||
Failover bekommt IMMER einen echten Versuch.
|
||
*Kosten:* Worst-Case-Wartezeit pro Item steigt (3 Provider × 60s = bis zu 180s, bevor ein
|
||
Item aufgibt). Du akzeptierst langsameres Worst-Case-pro-Item für vollständigeres Failover.
|
||
|
||
- **B) Globales Budget behalten, aber Slice für Failover reservieren:** Provider 1 wird auf
|
||
z.B. 35s gedeckelt, damit garantiert Zeit für Provider 2 bleibt.
|
||
*Kosten:* Ein legitim langsamer Provider 1 (Mega-Web-Account-Queue bis ~90s) wird früher
|
||
abgeschnitten → mehr Failover, auch wenn Provider 1 noch erfolgreich gewesen wäre.
|
||
|
||
Kernfrage, die nur du beantworten kannst: **Wie lange darf ein Item bei einem langsamen
|
||
aber funktionierenden Provider hängen, bevor wir ihn zugunsten des nächsten aufgeben?**
|
||
|
||
---
|
||
|
||
## 2. Gespiegelter Mega API/Web-Schalter (HIGH, in Runde 3 als 2/3 bestätigt)
|
||
|
||
**Was passiert (belegt):** Die Mega-Debrid API- und Web-Account-Zeilen teilen sich EINE
|
||
login-only Enable-Flag (`hasMegaDebridCredentials` gilt für beide; die Pro-Modus-Auswahl
|
||
läuft über `isMegaDebridModeEnabled(settings, "api"|"web")`). Dein Report: „API ausschalten
|
||
schaltet Web an" — weil der Schalter sich spiegelt.
|
||
|
||
**Warum ich es NICHT autonom geändert habe:**
|
||
- Daten-Modell-Fix (echte unabhängige Flags) braucht eine Settings-Migration, die
|
||
deaktivierte Accounts versehentlich re-aktivieren könnte.
|
||
- Die saubere UI-Variante (zwei echte unabhängige Schalter) ist ein Layout-Redesign — und
|
||
du bist UI-Geschmack-sensibel; das will ich nicht ungefragt umbauen.
|
||
|
||
**Deine Entscheidung:** Ein gemeinsamer Schalter (API+Web zusammen an/aus, klar beschriftet)
|
||
ODER zwei unabhängige Pro-Modus-Schalter (mehr Kontrolle, aber UI + Migration nötig)?
|
||
|
||
---
|
||
|
||
## Erledigt in dieser Runde (zur Info, kein Handlungsbedarf)
|
||
|
||
- **MED Failover-Routing:** Wenn ein Provider in den Manager-Cooldown läuft (≥20 Fehler in
|
||
Folge) und auto-Fallback an ist, hat der Manager bisher zwar einen Ersatz-Provider berechnet,
|
||
ihn aber WEGGEWORFEN — die Kette führte trotzdem wieder mit dem ausgebremsten Provider an.
|
||
Jetzt wird der Ersatz-Provider als „Lead" durchgereicht und die Kette führt mit ihm an, OHNE
|
||
einen Provider zu verlieren (der ausgebremste bleibt als letzter Notnagel in der Kette).
|
||
Greift nur, wenn der Provider bereits nachweislich degradiert ist → strikt-besser-wenn-aktiv,
|
||
kein Timeout/Cancel-Vertrag berührt. Rot-bewiesener Test. (Hält für Roll-up-Release bereit.)
|