release: publish Multi-Debrid Downloader v2.0.14

Add live English and German localization, queue availability and metadata resolution, responsive package controls, polished navigation and drag interactions, clearer history and account states, and a rebuilt public README. Harden Windows packaging with verified icons and version metadata, archive inspection, and expanded release tests.
This commit is contained in:
Sucukdeluxe
2026-08-10 19:42:49 +02:00
parent 069babfd54
commit a5758aa905
61 changed files with 9117 additions and 6754 deletions
+43 -2
View File
@@ -6,7 +6,7 @@ import crypto from "node:crypto";
import { EventEmitter, once } from "node:events";
import AdmZip from "adm-zip";
import { afterEach, describe, expect, it, vi } from "vitest";
import { DownloadManager, buildAutoRenameBaseNameFromFoldersWithOptions, extractArchiveNameFromExtractorLogMessage, getAuthoritativeRealDebridTotal, resolveArchiveItemsFromList } from "../src/main/download-manager";
import { DownloadManager, buildAutoRenameBaseNameFromFoldersWithOptions, extractArchiveNameFromExtractorLogMessage, getAuthoritativeRealDebridTotal, resolveArchiveItemsFromList, runWithLimitedConcurrency } from "../src/main/download-manager";
import { planDownloadCompletion, validateDownloadedFileCompletion } from "../src/main/download-completion";
import { defaultSettings } from "../src/main/constants";
import { parseDebridLinkApiKeys } from "../src/shared/debrid-link-keys";
@@ -20,7 +20,26 @@ import { getRenameLogPath, initRenameLog, shutdownRenameLog } from "../src/main/
import { UnrestrictedLink } from "../src/main/realdebrid";
const tempDirs: string[] = [];
const originalFetch = globalThis.fetch;
const originalFetch = globalThis.fetch;
describe("runWithLimitedConcurrency", () => {
it("processes the full batch without exceeding the configured worker count", async () => {
let active = 0;
let peak = 0;
const completed: number[] = [];
await runWithLimitedConcurrency([1, 2, 3, 4, 5, 6], 3, async (value) => {
active += 1;
peak = Math.max(peak, active);
await new Promise((resolve) => setTimeout(resolve, 5));
completed.push(value);
active -= 1;
});
expect(peak).toBe(3);
expect(completed.sort((a, b) => a - b)).toEqual([1, 2, 3, 4, 5, 6]);
});
});
describe("extractArchiveNameFromExtractorLogMessage", () => {
it("detects archive names from extractor log variants", () => {
@@ -166,6 +185,28 @@ afterEach(async () => {
});
describe("download manager", () => {
it("stores RapidGator metadata before a download starts", () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
tempDirs.push(root);
const manager = new DownloadManager(defaultSettings(), emptySession(), createStoragePaths(path.join(root, "state")));
manager.addPackages([{ name: "metadata", links: ["https://rapidgator.net/file/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"] }]);
const snapshot = manager.getSnapshot();
const itemId = snapshot.session.packages[snapshot.session.packageOrder[0]].itemIds[0];
const internal = manager as unknown as {
session: typeof snapshot.session;
applyRapidgatorCheckResult: (item: typeof snapshot.session.items[string], result: { online: boolean; fileName: string; fileSizeBytes: number | null }) => void;
};
internal.applyRapidgatorCheckResult(internal.session.items[itemId], {
online: true,
fileName: "episode.part01.rar",
fileSizeBytes: 1_610_612_736
});
expect(internal.session.items[itemId].totalBytes).toBe(1_610_612_736);
expect(internal.session.items[itemId].status).toBe("queued");
});
it("applies an imported settings snapshot without touching queued items or filesystem workflows", () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-settings-import-"));
tempDirs.push(root);