Fix: Mega-Debrid Einzel-Account friert bei 60s-Timeout nicht mehr die ganze Liste ein

Symptom (per Ferndiagnose live verifiziert): Bei nur EINEM Mega-Debrid-Account lief
der Download eine Weile sauber, dann standen schlagartig ALLE Items ~120s im
"Mega-Debrid Cooldown" — obwohl der Account voellig gesund war (andere Links loesten
zeitgleich in 13-18s auf). Jede Cooldown-Zeile zeigte exakt dieselbe Deadline
(20:52:55.901) -> ein einziger account-weiter Cooldown, einmal gesetzt.

Ursache: Laeuft eine Web-Aufloesung laenger als das 60s-Gesamt-Timeout, feuert der
Abbruch-Pfad (debrid.ts) einen 120s-Account-Cooldown. Dessen einziger Zweck (laut
Code-Kommentar) ist, den Retry auf den NAECHSTEN Account rotieren zu lassen. Bei nur
einem Account gibt es keinen naechsten -> stattdessen findet jedes folgende Item den
einzigen Account im Cooldown und wird bis zu 120s geparkt -> die ganze Liste steht.
Ein 60s-Timeout ist ein Signal fuer einen LANGSAMEN LINK, nicht fuer einen
ungesunden Account.

Fix: Der Account-Cooldown wird nur noch gesetzt, wenn es tatsaechlich einen anderen
nutzbaren Account zum Rotieren gibt. Ohne Rotationsziel (Einzel-Account / alle
anderen belegt) wird der Account NICHT mehr eingefroren; stattdessen wird nur der
langsame Link selbst geparkt (mega_debrid_slow_link -> Item-Retry), waehrend alle
anderen Items weiter ueber den gesunden Account laufen. Das Mehr-Account-Verhalten
(Rotation per Account-Cooldown) bleibt unveraendert.

Der baugleiche Debrid-Link-Pfad (Einzel-Key, debrid.ts) ist derselbe Muster-Typ,
aber ein separater, hier nicht genutzter Provider mit eigener Nachbehandlung -
bewusst nicht mitgebuendelt.

Tests: debrid.test.ts (Einzel-Account-Abbruch parkt nur den Link, KEIN
Account-Cooldown, zweites Item loest weiter auf) + unrestrict-retry.test.ts
(parseMegaDebridSlowLinkRetry, keine Token-Kollision). Suite 934 gruen, tsc 6.
This commit is contained in:
Sucukdeluxe
2026-06-21 21:21:34 +02:00
parent d594c5082b
commit 26df55f7ba
4 changed files with 139 additions and 11 deletions
+49
View File
@@ -2071,6 +2071,55 @@ describe("debrid service", () => {
expect(calls).toBeGreaterThanOrEqual(1);
}, 20000);
it("single Mega-Debrid account: a long Web abort parks only the slow link and does NOT freeze the sole account", async () => {
process.env.RD_MEGA_ABORT_MIN_RUN_MS = "0";
const settings = {
...defaultSettings(),
token: "",
bestToken: "",
allDebridToken: "",
megaLogin: "user",
megaPassword: "pass",
megaCredentials: "user:pass",
megaDebridPreferApi: false,
providerOrder: [] as const,
providerPrimary: "megadebrid" as const,
providerSecondary: "none" as const,
providerTertiary: "none" as const,
autoProviderFallback: false
};
globalThis.fetch = (async () => new Response("error", { status: 500 })) as typeof fetch;
const controller = new AbortController();
let calls = 0;
const megaWeb = vi.fn((): Promise<{ fileName: string; directUrl: string; fileSize: number | null; retriesUsed: number }> => {
calls += 1;
if (calls === 1) {
controller.abort("simulated-60s-timeout");
return Promise.reject(new Error("aborted"));
}
return Promise.resolve({
fileName: "healthy.rar",
directUrl: "https://www11.unrestrict.link/download/file/ok/healthy.rar",
fileSize: null,
retriesUsed: 0
});
});
const service = new DebridService(settings, { megaWebUnrestrict: megaWeb });
const err = await service.unrestrictLink("https://rapidgator.net/file/slow-link.rar.html", controller.signal).then(() => null, (e: unknown) => e);
expect(err).toBeTruthy();
expect(String(err)).toMatch(/mega_debrid_slow_link:\d+:/i);
const key = `${getMegaDebridAccountId("user")}:web`;
expect(getMegaDebridAccountCooldownState(key)).toBeNull();
const second = await service.unrestrictLink("https://rapidgator.net/file/healthy.rar.html");
expect(second.provider).toBe("megadebrid");
expect(calls).toBeGreaterThanOrEqual(2);
}, 20000);
it("escalates a Mega-Debrid account to 'until restart' after the empty-response streak threshold", () => {
const key = `${getMegaDebridAccountId("user1")}:web`;
expect(MEGA_DEBRID_EMPTY_STREAK_UNTIL_RESTART).toBe(3);