real-debrid-downloader/tasks/todo.md

13 KiB
Raw Permalink Blame History

Real-Debrid-Downloader — Tasks (Stand 2026-06-08)

Status: Alle zugesagten Features erledigt+released (Archiv unten). Aktuell läuft ein intensiver Bug-Audit (User-Goal 2026-06-08, "schaue intensiv nach weiteren Bugs") — Fortschritt direkt unten.


🔴 LAUFEND — Bug-Audit 2026-06-08 (Multi-Agent find→verify, 18 bestätigt)

Advisor-Triage: A = einzige echte Daten-Verlust-Notlage (zerstört echte Datei auf Platte) → zuerst, ALLEINE Release. B verifiziert demoted: applyRetroactiveCleanupPolicy/ removePackageFromSession löschen KEINE Platten-Dateien (nur Session/Queue-Einträge + ggf. History-Eintrag) → Queue-Integrität, nicht Daten-Verlust → in v1.7.190-Batch. Sequenz: Release 1 (v1.7.189) = A allein; Release 2 (v1.7.190) = B/I,C,D/E,F,G,H,J,L,M,N,O,P,Q. Ein Commit pro Fix, jeder einzeln verifiziert. K übersprungen (auto-rename-Reorder, schlechtestes Risiko/Nutzen, kann für diesen User gar nicht feuern).

Release 1 — Daten-Verlust-Stopper (v1.7.189, A ALLEIN)

  • A video-processor.ts atomic-replace zerstörte bei Windows-Lock BEIDE Kopien (rm(original) VOR bestätigtem Replace + outer-catch rm(temp) → 0 Kopien). GEFIXT: atomic replace-over + renameWithRetry (EBUSY/EACCES/EPERM/EEXIST, Backoff 200/500/1000ms), rm-first-Fallback entfernt, unique Temp-Name (~rd<pid><rand>, löst auch C-Kollision). Advisor bestätigt Ansatz besser als bak-dance (kein Missing-File-Window). 3 neue Tests (Recovery + Retry-Pfad), 41 video-processor-Tests grün, tsc=6 (Baseline). Commit 189af22.

Release 2 — v1.7.190 (GEFIXT + verifiziert, ein Commit pro Fix)

  • L+M video-processor.ts zu weite Deutsch-Erkennung. isGermanStream Titel-Fallback nur ganze Wörter (ger/deu raus → konnten falsche Spur picken + echte dt. löschen); looksLikeGerman Release 'dubbed' raus (ital./franz. Dub triggerte German-first). 2 Negativtests. Commit 272a41a.
  • H logger.ts flushAsync slice-snapshot korrumpiert bei 1MB-Cap-Trim während await → ungeschriebene Zeilen verloren. Move-snapshot (Buffer auf [] übernehmen) + Requeue bei Schreibfehler. Commit 4432fa2.
  • J+Q download-manager. J: runPackagePostProcessing finally löschte Map-Eintrag ohne Identity-Guard → Abort+Neustart-Race riss neuen Task raus (Waise + Doppel-Lauf); jetzt nur löschen wenn Map noch auf DIESEN Task/Controller zeigt (handle-Objekt wegen TS2454). Q: collectFilesByExtensions filtert ~rd-Temp-Präfix (crash-verwaiste Teil-Remuxe nie ins Library). Commit 3c33b98.
  • P extractor.ts nested-Resume-Keys (nested:<name>) bei jedem extractPackageArchives gepurged → verschachtelte Archive beim Resume neu entpackt; startsWith("nested:") im Prune übersprungen. Commit 61a8304.
  • B/I app-controller.ts importBackup settings-only purgte LIVE-Queue (Dateien blieben auf Platte) + rollte Usage-Zähler zurück. Fix: setSettings({suppressRetroactiveCleanup}) + overlayLiveUsageCounters (extrahiert+wiederverwendet, inkl. Key-Filter). Commit dc05b51.

Verifiziert KEINE Bugs / bewusst NICHT angefasst (Advisor-Disziplin: erst belegen, dann ändern)

  • G dropItemContribution "subtrahiert Session-Totals nicht" → KEIN Bug: Test "keeps cumulative session totals when completed items are removed" kodifiziert die Absicht (Session- Zähler kumulativ, divergieren bewusst von der Item-Map; Retry-Pfad zieht ab, weil neu geladen wird). Fix-Versuch ließ den Test failen → revertiert, Klarstellungs-Kommentar gesetzt.
  • N stripDualLangFromFileName "Kollision" → bereits geguarded: existsAsync-Skip verhindert Überschreiben; Remux machte Inhalt eh deutsch-only; collect strippt .DL. downstream. Residual = generischer Rename-TOCTOU (in JEDEM Rename-Pfad), kein spezifischer Bug hier.
  • D/E abort-Klassifizierung über signal.reason statt Text → deferred (Robustheit, kein Live-Bug auf User-Pfad). BELEGT: mega-web-fallback normalisiert JEDEN Abort (Timeout UND Cancel) zu new Error("aborted:mega-web") → aktueller Guard /aborted/i && !/timeout/i FEUERT → v1.7.187-Cooldown LÄUFT auf dem Web-Pfad (User-Pfad). Einzige Imperfektion: Cancel >8s wird fälschlich gecooled (minor). Empirisch bestätigt: AbortSignal.any([ac,timeout]).reason?.name=== 'TimeoutError' (timeout) vs string/AbortError (cancel) — falls je gebaut: signal.aborted-gaten, reason.name nutzen, Text-Fallback behalten, reason-Test. Hoch-Risiko (kritischer Unrestrict-Pfad JEDES Downloads) → nicht für Robustheit anfassen. API-Pfad-Abort-Text nicht erschöpfend geprüft.
  • E "API 'cancel'-Pfad umgeht" → nicht real: kein 'cancel'-throw im Code gefunden.
  • O classifyAccountFailure abort-Branch tot → stehen lassen: tot NUR wegen aktueller Text-Interception; ein signal.aborted-gated D/E würde ihn wiederbeleben. Kein Kosmetik-Churn.
  • F Mega-Web empty-streak Concurrency → N-shaped, deferred: Streak wird bei Erfolg (1956)
    • Nicht-Limit-Fehler (2005) gecleart; "bis Neustart gesperrt" ist bewusste Tageslimit-Logik, Restart-cleared; Mega-Web single-flight → Concurrency greift nicht. Keine fühlbare Schädigung konstruierbar → keine Park-State-Maschinerie.
  • C → in A subsumiert (unique Temp-Name). K übersprungen (auto-rename-Reorder, Risiko≫Nutzen).

🟢 OFFEN — Backlog (optional, nie begonnen)

Mega-Web Account-Rotation überspringt Account 3 — GEFIXT 2026-06-08 (v1.7.187)

Fix: Ein Mega-Web-Account-Abbruch (geteiltes Timeout feuert während der Account lief) setzt jetzt einen 2-min-Cooldown auf den Account (nur wenn er ≥8s lief, sonst = User-Cancel, RD_MEGA_ABORT_MIN_RUN_MS env). Dadurch überspringt der download-manager-Retry diesen Account und rotiert zum nächsten (debrid.ts, abort-Handling im Rotations-catch, vor classifyAccountFailure). Log-Event TIMEOUT_COOLDOWN (gelb, "Timeout/Abbruch → nächster Account beim Retry") statt rotem "fataler Fehler" (App.tsx:1141 Label). 2 Regressionstests (Cooldown gesetzt → Call 2 rotiert; Quick-Abbruch → kein Cooldown). EHRLICH: fixt Korrektheit, NICHT Latenz — Account 1 brennt weiter ~60s ins Timeout bevor der Retry auf Account 2 wechselt (instant-Failover bräuchte per-Account-Timeout = größerer Eingriff, bewusst verschoben). Advisor-gegengeprüft.

(Ursprüngliche Analyse — Symptom & Mechanismus, zur Doku belassen) Symptom (User): 3 Mega-Debrid-Web-Accounts aktiv, Rotation pendelt aber nur zwischen Account 1 ↔ 2 (bzw. nur Account 1), Account 3 (Su****xe) wird NIE probiert.

Verifizierter Mechanismus (Code):

  • Rotationsschleife debrid.ts:1898. Account 1 → "Mega-Web Antwort leer" → Cooldown 20s → weiter zu Account 2. Account 2 → aborted:debrid.
  • classifyAccountFailure (debrid.ts:2036) stuft JEDEN Abbruch als fatal ein → throw (debrid.ts:1991) → Schleife bricht ab → Account 3 nie erreicht.
  • Account 2 bekommt beim Fatal-Abbruch keinen Cooldown (cooldownMs:0). Beim download-manager-Retry wird Account 1 (Cooldown) übersprungen, aber Account 2 (kein Cooldown) ERNEUT vor Account 3 probiert → bricht wieder ab → ewiges 1↔2.
  • Geteiltes 60s-Unrestrict-Timeout download-manager.ts:8590 (AbortSignal.any([taskAbort, timeout(60s)])) gilt für die GANZE Rotation, nicht pro Account. Mega-Web pollt intern bis 180s (mega-web-fallback.ts:235 + Poll-Loop :371). Sobald das geteilte 60s feuert, bleibt das kombinierte Signal aborted → KEIN späterer Account kriegt im selben Pass eine echte Chance.

BESTÄTIGT 2026-06-08 (zweite Screenshots): Account 1 läuft 10x rasch "erfolgreich" (11:51:4511:52:26), dann zwei "abgebrochen (aborted:debrid)" um 11:53:30 UND 11:54:30 — exakt 60s auseinander = das geteilte 60s-Unrestrict-Timeout feuert (kein User-Stop, der wiederholt sich nicht periodisch). Hier rotiert GAR NICHTS: Account 1 bricht ab → fatal → Rotation stoppt sofort bei idx=0 → Account 2 und 3 werden NIE probiert. Bug eindeutig bestätigt, elapsedMs nicht mehr nötig. Account 1 selbst ist gesund (10x ok) — Mega-Web hängt nur sporadisch (no-server-Poll) bis ins 60s-Timeout.

Fix-Design (wenn bestätigt): Pro-Account-Timeout-Budget, abgekoppelt vom geteilten Cap. debrid.ts braucht das cancel-only Signal getrennt vom Timeout (kombiniertes Signal kann beides nicht unterscheiden). Minimal-invasiv: optionaler opts-Param an unrestrictLink ({cancelSignal, perAttemptTimeoutMs}) — nur die Mega-Rotation liest ihn, andere Provider unberührt (kombiniertes Signal bleibt). Pro Account: AbortSignal.any([cancelSignal, AbortSignal.timeout(perAttemptMs)]). Abbruch-Logik: cancelSignal aborted → echter Stop; eigenes Account-Timer gefeuert → non-fatal, Cooldown, weiter zum nächsten Account (inkl. 3). Regressionstest ZUERST (3 Accounts, 1+2 failen/aborten → assert Account 3 kriegt TEST). Advisor-Gate vor Eingriff (kritischer Unrestrict-Pfad, betrifft jeden Download). Hinweis: Grundursache der leeren Antworten = Mega-Debrid Server/IP-Thema — Fix macht Rotation nur FAIRER (alle Accounts drankommen), bringt aber keinen busy Server zum Antworten.

Features / UX (nach ROI)

App läuft headless auf Windows-Server → Nutzer sitzt nicht davor.

  1. Push-Benachrichtigungen (Discord/Telegram/ntfy) — SM. Paket fertig/Fehler/Quota/Provider-down aufs Handy. Neuer notifier.ts, Hooks an Completion-Punkten. Höchster ROI.
  2. Fernsteuerung über Debug-Server (POST-Endpunkte) — SM. Server hat HTTP + Token-Auth, aber nur GET. POST /control/add-links, /start, /stop.
  3. URL-Duplikat-Erkennung beim Hinzufügen — S. History-urls existiert, wird nie geprüft → versehentliche Re-Downloads. Warnen: "3 Links bereits geladen".
  4. Pre-Flight-Check + Bulk-Skip toter Links — M. Vor Start Größe/Name/Online für ganze Queue, "alle offline überspringen".
  5. Speicherplatz-Vorabprüfung vor Start — S. Aktuell keine Free-Space-Prüfung für Downloads → Abbruch mitten drin bei voller Platte.
  6. Konsolidierte Fehler-Ansicht — M. Alle fehlgeschlagenen Items flach + Fehlertext + "alle erneut versuchen". (Daten dafür liegen jetzt teils in der Error-Ring aus v1.7.185.)
  7. Per-Provider-Statistik — M. Rohdaten (providerTotalUsageBytes) existieren, werden nicht dargestellt. Welches Abo lohnt sich?
  8. Auto-Retry fehlgeschlagener Pakete nach Wartezeit — SM. Quota/Cooldown-Fails am nächsten Tag automatisch neu.
  9. Plex/Jellyfin Library-Refresh nach MKV-Move — S. Gleicher Hook wie #1.
  10. Watch-Folder für DLC/Link-Auto-Import — M.

Design-Richtung (Entscheidung steht aus)

4 Mockups in design-mockups/ (index.html = Vergleich): Aurora (verfeinert dark, geringstes Risiko) · Command (Terminal/Ops, dicht) · Vellum (light editorial) · Nebula (neon). → Richtung wählen. Siehe Memory: design-taste (Anti-KI-Look) + design-direction (Ember-Wärme, flach/ehrlich).

Alte Audit-Items (2026-04-04, Status ggf. veraltet — VOR Fix gegen aktuellen Code verifizieren)

  • Debrid-Link maxDataHost kühlt ganzen Key ab statt nur den Host
  • Debrid-Link fileNotAvailable setzt Key auf "error" statt temporär
  • AllDebrid: kein per-host-Cooldown für erschöpfte Quotas
  • LinkSnappy: keine Auth-Dedup (parallele Requests rufen beide authenticate())
  • Extractor password-cache race (parallele Worker mutieren packageLearnedPasswords)
  • Hybrid race: 1 Datei/Staffel evtl. beim MKV-Move nicht umbenannt (NUR per-package fixen — Post-MKV-Move-Scan ist tabu, v1.7.107 revertiert)

ERLEDIGT — Archiv (Details in git-History + Memory)

  • Erweitertes Logging → released v1.7.185 (Crash-Handler, Renderer-Fehler-IPC, RD_DEBUG-Level, Error-Ring + /errors, ENOSPC-Klassifizierung, Memory-Heartbeat). → Memory: extended-logging
  • Link-Prefetch → untersucht (6-Agent) + bewusst verworfen (marginal bei maxParallel 8, Mega-Web single-flight). → Memory: link-prefetch-declined
  • Backup nur Settings → v1.7.184 (backupIncludeDownloads-Toggle + 4 Selektions/Flicker-Fixes). → Memory: backup-settings-only
  • Account-Rotation-Overhaul → v1.7.164168 (Validity/Premium-Badges, Live-Panel, "Alle prüfen"). → Memory: account-rotation
  • Mega-Debrid-Account deaktivieren (UI) → erledigt (Toggle im Edit-Dialog, im Code verifiziert 2026-06-07)
  • Bugs/Robustheit (Deferred-Pipeline H1/H2/H3/M1/M2/N1) → v1.7.158/159; M3 bewusst übersprungen (Generation-Guard schützt Integrität bereits)
  • Deferred-Pfad Rename-Gap → gefixt v1.7.162+ (finaler Deferred-Pass benennt frische Dateien vor Collect um; Repro-Test grün)
  • Repo-Privacy-Audit → GitHub gelöscht+neu (saubere History), Gitea unberührt. → Memory: repo-privacy-audit

Bewusst NICHT angefasst (Crash-Debris / alte Experimente)

  • Gestashtes Crash-Debris stash@{0} (Revert von 08372f9/18eada9/98dc366 + log.old) — bei Bedarf recoverbar, sonst verwerfbar
  • Untracked *-postprocess/ + fix-library-renames.mjs — alte Experimente (Apr/Mai)