Fix: Mega-Cooldown vor Tagesreset-Park bevorzugen + Token-Truncation + Rewind-Fail-Haertung (Synthese-Pass)
Capstone-Synthese/Regressions-Pass ueber den kumulativen Audit-Diff (v1.7.212..HEAD) hat 3 Fix-INTERAKTIONEN gefunden — der Rest komponiert sauber. C1 (MED): Sind BEIDE Mega-Modi aktiv (API + Web), aggregiert die Provider-Kette bei Fehlschlag beide Token in einen String: `mega_debrid_reset_park:LONG` (API bis Tagesreset geparkt) + `mega_debrid_cooldown:30000` (Web nur kurz im Cooldown). Der Manager pruefte reset_park VOR cooldown → das Item wurde ~24h geparkt, obwohl der Web-Modus in ~30s wieder verfuegbar gewesen waere. Fix: den Cooldown-Zweig (kuerzerer Delay) VOR dem reset_park-Zweig pruefen; der erholbarere Pfad gewinnt. C2 (LOW): Das reset_park-Token konnte von compactErrorText (220-Zeichen-Cap) abgeschnitten werden, wenn Mega nicht der Lead-Provider war und ein vorheriger Provider eine lange Fehlermeldung lieferte → Park wurde still uebersprungen und Mega am Tageslimit weiter gehaemmert. Fix: beide Mega-Token aus der UNGEKUERZTEN error.message parsen statt aus dem gekuerzten Anzeige-String. RANGE1-truncate-fail (LOW, Haertung + Doku-Korrektur): Der v1.7.219-Commit behauptete, das Setzen von downloadedBytes ZUERST rette den Fall eines fehlgeschlagenen finalen Rewind-truncate ueber den Re-Entry-prealloc-reconcile. Das stimmt nicht — bei truncate-Fehler finalisiert tryFinalizeItemFromDisk die exakt-langen Garbage-Datei VOR dem Re-Entry (net-neutral vs pre-audit, KEINE Regression, aber der Claim war falsch). Haertung: bei fehlgeschlagenem finalem Rewind die Teil-Datei verwerfen (rmSync + downloadedBytes=0) → Finalize lehnt ab → sauberer Re-Download. Best-effort (faellt der rm auch, bleibt es net-neutral) — macht den 219-Claim jetzt wahr. Rot-bewiesen: Single-Pass-processItem-Test mit Aggregat beider Mega-Token → retryAfter < 60s (Cooldown) und fullStatus "Cooldown" statt "Tagesreset"; mit deaktiviertem Cooldown-Zweig (alte Reihenfolge) ~24h-Park = rot. Volle Suite 886 gruen, tsc=6.
This commit is contained in:
parent
f2ae1c19e2
commit
85c8d6bdf9
@ -9436,24 +9436,8 @@ export class DownloadManager extends EventEmitter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const megaResetPark = parseMegaDebridResetPark(errorText);
|
const megaRawError = error instanceof Error ? String(error.message || "") : String(error || "");
|
||||||
if (megaResetPark && active.unrestrictRetries < maxUnrestrictRetries) {
|
const megaCooldownRetry = parseMegaDebridCooldownRetry(megaRawError);
|
||||||
active.unrestrictRetries += 1;
|
|
||||||
item.retries += 1;
|
|
||||||
logger.warn(`Mega-Debrid bis Tagesreset gesperrt: item=${item.fileName || item.id}, retry=${active.unrestrictRetries}/${retryDisplayLimit}, delay=${megaResetPark.delayMs}ms, link=${item.url.slice(0, 80)}`);
|
|
||||||
this.queueRetry(
|
|
||||||
item,
|
|
||||||
active,
|
|
||||||
megaResetPark.delayMs,
|
|
||||||
`Mega-Debrid bis Tagesreset gesperrt, Pause ${Math.ceil(megaResetPark.delayMs / 1000)}s`
|
|
||||||
);
|
|
||||||
item.lastError = megaResetPark.detail || errorText;
|
|
||||||
this.persistSoon();
|
|
||||||
this.emitState();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const megaCooldownRetry = parseMegaDebridCooldownRetry(errorText);
|
|
||||||
if (megaCooldownRetry && active.unrestrictRetries < maxUnrestrictRetries) {
|
if (megaCooldownRetry && active.unrestrictRetries < maxUnrestrictRetries) {
|
||||||
active.unrestrictRetries += 1;
|
active.unrestrictRetries += 1;
|
||||||
item.retries += 1;
|
item.retries += 1;
|
||||||
@ -9470,6 +9454,23 @@ export class DownloadManager extends EventEmitter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const megaResetPark = parseMegaDebridResetPark(megaRawError);
|
||||||
|
if (megaResetPark && active.unrestrictRetries < maxUnrestrictRetries) {
|
||||||
|
active.unrestrictRetries += 1;
|
||||||
|
item.retries += 1;
|
||||||
|
logger.warn(`Mega-Debrid bis Tagesreset gesperrt: item=${item.fileName || item.id}, retry=${active.unrestrictRetries}/${retryDisplayLimit}, delay=${megaResetPark.delayMs}ms, link=${item.url.slice(0, 80)}`);
|
||||||
|
this.queueRetry(
|
||||||
|
item,
|
||||||
|
active,
|
||||||
|
megaResetPark.delayMs,
|
||||||
|
`Mega-Debrid bis Tagesreset gesperrt, Pause ${Math.ceil(megaResetPark.delayMs / 1000)}s`
|
||||||
|
);
|
||||||
|
item.lastError = megaResetPark.detail || errorText;
|
||||||
|
this.persistSoon();
|
||||||
|
this.emitState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (isMegaDebridTransientResolveFailure(errorText) && active.unrestrictRetries < maxUnrestrictRetries) {
|
if (isMegaDebridTransientResolveFailure(errorText) && active.unrestrictRetries < maxUnrestrictRetries) {
|
||||||
active.unrestrictRetries += 1;
|
active.unrestrictRetries += 1;
|
||||||
item.retries += 1;
|
item.retries += 1;
|
||||||
@ -10607,7 +10608,12 @@ export class DownloadManager extends EventEmitter {
|
|||||||
rewindTarget
|
rewindTarget
|
||||||
});
|
});
|
||||||
} catch (rewindError) {
|
} catch (rewindError) {
|
||||||
logAttemptEvent("WARN", "Resume-Schutz: finales Rueckspulen fehlgeschlagen", {
|
try {
|
||||||
|
fs.rmSync(effectiveTargetPath, { force: true });
|
||||||
|
item.downloadedBytes = 0;
|
||||||
|
} catch {
|
||||||
|
}
|
||||||
|
logAttemptEvent("WARN", "Resume-Schutz: finales Rueckspulen fehlgeschlagen, Teil-Datei verworfen", {
|
||||||
attempt,
|
attempt,
|
||||||
written,
|
written,
|
||||||
rewindTarget,
|
rewindTarget,
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
# Autonomer Audit-Loop — Download/Fehler/Rotation (Goal 2026-06-17, 8h)
|
# Autonomer Audit-Loop — Download/Fehler/Rotation (Goal 2026-06-17, 8h)
|
||||||
|
|
||||||
|
## GOAL-ANPASSUNG (Nutzer, nach Runde 8 + Synthese-Start): noch Runde 9 + 10, dann Goal BEENDEN.
|
||||||
|
Plan: Synthese-Pass (wrexz7mdf, Capstone R1-8) auswerten → Runde 9 (Provider-spezifische
|
||||||
|
Unrestrict-/Rotations-Pfade: Mega-Web-Fallback Session/Single-Flight, AllDebrid Host-Cooldown/
|
||||||
|
Rapidgator-Backoff, DebridLink-Key-Rotation, Passwort-Cache-Race) → Runde 10 (Concurrency/Locking +
|
||||||
|
Settings/Persistenz-Integritaet: Hybrid-Race, targetPath-Claim/Release-Races, Settings-Migration,
|
||||||
|
Backup/Restore, account-check) → Abschlussbericht + Ende. Fixes je rot-bewiesen, buendeln zu v1.7.220 falls HIGH/MED.
|
||||||
|
|
||||||
Disziplin: erst BELEGEN (Code-Zitat + konkretes Szenario), dann adversarisch verifizieren,
|
Disziplin: erst BELEGEN (Code-Zitat + konkretes Szenario), dann adversarisch verifizieren,
|
||||||
dann TDD-Fix. Kein Blind-Fix. Tests gruen + tsc=6 nach jeder Runde. Periodisch releasen.
|
dann TDD-Fix. Kein Blind-Fix. Tests gruen + tsc=6 nach jeder Runde. Periodisch releasen.
|
||||||
|
|
||||||
@ -185,6 +192,28 @@ Multi-Agent-adversarisch, Routing-Fix frueher advisor-gesegnet, Praezedenz 215/2
|
|||||||
Runde-5/6-Charakterisierungen (PP-SEM-1 benign, DISK-1 deferred, deferred-LOW-Cluster #10/#11/#13
|
Runde-5/6-Charakterisierungen (PP-SEM-1 benign, DISK-1 deferred, deferred-LOW-Cluster #10/#11/#13
|
||||||
benign) NICHT released — dokumentiert.
|
benign) NICHT released — dokumentiert.
|
||||||
|
|
||||||
|
## SYNTHESE/Regressions-Pass (Fix-Interaktion ueber kumulativen Diff v1.7.212..HEAD) — Workflow wrexz7mdf
|
||||||
|
3 Reviewer (Catch-Cascade / Streaming / Provider-Chain) + adversarisch verifizieren. 3 confirmed (1 MED, 2 LOW),
|
||||||
|
2 refuted. Bestaetigt: Rest komponiert sauber; Risiko war (wie Advisor sagte) Fix-Interaktion, nicht neue Bugs.
|
||||||
|
- **CONFIRMED MED (GEFIXT) C1 reset_park maskiert kurzen Cooldown:** Bei BEIDEN Mega-Modi aktiv aggregiert die
|
||||||
|
Provider-Kette beide Token (`mega_debrid_reset_park:LONG` von API-Park + `mega_debrid_cooldown:30000` von
|
||||||
|
Web-Cooldown). Manager prueft reset_park VOR cooldown → Item ~24h geparkt obwohl Web in ~30s erholt. Fix:
|
||||||
|
Cooldown-Zweig VOR reset_park (kuerzerer Delay gewinnt). Rot-bewiesen (Single-Pass-processItem-Test: Aggregat
|
||||||
|
beider Token → retryAfter < 60s, fullStatus "Cooldown" nicht "Tagesreset"; ohne Reorder ~24h).
|
||||||
|
- **CONFIRMED LOW (GEFIXT) C2 Token-Truncation:** reset_park-Token konnte von compactErrorText (220-Zeichen-Cap)
|
||||||
|
abgeschnitten werden, wenn Mega nicht Lead + vorheriger Provider verbose → Park still uebersprungen. Fix: Mega-
|
||||||
|
Token aus der UNGEKUERZTEN error.message parsen (megaRawError). (Im selben Edit wie C1.)
|
||||||
|
- **CONFIRMED LOW (GEHAERTET + Doku korrigiert) RANGE1-TRUNCATEFAIL-FINALIZE:** Mein 219-Commit OVERCLAIMte —
|
||||||
|
bei FEHLGESCHLAGENEM finalem Rewind-truncate finalisiert tryFinalizeItemFromDisk(9238) die Garbage-Datei
|
||||||
|
(size==totalBytes) VOR dem Re-Entry. NET-NEUTRAL vs pre-audit (v1.7.212 hatte denselben Finalize), KEINE
|
||||||
|
Regression. Haertung: bei truncate-Fehler jetzt rmSync(Teil-Datei)+downloadedBytes=0 → Finalize lehnt ab →
|
||||||
|
sauberer Re-Download (best-effort; faellt der rm auch, bleibt es net-neutral). Macht den 219-Claim wahr.
|
||||||
|
- **Refuted (1/3 je):** reset_park von shelve-guard/unrestrictRetries-Exhaustion unter finite retryLimit
|
||||||
|
verdraengt — beide nicht bestaetigt (compound/nicht erreichbar).
|
||||||
|
- **Capstone-Verdikt:** Die 8-Runden-Fixes komponieren — inner-rewind success-only-reset und final-rewind sind
|
||||||
|
per-attempt mutually exclusive; prealloc-reconcile double-truncated nicht nach erfolgreichem Rewind; Routing
|
||||||
|
+ cooldown + park kohaerent NACH C1/C2-Fix. Diese 3 Synthese-Fixes buendeln zu v1.7.220.
|
||||||
|
|
||||||
## Runde 8 (Byte-Streaming downloadToFile: Range/Resume/Append/Truncation) — Workflow wwjz4srkq
|
## Runde 8 (Byte-Streaming downloadToFile: Range/Resume/Append/Truncation) — Workflow wwjz4srkq
|
||||||
3 Finder + adversarisch verifizieren. 3 confirmed (1 HIGH + 2 MED, alle Silent-Corruption-Familie), 1 refuted.
|
3 Finder + adversarisch verifizieren. 3 confirmed (1 HIGH + 2 MED, alle Silent-Corruption-Familie), 1 refuted.
|
||||||
Advisor VOR Implementierung konsultiert (HIGH-Hot-Path) — Design + Check-A-Branch (totalBytes-null) bestaetigt.
|
Advisor VOR Implementierung konsultiert (HIGH-Hot-Path) — Design + Check-A-Branch (totalBytes-null) bestaetigt.
|
||||||
|
|||||||
@ -301,6 +301,57 @@ describe("download manager", () => {
|
|||||||
expect(session.items[itemId].retries).toBeLessThan(20);
|
expect(session.items[itemId].retries).toBeLessThan(20);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("prefers the short Mega cooldown over the until-reset park when an aggregate error carries both tokens", async () => {
|
||||||
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-mega-both-"));
|
||||||
|
tempDirs.push(root);
|
||||||
|
const storagePaths = createStoragePaths(path.join(root, "state"));
|
||||||
|
initPackageLogs(storagePaths.baseDir);
|
||||||
|
initItemLogs(storagePaths.baseDir);
|
||||||
|
|
||||||
|
const session = emptySession();
|
||||||
|
const packageId = "mega-both-pkg";
|
||||||
|
const itemId = "mega-both-item";
|
||||||
|
const outputDir = path.join(root, "downloads", "MegaBoth");
|
||||||
|
const extractDir = path.join(root, "extract", "MegaBoth");
|
||||||
|
fs.mkdirSync(outputDir, { recursive: true });
|
||||||
|
fs.mkdirSync(extractDir, { recursive: true });
|
||||||
|
session.packageOrder = [packageId];
|
||||||
|
session.packages[packageId] = {
|
||||||
|
id: packageId, name: "MegaBoth", outputDir, extractDir,
|
||||||
|
status: "downloading", itemIds: [itemId], cancelled: false, enabled: true,
|
||||||
|
createdAt: Date.now(), updatedAt: Date.now()
|
||||||
|
} as any;
|
||||||
|
session.items[itemId] = {
|
||||||
|
id: itemId, packageId, url: "https://rapidgator.net/file/mega-both.rar", provider: "megadebrid-api",
|
||||||
|
status: "downloading", retries: 0, speedBps: 0, downloadedBytes: 0, totalBytes: null,
|
||||||
|
progressPercent: 0, fileName: "mega-both.rar", targetPath: "", resumable: true,
|
||||||
|
attempts: 0, lastError: "", fullStatus: "", createdAt: Date.now(), updatedAt: Date.now()
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
const manager = new DownloadManager(
|
||||||
|
{ ...defaultSettings(), megaLogin: "u", megaPassword: "p", megaCredentials: "u:p", megaDebridApiEnabled: true, megaDebridWebEnabled: true, outputDir: path.join(root, "downloads"), extractDir: path.join(root, "extract") },
|
||||||
|
session,
|
||||||
|
storagePaths
|
||||||
|
);
|
||||||
|
(manager as any).retryStateByItem.set(itemId, { freshRetryUsed: true, resumeHardResetUsed: true, stallRetries: 0, genericErrorRetries: 0, unrestrictRetries: 0 });
|
||||||
|
(manager as any).debridService.unrestrictLink = async () => {
|
||||||
|
throw new Error("Unrestrict fehlgeschlagen: Mega-Debrid API: mega_debrid_reset_park:86400000:Alle Accounts bis zum Tagesreset gesperrt | Mega-Debrid Web: mega_debrid_cooldown:30000:rate limit");
|
||||||
|
};
|
||||||
|
|
||||||
|
const active = { itemId, packageId, abortController: new AbortController(), abortReason: "none", resumable: true, nonResumableCounted: false, blockedOnDiskWrite: false, blockedOnDiskSince: 0 };
|
||||||
|
(manager as any).activeTasks.set(itemId, active);
|
||||||
|
|
||||||
|
const before = Date.now();
|
||||||
|
await (manager as any).processItem(active);
|
||||||
|
|
||||||
|
const retryAfter = (manager as any).retryAfterByItem.get(itemId) as number;
|
||||||
|
expect(retryAfter).toBeGreaterThan(0);
|
||||||
|
const delayMs = retryAfter - before;
|
||||||
|
expect(delayMs).toBeLessThan(60000);
|
||||||
|
expect(session.items[itemId].fullStatus).toMatch(/Cooldown/i);
|
||||||
|
expect(session.items[itemId].fullStatus).not.toMatch(/Tagesreset/i);
|
||||||
|
});
|
||||||
|
|
||||||
it("keeps the quick post-process requeue once the final package items are finished", () => {
|
it("keeps the quick post-process requeue once the final package items are finished", () => {
|
||||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-postprocess-final-"));
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-postprocess-final-"));
|
||||||
tempDirs.push(root);
|
tempDirs.push(root);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user