Throttle Mega-Debrid Web validation starts

This commit is contained in:
Sucukdeluxe 2026-03-09 04:21:56 +01:00
parent 9cbf917140
commit a3e3d6faf7
2 changed files with 107 additions and 2 deletions

View File

@ -5750,6 +5750,31 @@ export class DownloadManager extends EventEmitter {
return count; return count;
} }
private getProviderValidatingTaskCount(provider: DebridProvider, excludeItemId?: string): number {
let count = 0;
for (const active of this.activeTasks.values()) {
if (excludeItemId && active.itemId === excludeItemId) {
continue;
}
const activeItem = this.session.items[active.itemId];
if (!activeItem || activeItem.status !== "validating") {
continue;
}
const expectedProvider = resolveMegaDebridProvider(this.settings, this.getExpectedProviderForItem(activeItem));
if (expectedProvider === provider) {
count += 1;
}
}
return count;
}
private getSerializedValidatingLimit(provider: DebridProvider | null): number {
if (provider === "megadebrid-web") {
return 1;
}
return Number.MAX_SAFE_INTEGER;
}
private delayPacedStartForItem(item: DownloadItem, now: number): boolean { private delayPacedStartForItem(item: DownloadItem, now: number): boolean {
const paceKey = this.getPacedStartKeyForItem(item); const paceKey = this.getPacedStartKeyForItem(item);
if (!paceKey) { if (!paceKey) {
@ -5842,7 +5867,11 @@ export class DownloadManager extends EventEmitter {
} }
private shouldDelayStartForItem(item: DownloadItem): boolean { private shouldDelayStartForItem(item: DownloadItem): boolean {
const provider = this.getExpectedProviderForItem(item); const provider = resolveMegaDebridProvider(this.settings, this.getExpectedProviderForItem(item));
const serializedValidatingLimit = this.getSerializedValidatingLimit(provider);
if (provider && Number.isFinite(serializedValidatingLimit) && serializedValidatingLimit < Number.MAX_SAFE_INTEGER) {
return this.getProviderValidatingTaskCount(provider, item.id) >= serializedValidatingLimit;
}
if (provider !== "alldebrid") { if (provider !== "alldebrid") {
return false; return false;
} }

View File

@ -4,7 +4,7 @@ import path from "node:path";
import http from "node:http"; import http from "node:http";
import { EventEmitter, once } from "node:events"; import { EventEmitter, once } from "node:events";
import AdmZip from "adm-zip"; import AdmZip from "adm-zip";
import { afterEach, describe, expect, it } from "vitest"; import { afterEach, describe, expect, it, vi } from "vitest";
import { DownloadManager, extractArchiveNameFromExtractorLogMessage, getAuthoritativeRealDebridTotal } from "../src/main/download-manager"; import { DownloadManager, extractArchiveNameFromExtractorLogMessage, getAuthoritativeRealDebridTotal } from "../src/main/download-manager";
import { defaultSettings } from "../src/main/constants"; import { defaultSettings } from "../src/main/constants";
import { parseDebridLinkApiKeys } from "../src/shared/debrid-link-keys"; import { parseDebridLinkApiKeys } from "../src/shared/debrid-link-keys";
@ -5031,6 +5031,82 @@ describe("download manager", () => {
} }
}, 20000); }, 20000);
it("limits Mega-Debrid Web validating starts to one item at a time", async () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
tempDirs.push(root);
let unrestrictCalls = 0;
const pendingRejectors = new Set<(error: Error) => void>();
const manager = new DownloadManager(
{
...defaultSettings(),
megaLogin: "mega-user",
megaPassword: "mega-pass",
megaDebridWebEnabled: true,
megaDebridApiEnabled: false,
megaDebridPreferApi: false,
providerOrder: [],
providerPrimary: "megadebrid",
providerSecondary: "none",
providerTertiary: "none",
outputDir: path.join(root, "downloads"),
extractDir: path.join(root, "extract"),
autoExtract: false,
autoReconnect: false,
enableIntegrityCheck: false,
maxParallel: 3
},
emptySession(),
createStoragePaths(path.join(root, "state")),
{
megaWebUnrestrict: vi.fn(async (_link: string, signal?: AbortSignal) => {
unrestrictCalls += 1;
return await new Promise((resolve, reject) => {
const rejector = (error: Error): void => {
signal?.removeEventListener("abort", onAbort);
pendingRejectors.delete(rejector);
reject(error);
};
const onAbort = (): void => {
rejector(new Error("aborted:test-mega-web"));
};
if (signal?.aborted) {
onAbort();
return;
}
signal?.addEventListener("abort", onAbort, { once: true });
pendingRejectors.add(rejector);
});
})
}
);
manager.addPackages([{
name: "mega-web-serialized",
links: [
"https://rapidgator.net/file/mega-web-1.part1.rar.html",
"https://rapidgator.net/file/mega-web-2.part2.rar.html",
"https://rapidgator.net/file/mega-web-3.part3.rar.html"
]
}]);
await manager.start();
await waitFor(() => unrestrictCalls === 1, 10000);
await new Promise((resolve) => setTimeout(resolve, 250));
const items = Object.values(manager.getSnapshot().session.items);
expect(items.filter((item) => item.status === "validating")).toHaveLength(1);
expect(items.filter((item) => item.status === "queued")).toHaveLength(2);
expect(unrestrictCalls).toBe(1);
manager.stop();
for (const reject of Array.from(pendingRejectors)) {
reject(new Error("aborted:test-mega-web"));
}
await new Promise((resolve) => setTimeout(resolve, 150));
});
it("shows the same AllDebrid countdown for all immediately free slots", async () => { it("shows the same AllDebrid countdown for all immediately free slots", async () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-")); const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
tempDirs.push(root); tempDirs.push(root);