RANGE-1 (HIGH, adversarisch 3/3): Wenn ein Debrid-Server beim Socket-Abbruch einen Garbage-Block in den Body schreibt UND das im LETZTEN inneren Versuch passiert, wurde der Tail nicht zurueckgespult: der `attempt < maxAttempts`-Guard verhindert das Vormerken, resumeRewindBytesNextAttempt ist funktions-lokal und ueberlebt den downloadToFile-Re-Entry nicht, und der Outer-Handler nimmt fuer terminated-class den generic-retry-Zweig (loescht die Teil-Datei NICHT). Beim Fresh-Link-Resume wurden die echten Bytes NACH dem Garbage angehaengt → eine Datei mit exakt korrekter Laenge, die die reine Laengen-Pruefung (validateDownloadedFileCompletion) besteht; bei manifestlosen .mkv/.mp4 ohne Pruefsumme fiel die Korruption nie auf. Fix: am Exhaustion-Punkt vor dem Throw den verdaechtigen Tail zurueckspulen (truncate auf written - RESUME_REWIND_BYTES, downloadedBytes ZUERST gesetzt, damit der binary-Re-Entry-prealloc-reconcile auch bei fehlgeschlagenem truncate greift). GEGATED auf bekannte Groesse (totalBytes != null && > 0): dann ist rewound size < totalBytes = minBytes, sodass tryFinalizeItemFromDisk die gekuerzte Datei ablehnt und der Re-Entry den Garbage sauber ueberschreibt. Ehrliche Einordnung: behoben fuer Medien mit bekannter Groesse (Debrid liefert fast immer fileSize). Der seltene Fall OHNE Groessenangabe behaelt eine separate, schon vorher bestehende stille Korruption unter dem dokumentierten Size-only-Validation-Blindspot — durch dieses Rueckspulen nicht loesbar (kein Laengensignal; nur ein Hard-Reset wuerde helfen, groesserer separater Eingriff). Bewusst NICHT angefasst, um keinen vierten Bug einzubauen. REWIND-TRUNCATE-FAIL (MED, 3/3): Das `finally` setzte resumeRewindBytesNextAttempt=0 unbedingt — auch wenn die Rewind-truncate warf (transienter win32-EBUSY/AV-Lock) → Garbage-Tail blieb, Flag gecleart, nie erneut versucht. Fix: Reset nur noch im Success-Branch, sodass ein fehlgeschlagenes Rueckspulen im naechsten Versuch erneut probiert wird. PREALLOC-ZEROS-ACCEPTED (MED, schwaechste) dokumentiert nicht gefixt: dominante Medien/Archive sind immun (footprint-threshold 0); narrow non-binary-Conjunction. Advisor vor Implementierung konsultiert (HIGH-Hot-Path); Check A (totalBytes-null finalisiert die gekuerzte Datei) am echten Code verifiziert und der Fix entsprechend auf known-total gegated. Rot-bewiesen: Cross-Call-Integrationstest (final-attempt Garbage-Inject + Drop → Exhaustion → queueRetry → 2. downloadToFile via Fresh-Link → byte-genaue Inhaltsgleichheit). Ohne den Fix ist die Laengen-Assertion gruen und die Inhalts-Assertion rot (exakt-laengen-Korruption) — genau die RANGE-1-Signatur. Volle Suite 885 gruen, tsc unveraendert (6).
118 lines
7.5 KiB
Markdown
118 lines
7.5 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 Budget.
|
|
Failover bekommt IMMER einen echten Versuch.
|
|
*Kosten:* Worst-Case-Wartezeit pro Item steigt. Das Budget ist dabei ein DREHregler, kein
|
|
fixer Wert: 60s/Provider = bis 180s Worst-Case (3 Provider), 30s/Provider = bis 90s usw.
|
|
Du akzeptierst langsameres Worst-Case-pro-Item für vollständigeres Failover — und stellst
|
|
über den Pro-Provider-Wert ein, wie viel langsamer.
|
|
|
|
- **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)
|
|
|
|
- **Stille Datei-Beschädigung beim Resume nach Verbindungsabbruch behoben (für Dateien mit bekannter Größe):**
|
|
Wenn ein Debrid-Server beim Abbruch einen kleinen Fehler-Müll-Block mitten in den Datenstrom schreibt und
|
|
das genau im LETZTEN Wiederhol-Versuch passierte, blieb dieser Müll in der Datei und der anschließende
|
|
Neuversuch (mit frischem Link) hängte die echten Bytes DAHINTER an — die Datei hatte am Ende exakt die
|
|
richtige Größe und galt deshalb als fertig, obwohl mittendrin Müll steckte. Bei .mkv/.mp4 ohne Prüfsumme
|
|
fiel das nie auf. Jetzt wird der verdächtige Datei-Schwanz vor der Linkerneuerung zurückgespult, sodass der
|
|
Neuversuch ihn sauber überschreibt. Rot-bewiesener Test (Inhalt byte-genau geprüft, nicht nur die Länge).
|
|
Ehrlich eingeordnet: Das ist behoben für Dateien, bei denen der Anbieter die Größe meldet (fast immer der
|
|
Fall). Für die seltenen Fälle ganz ohne Größenangabe bleibt eine separate, schon vorher bestehende Lücke
|
|
(ohne Längensignal nicht über dieses Rückspulen lösbar) — dokumentiert als langfristiges Thema.
|
|
- Zusätzlich gehärtet: Ein fehlgeschlagenes Zurückspulen (z.B. Datei kurz von Virenscanner gesperrt) wird
|
|
jetzt im nächsten Versuch erneut probiert statt still übergangen.
|
|
|
|
|
|
- **Deutsche Tonspur: falsche Spur-Auswahl behoben (Datenverlust-Schutz):** Die Erkennung der
|
|
deutschen Tonspur hat den Titel-Text einer Spur („...German...") auch dann ausgewertet, wenn die
|
|
Spur bereits ein anderssprachiges Tag hatte (z.B. eine englische Spur mit „German" im Titel, wie
|
|
„German Commentary"). Lag so eine Spur VOR der korrekt mit „ger" getaggten Spur, wurde die falsche
|
|
(englische) behalten und die echte deutsche Spur beim Remux unwiderruflich verworfen — das Ergebnis
|
|
wurde als Erfolg gemeldet. Jetzt wird der Titel nur noch dann herangezogen, wenn gar kein Sprach-Tag
|
|
vorhanden ist (so war es ohnehin gemeint). Korrekt getaggte deutsche Spuren gewinnen jetzt immer.
|
|
Rot-bewiesener Test. (Noch nicht released — wird mit der nächsten Runde gebündelt.)
|
|
|
|
|
|
- **Endlos-Wiederholung bei festem Wiederholungslimit behoben:** Wenn du ein FESTES Retry-Limit
|
|
(z.B. 5) eingestellt hattest UND ein Link sprunghaft verschiedene Fehlerarten produzierte
|
|
(mal Umwandlungs-Timeout, mal Abbruch mitten im Download, mal allgemeiner Fehler), konnte ein
|
|
Eintrag in einer Endlosschleife hängen: eine interne „Viele-Fehler"-Pause halbierte die Zähler,
|
|
sodass das eingestellte Limit nie erreicht wurde — der Eintrag scheiterte nie, blockierte dauerhaft
|
|
einen Download-Slot und hämmerte den Anbieter (weil dabei auch die Anbieter-Sperre zurückgesetzt
|
|
wurde). Jetzt wird das von dir eingestellte Limit hart eingehalten: nach N Versuchen scheitert der
|
|
Eintrag sauber. Standard-Einstellung („unendlich", der Auslieferungs-Default) war nie betroffen.
|
|
Rot-bewiesener Test.
|
|
|
|
|
|
- **Mega „bis Tagesreset gesperrt" parkt jetzt wirklich (statt alle 2 min neu zu versuchen):**
|
|
Wenn ALLE Mega-Accounts wegen wiederholt leerer Antworten bis zum Tagesreset geparkt waren,
|
|
hat das Tool den Fehler bisher als normalen Umwandlungsfehler behandelt und den ganzen Tag
|
|
alle ~2 Minuten neu probiert (und dabei den Provider-Circuit-Breaker mit Fehlern vollgemüllt).
|
|
Jetzt erkennt es den Park und legt das Paket EINMAL bis zum Tagesreset schlafen — genau das,
|
|
was der „bis Tagesreset"-Park eigentlich erreichen sollte. Bei Standard-Einstellungen heilte
|
|
sich das vorher schon um Mitternacht selbst (kein Datenverlust), war aber unnötige Log-Flut
|
|
und Churn. Rot-bewiesener Test.
|
|
|
|
|
|
- **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.)
|