d594c5082b
8 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ba144f323b |
Fix: Statistik-Doppelzaehlung bei Retry + Debrid-Link Key-Cooldown bei Abbruch (Nutzer-Nachforderung Audit)
BYTE-DROP-RETRY-1 (MED): Integritaets-/Zu-klein-/Tiny-Neuversuche zaehlten die volle Dateigroesse pro Versuch erneut in die Byte-Statistik. Ursache: die 3 rm-dann-frisch-Sites (Integrity-Fail 8979, too-small 9020, tiny 10486) riefen dropItemContribution() auf, das den itemContributedBytes-Eintrag loescht, den die einzige Reconciliation (9991, writeMode 'w') zum Subtrahieren braucht -> Subtraktion tot -> Re-Download addiert erneut. Fix (Mechanismus a): dropItemContribution an diesen 3 Sites entfernt, der Eintrag ueberlebt, 9991 subtrahiert ihn korrekt (selbst-korrigierend nach writeMode). Zusaetzlich am selben Punkt totalDownloadedAllTime subtrahiert (wurde nie subtrahiert -> doppelte bei JEDEM frischen Re-Download). BEWUSST ausgeklammert: recordProviderDownloadedBytes/ providerDailyUsageBytes, da diese isProviderDailyLimited (= Verhalten) steuern und nicht provider-keyed sind. Reine Telemetrie-Korrektur, kein Slot/Admission betroffen. DL-1 (LOW): Ein abort-ohne-timeout (User-Cancel) setzte am DebridLink-Rotations- Catch via classifyKeyFailure einen 15s-Key-Cooldown (und konnte ueber Keys zu einer providerweiten Kaskade fuehren). Fix: Mega-Gate (2072) am Catch (2789) gespiegelt - abort-ohne-timeout + elapsedMs < getMegaDebridAbortMinRunMs() -> kein Cooldown; ran-long-enough -> 120s (Retry rotiert); throw bailt die Rotation. DL-CONCURRENCY-PILEUP (MED): untersucht -> bereits strukturell geloest (getSerializedValidatingLimit = nutzbare Accounts + shouldDelayStartForItem 8526; MW-1 haelt das Limit hoch). Kein Eingriff (Nutzer-Entscheidung). Je rot-bewiesener Test (per Temp-Revert, nicht-vakuum; BYTE-DROP beide Beine einzeln). Volle Suite 893 gruen, tsc unveraendert (6 vorbestehende Fehler). |
||
|
|
fa2c7eb6be |
Fix: Mega-Web Selbst-Cooldown bei belegter Queue + Legacy-Config Mega-Demotion (Audit Runde 9+10)
MW-1 (HIGH): Ein gesunder Mega-Web-Account wurde 120s gesperrt, wenn der
60s-Gesamttimeout ablief, waehrend die Umwandlung noch SERIELL in der
per-Account-Single-Flight-Queue auf ihren Vorgaenger wartete - also bevor echte
Arbeit begann. Die Queue-Wartezeit zaehlte zu elapsedMs (>= 8s), sodass der
Abbruch faelschlich wie ein In-Arbeit-Abbruch eines langsamen Accounts gewertet
wurde. Genau die gemeldete 'Tool sperrt sich selbst'-Klasse (Web-Variante).
Zweiteilig: (1) MegaWebFallback.runExclusive trackt workStarted und meldet einen
Abbruch-vor-Arbeitsbeginn als Queue-Timeout statt aborted:mega-web;
(2) unrestrictViaWeb bewahrt diese Klassifikation statt sie zu aborted:debrid zu
plaetten -> die Rotation trifft die Queue-Timeout-Ausnahme (cooldownMs 0) statt
den Abbruch-Cooldown. Ein echter In-Arbeit-Abbruch sperrt weiterhin (langsame
Accounts korrekt ueberspringen). Nach dem No-Cooldown-Pfad rotiert der Retry per
In-Flight-Tiefe auf einen freien Account.
SET-MIG-01 (MED): Eine Config von vor v1.6.90 kannte die getrennten
Mega-API/Web-Enable-Flags nicht. readSettingsFile merged {...defaultSettings(),
...parsed}, also fuellten die false-Defaults die fehlenden Flags BEVOR
normalizeSettings lief -> der creds-basierte Migrationszweig war tot, Mega blieb
auf aus trotz vorhandener Creds und wurde beim ersten Settings-Save still aus der
providerOrder demotet. Fix: reine migrateLegacyMegaEnableFlags seedet die Flags
nur, wenn BEIDE im RAW-parsed fehlen und Creds vorhanden sind (absent-both als
einziger sicherer Trigger; present-false bleibt unberuehrt = bewusst-deaktiviert
nicht re-aktivieren). Greift nur bei Legacy-Configs, die seit dem Upgrade noch
nicht neu gespeichert wurden.
Je rot-bewiesener Test (per Temp-Revert verifiziert, nicht-vakuum). Volle Suite
890 gruen, tsc unveraendert (6 vorbestehende Fehler). Runde-9/10-Doku + die zwei
offenen Nutzer-Entscheidungen aktualisiert.
|
||
|
|
eacd0c9d81 |
Fix: stille Mid-File-Korruption beim Resume nach Verbindungsabbruch (known-total) + Rewind-Haertung (Runde-8-Audit)
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). |
||
|
|
be15419882 |
Fix: deutsche Tonspur-Erkennung waehlt nicht mehr eine falsch-getaggte Spur per Titel (Datenverlust-Schutz, Runde-7-Audit)
isGermanStream wertete die Titel-Regex /\b(german|deutsch)\b/ fuer JEDEN nicht
deutsch-getaggten Audiostream aus — also auch fuer eine bereits anderssprachig
getaggte Spur (z.B. {language:"eng", title:"German Commentary"}). Der Kommentar
ueber dem Titel-Fallback sagt ausdruecklich, er sei "used when the language tag
is missing", aber genau diese Gating-Bedingung fehlte.
Folge: pickAudioTrack (der EINZIGE Caller) nimmt den ersten isGermanStream-
Treffer. Lag eine eng-Spur mit "German"/"Deutsch" im Titel VOR der korrekt mit
"ger" getaggten Spur, gewann die englische Spur. Der Remux behielt dann Englisch,
verwarf die echte deutsche Spur, ersetzte das Original atomar in-place und
strippte den .DL.-Marker — irreversibler Datenverlust mit falscher Sprache, vom
System als Erfolg ("german-tag"/"remuxed") gemeldet. Single-Trigger, kein
compound-Vorbedingung. Betrifft genau die vom Nutzer bestaetigte Tonspur-Funktion.
Fix: Titel-Fallback nur noch ausfuehren, wenn KEIN Sprach-Tag vorhanden ist
(`if (lang) return false;` vor der Titel-Pruefung) — deckt sich exakt mit der
dokumentierten Absicht des bestehenden Kommentars. Korrekt getaggte deutsche
Spuren gewinnen jetzt immer; eine anderssprachige Spur mit "German" im Titel wird
nicht mehr faelschlich gewaehlt. Untagged-Spuren nutzen weiter den Titel-Fallback
(unveraendert), und der German-Release-Mislabel-Pfad (German-first-Konvention)
greift weiterhin.
Runde-7-Audit (adversarisch verifiziert) hat ausserdem den deferred-Extraction-
Cluster abgeschlossen: #10 CRC-Kleindatei-Delete und #12 7z-Exit-1-als-Erfolg sind
BENIGN, #11 resume-empty-output not-confirmed (compound), #13 = das dokumentierte
LOW Companion-Overwrite (PP-1, compound, nur Sekundaerdateien).
Rot-bewiesen: zwei reine pickAudioTrack-Tests (eng-Titel-"German" vor echter ger →
audioRelIndex 1; eng-Titel-"Deutsch entfernt" ohne echte dt. Spur → skip) failen
ohne den Gate. Volle Suite 884 gruen, tsc unveraendert.
|
||
|
|
d2a1b831a0 |
Fix: festes Retry-Limit wird jetzt hart eingehalten — kein Endlos-Shelve-Loop mehr (Runde-6-Audit)
Bei einem FESTEN retryLimit (>=5) sind alle drei Per-Klasse-Retry-Caps (stall/unrestrict/generic) gleich dem Limit. Die beiden "15-Fehler"-Shelve-Zweige (download-manager.ts stall-Pfad + error-Pfad) feuern aber auf der hartkodierten Summe >=15 OBERHALB der Per-Klasse-Terminal-Fail-Pruefungen und HALBIEREN danach alle drei Zaehler. Dadurch wurden die Per-Klasse-Caps nie gleichzeitig ueberschritten: der Eintrag erreichte nie status="failed", schliff endlos im 90s-Takt, item.retries wuchs weit ueber das konfigurierte Limit, der Download-Slot blieb dauerhaft belegt, und das wiederholte providerFailures.delete besiegte immer wieder den Provider-Circuit-Breaker → Hoster-Hammering. resetStaleRetryState konnte nicht eingreifen, weil der <=90s-Re-Admit item.updatedAt frisch haelt (nie 10min stale). Der Auslieferungs-Default retryLimit=0 (= unendlich) ist NICHT betroffen — dort ist der 15-Fehler-Shelve der gewollte Dauer-Park-Backstop und es gibt kein endliches Budget zu verletzen. Fix: in BEIDEN Shelve-Zweigen vor dem queueRetry eine harte Obergrenze — `if (configuredRetryLimit > 0 && item.retries >= configuredRetryLimit)` failt den Eintrag terminal (status="failed", recordRunOutcome, retryStateByItem.delete) statt ihn neu zu queuen. Der ∞-Modus (retryLimit<=0) ueberspringt die Grenze und bleibt unveraendert. Runde-6-Audit (adversarisch verifiziert) hat ausserdem den deferred-LOW-Cluster abgeschlossen: #10 HTTP416-shared-counter, #11 fresh-retry-preempt und #13 queue-wait→elapsedMs sind BENIGN; #12 war genau dieser Bug. Rot-bewiesen: Single-Pass-Integrationstest seedet genericErrorRetries=15 vor und injiziert einen generischen Fehler bei retryLimit=5 → ohne Fix requeued der Shelve (status bleibt nicht "failed"), mit Fix wird terminal gefailt. Volle Suite 882 gruen, tsc unveraendert. |
||
|
|
f1e35f5f41 |
Fix: Mega "bis Tagesreset gesperrt" parkt das Paket bis zum Reset statt es den ganzen Tag alle 2 min neu zu versuchen
Wenn ALLE Mega-Debrid-Accounts wegen wiederholt leerer/Server-loser Antworten
bis zum Tagesreset geparkt waren (in-memory untilRestart-Park aus Runde 3),
warf unrestrictWithAccounts einen reinen Klartext-Fehler ohne Maschinen-Token.
Im Manager-Catch fiel dieser durch parseMegaDebridCooldownRetry (kein
mega_debrid_cooldown:-Praefix) und isMegaDebridTransientResolveFailure und
landete im generischen isUnrestrictFailure-Zweig — nur weil der Text
"mega-debrid" enthaelt. Folge: das Item wurde den ganzen Tag etwa alle zwei
Minuten neu versucht (120s-Cap) und fuetterte dabei recordProviderFailure den
Provider-Circuit-Breaker, statt einmal bis zum Tagesreset zu parken. Bei
Standard-Einstellungen (retryLimit=0=unendlich) heilte es sich zwar um
Mitternacht selbst (kein Stranding), war aber unnoetige Log-Flut und Churn und
unterlief genau die untilRestart-Park-Absicht aus Runde 3.
Cross-Layer-Synthese-Pass (adversarisch verifiziert) hat parallel die
Advisor-Hypothese eines Key-Mismatch (Manager cool't aufgeloesten
"megadebrid-api", Fallback-Suche liest rohen "megadebrid") WIDERLEGT:
normalizeProviderOrder speichert immer den aufgeloesten Key, also faellt
Write/Check/Clear/Read auf denselben Key — der Routing-Fix (
|
||
|
|
03c908bd30 |
Test: No-Stranding-Gate fuer Provider-Fallback-Routing + Doku-Praezisierung
findFallbackProviderNotInCooldown-Charakterisierungstest sichert den Invariant
ab, auf dem der Routing-Fix (
|
||
|
|
986fbab04f |
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). |