Compare commits
No commits in common. "73f871bfcb7b4328252c4ddee6fd7182f82e073d" and "b60b2b00f3e6dbb229ebec3feee98b1b33a77fba" have entirely different histories.
73f871bfcb
...
b60b2b00f3
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "real-debrid-downloader",
|
||||
"version": "1.7.198",
|
||||
"version": "1.7.197",
|
||||
"description": "Desktop downloader",
|
||||
"main": "build/main/main/main.js",
|
||||
"author": "Sucukdeluxe",
|
||||
|
||||
@ -298,22 +298,6 @@ export const MEGA_DEBRID_EMPTY_STREAK_UNTIL_RESTART = 3;
|
||||
|
||||
let megaDebridRotationCursor = 0;
|
||||
|
||||
const MEGA_DEBRID_SLOW_ACCOUNT_FLOOR_MS = 6000;
|
||||
const MEGA_DEBRID_SLOW_ACCOUNT_FACTOR = 3;
|
||||
const megaDebridAccountLatencyEma = new Map<string, number>();
|
||||
|
||||
function recordMegaDebridUnrestrictLatency(cooldownKey: string, elapsedMs: number): void {
|
||||
if (!Number.isFinite(elapsedMs) || elapsedMs < 0) {
|
||||
return;
|
||||
}
|
||||
const prev = megaDebridAccountLatencyEma.get(cooldownKey);
|
||||
megaDebridAccountLatencyEma.set(cooldownKey, prev === undefined ? elapsedMs : prev * 0.7 + elapsedMs * 0.3);
|
||||
}
|
||||
|
||||
export function primeMegaDebridLatencyForTests(cooldownKey: string, emaMs: number): void {
|
||||
megaDebridAccountLatencyEma.set(cooldownKey, emaMs);
|
||||
}
|
||||
|
||||
export function recordMegaDebridEmptyResponseStreak(accountId: string): number {
|
||||
const streak = (megaDebridEmptyResponseStreaks.get(accountId) || 0) + 1;
|
||||
megaDebridEmptyResponseStreaks.set(accountId, streak);
|
||||
@ -328,7 +312,6 @@ export function resetMegaDebridRuntimeStateForTests(): void {
|
||||
megaDebridAccountCooldowns.clear();
|
||||
megaDebridEmptyResponseStreaks.clear();
|
||||
megaDebridRotationCursor = 0;
|
||||
megaDebridAccountLatencyEma.clear();
|
||||
}
|
||||
|
||||
export function pruneExpiredMegaDebridRuntimeState(now = Date.now()): number {
|
||||
@ -1932,48 +1915,9 @@ class MegaDebridClient {
|
||||
// wurde nie auch nur angeschaut. Der Cursor laesst jede Aufloesung beim Account
|
||||
// NACH dem zuletzt getesteten starten, alle Skip-/Cooldown-Checks bleiben.
|
||||
const startOffset = ((megaDebridRotationCursor % accounts.length) + accounts.length) % accounts.length;
|
||||
const rotationOrder: { account: MegaDebridAccountEntry; idx: number }[] = [];
|
||||
for (let step = 0; step < accounts.length; step += 1) {
|
||||
const idx = (startOffset + step) % accounts.length;
|
||||
rotationOrder.push({ account: accounts[idx], idx });
|
||||
}
|
||||
|
||||
// Latenz-Schutz fuer die Mega-Web-Single-Flight-Queue: dort laufen ALLE
|
||||
// Umwandlungen seriell, ein nachweislich langsamer Account bremst also
|
||||
// jeden nachfolgenden Link aus. Accounts mit deutlich erhoehtem Erfolgs-EMA
|
||||
// wandern ans Ende der Reihenfolge (= Failover-Reserve) statt in der
|
||||
// gleichmaessigen Rotation mitzulaufen; erholt sich ihr EMA, rotieren sie
|
||||
// automatisch wieder mit. Ungemessene Accounts gelten als gesund.
|
||||
let bestEma = Infinity;
|
||||
let anyUnmeasuredUsable = false;
|
||||
for (const entry of rotationOrder) {
|
||||
if (isMegaDebridAccountDisabled(settings, entry.account.id)) continue;
|
||||
if (isMegaDebridAccountDailyLimitReached(settings, entry.account.id)) continue;
|
||||
if (getMegaDebridAccountCooldownState(`${entry.account.id}:${mode}`)) continue;
|
||||
const ema = megaDebridAccountLatencyEma.get(`${entry.account.id}:${mode}`);
|
||||
if (ema === undefined) {
|
||||
anyUnmeasuredUsable = true;
|
||||
continue;
|
||||
}
|
||||
if (ema < bestEma) bestEma = ema;
|
||||
}
|
||||
const slowThreshold = Math.max(
|
||||
MEGA_DEBRID_SLOW_ACCOUNT_FLOOR_MS,
|
||||
anyUnmeasuredUsable ? 0 : (Number.isFinite(bestEma) ? MEGA_DEBRID_SLOW_ACCOUNT_FACTOR * bestEma : Infinity)
|
||||
);
|
||||
const isSlowEntry = (entry: { account: MegaDebridAccountEntry }): boolean => {
|
||||
const ema = megaDebridAccountLatencyEma.get(`${entry.account.id}:${mode}`);
|
||||
return ema !== undefined && ema > slowThreshold;
|
||||
};
|
||||
const orderedEntries = [
|
||||
...rotationOrder.filter((entry) => !isSlowEntry(entry)),
|
||||
...rotationOrder.filter((entry) => isSlowEntry(entry))
|
||||
];
|
||||
|
||||
for (let orderPos = 0; orderPos < orderedEntries.length; orderPos += 1) {
|
||||
const entry = orderedEntries[orderPos];
|
||||
const account = entry.account;
|
||||
const idx = entry.idx;
|
||||
const account = accounts[idx];
|
||||
const accountLabel = ` (${account.label}/${totalAccounts}, ${account.maskedLogin})`;
|
||||
const rotationLabel = `${account.label}/${totalAccounts} (${account.maskedLogin})`;
|
||||
|
||||
@ -2011,13 +1955,8 @@ class MegaDebridClient {
|
||||
continue;
|
||||
}
|
||||
|
||||
const emaForLog = megaDebridAccountLatencyEma.get(cooldownKey);
|
||||
logger.info(`Mega-Debrid${accountLabel}: TESTE Account fuer Link-Generierung...`);
|
||||
logAccountRotation("INFO", providerName, rotationLabel, "TEST", {
|
||||
link: linkShort,
|
||||
emaMs: emaForLog !== undefined ? Math.round(emaForLog) : undefined,
|
||||
slow: isSlowEntry(entry) ? true : undefined
|
||||
});
|
||||
logAccountRotation("INFO", providerName, rotationLabel, "TEST", { link: linkShort });
|
||||
const testStartedAt = Date.now();
|
||||
|
||||
usableAccountSeen = true;
|
||||
@ -2028,7 +1967,6 @@ class MegaDebridClient {
|
||||
clearMegaDebridAccountCooldownState(cooldownKey);
|
||||
clearMegaDebridEmptyResponseStreak(cooldownKey);
|
||||
const elapsedMs = Date.now() - testStartedAt;
|
||||
recordMegaDebridUnrestrictLatency(cooldownKey, elapsedMs);
|
||||
logger.info(`Mega-Debrid${accountLabel}: Unrestrict OK nach ${elapsedMs}ms -> ${result.fileName || "?"}`);
|
||||
logAccountRotation("INFO", providerName, rotationLabel, "OK", {
|
||||
elapsedMs,
|
||||
@ -2101,8 +2039,8 @@ class MegaDebridClient {
|
||||
? `, Cooldown ${Math.ceil(failure.cooldownMs / 1000)}s`
|
||||
: "";
|
||||
let nextLabel = "ENDE";
|
||||
for (let nextPos = orderPos + 1; nextPos < orderedEntries.length; nextPos += 1) {
|
||||
const nextAcc = orderedEntries[nextPos].account;
|
||||
for (let nextStep = step + 1; nextStep < accounts.length; nextStep += 1) {
|
||||
const nextAcc = accounts[(startOffset + nextStep) % accounts.length];
|
||||
if (!isMegaDebridAccountDisabled(settings, nextAcc.id) && !isMegaDebridAccountDailyLimitReached(settings, nextAcc.id) && !getMegaDebridAccountCooldownState(`${nextAcc.id}:${mode}`)) {
|
||||
nextLabel = `${nextAcc.label}/${totalAccounts} (${nextAcc.maskedLogin})`;
|
||||
break;
|
||||
|
||||
@ -3,7 +3,7 @@ import { defaultSettings, REQUEST_RETRIES } from "../src/main/constants";
|
||||
import { parseDebridLinkApiKeys } from "../src/shared/debrid-link-keys";
|
||||
import { getMegaDebridAccountId } from "../src/shared/mega-debrid-accounts";
|
||||
import { getProviderUsageDayKey } from "../src/shared/provider-daily-limits";
|
||||
import { clearMegaDebridEmptyResponseStreak, DebridService, extractRapidgatorFilenameFromHtml, fetchAllDebridHostInfo, fetchDebridLinkHostLimits, filenameFromRapidgatorUrlPath, getDebridLinkKeyRuntimeStateForTests, getMegaDebridAccountCooldownState, MEGA_DEBRID_EMPTY_STREAK_UNTIL_RESTART, normalizeResolvedFilename, primeMegaDebridLatencyForTests, primeMegaDebridUntilRestartForTests, recordMegaDebridEmptyResponseStreak, resetDebridLinkRuntimeStateForTests, resetMegaDebridRuntimeStateForTests } from "../src/main/debrid";
|
||||
import { clearMegaDebridEmptyResponseStreak, DebridService, extractRapidgatorFilenameFromHtml, fetchAllDebridHostInfo, fetchDebridLinkHostLimits, filenameFromRapidgatorUrlPath, getDebridLinkKeyRuntimeStateForTests, getMegaDebridAccountCooldownState, MEGA_DEBRID_EMPTY_STREAK_UNTIL_RESTART, normalizeResolvedFilename, primeMegaDebridUntilRestartForTests, recordMegaDebridEmptyResponseStreak, resetDebridLinkRuntimeStateForTests, resetMegaDebridRuntimeStateForTests } from "../src/main/debrid";
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
|
||||
@ -1463,84 +1463,6 @@ describe("debrid service", () => {
|
||||
]);
|
||||
}, 30000);
|
||||
|
||||
it("depriorisiert einen nachweislich langsamen Account in der Rotation (Single-Flight-Schutz)", async () => {
|
||||
const settings = {
|
||||
...defaultSettings(),
|
||||
token: "",
|
||||
bestToken: "",
|
||||
allDebridToken: "",
|
||||
megaLogin: "user1",
|
||||
megaPassword: "pass1",
|
||||
megaCredentials: "user1:pass1\nuser2:pass2",
|
||||
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 megaWeb = vi.fn(async () => ({
|
||||
fileName: "ok.rar",
|
||||
directUrl: "https://mega-web.example/ok.rar",
|
||||
fileSize: null,
|
||||
retriesUsed: 0
|
||||
}));
|
||||
|
||||
primeMegaDebridLatencyForTests(`${getMegaDebridAccountId("user1")}:web`, 15000);
|
||||
primeMegaDebridLatencyForTests(`${getMegaDebridAccountId("user2")}:web`, 800);
|
||||
|
||||
const service = new DebridService(settings, { megaWebUnrestrict: megaWeb });
|
||||
const usedIds: (string | undefined)[] = [];
|
||||
for (let i = 0; i < 2; i += 1) {
|
||||
const result = await service.unrestrictLink(`https://rapidgator.net/file/slow-${i}`);
|
||||
usedIds.push((result as { sourceAccountId?: string }).sourceAccountId);
|
||||
}
|
||||
|
||||
expect(usedIds).toEqual([
|
||||
getMegaDebridAccountId("user2"),
|
||||
getMegaDebridAccountId("user2")
|
||||
]);
|
||||
}, 30000);
|
||||
|
||||
it("nutzt den langsamen Account weiterhin, wenn die schnellen gesperrt sind (Failover-Reserve)", async () => {
|
||||
const settings = {
|
||||
...defaultSettings(),
|
||||
token: "",
|
||||
bestToken: "",
|
||||
allDebridToken: "",
|
||||
megaLogin: "user1",
|
||||
megaPassword: "pass1",
|
||||
megaCredentials: "user1:pass1\nuser2:pass2",
|
||||
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 megaWeb = vi.fn(async () => ({
|
||||
fileName: "ok.rar",
|
||||
directUrl: "https://mega-web.example/ok.rar",
|
||||
fileSize: null,
|
||||
retriesUsed: 0
|
||||
}));
|
||||
|
||||
primeMegaDebridLatencyForTests(`${getMegaDebridAccountId("user1")}:web`, 15000);
|
||||
primeMegaDebridLatencyForTests(`${getMegaDebridAccountId("user2")}:web`, 800);
|
||||
primeMegaDebridUntilRestartForTests(`${getMegaDebridAccountId("user2")}:web`);
|
||||
|
||||
const service = new DebridService(settings, { megaWebUnrestrict: megaWeb });
|
||||
const result = await service.unrestrictLink("https://rapidgator.net/file/slow-fallback");
|
||||
|
||||
expect((result as { sourceAccountId?: string }).sourceAccountId).toBe(getMegaDebridAccountId("user1"));
|
||||
}, 30000);
|
||||
|
||||
it("rotates to the next Mega-Debrid account when one hits its daily limit (error-based)", async () => {
|
||||
const settings = {
|
||||
...defaultSettings(),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user