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:
@@ -605,7 +605,7 @@ export function getAuthoritativeRealDebridTotal(
|
||||
if (!resumeHardResetUsed || source !== "content-length") {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1696,14 +1696,27 @@ function formatExtractFailureLabel(reason: string, archiveName = ""): string {
|
||||
: `Entpack-Fehler: ${summary}`;
|
||||
}
|
||||
|
||||
function retryDelayWithJitter(attempt: number, baseMs: number): number {
|
||||
function retryDelayWithJitter(attempt: number, baseMs: number): number {
|
||||
const exponential = baseMs * Math.pow(1.5, Math.min(attempt - 1, 14));
|
||||
const capped = Math.min(exponential, 120000);
|
||||
const jitter = capped * (0.5 + Math.random() * 0.5);
|
||||
return Math.floor(jitter);
|
||||
}
|
||||
|
||||
export class DownloadManager extends EventEmitter {
|
||||
return Math.floor(jitter);
|
||||
}
|
||||
|
||||
export async function runWithLimitedConcurrency<T>(items: readonly T[], concurrency: number, worker: (item: T) => Promise<void>): Promise<void> {
|
||||
const workerCount = Math.min(items.length, Math.max(1, Math.floor(concurrency)));
|
||||
let nextIndex = 0;
|
||||
const workers = Array.from({ length: workerCount }, async () => {
|
||||
while (nextIndex < items.length) {
|
||||
const item = items[nextIndex];
|
||||
nextIndex += 1;
|
||||
await worker(item);
|
||||
}
|
||||
});
|
||||
await Promise.all(workers);
|
||||
}
|
||||
|
||||
export class DownloadManager extends EventEmitter {
|
||||
private settings: AppSettings;
|
||||
|
||||
private session: SessionState;
|
||||
@@ -3278,31 +3291,28 @@ export class DownloadManager extends EventEmitter {
|
||||
this.emitState();
|
||||
}
|
||||
|
||||
const checkedUrls = new Map<string, Awaited<ReturnType<typeof checkRapidgatorOnline>>>();
|
||||
|
||||
for (const { itemId, url } of itemsToCheck) {
|
||||
const item = this.session.items[itemId];
|
||||
if (!item) continue;
|
||||
|
||||
if (checkedUrls.has(url)) {
|
||||
const cached = checkedUrls.get(url);
|
||||
if (cached !== undefined) {
|
||||
this.applyRapidgatorCheckResult(item, cached);
|
||||
}
|
||||
this.emitState();
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await checkRapidgatorOnline(url);
|
||||
checkedUrls.set(url, result);
|
||||
this.applyRapidgatorCheckResult(item, result);
|
||||
} catch (err) {
|
||||
logger.warn(`checkRapidgatorOnline Fehler für ${url}: ${compactErrorText(err)}`);
|
||||
item.onlineStatus = undefined;
|
||||
}
|
||||
this.emitState();
|
||||
}
|
||||
const checkedUrls = new Map<string, ReturnType<typeof checkRapidgatorOnline>>();
|
||||
|
||||
await runWithLimitedConcurrency(itemsToCheck, 8, async ({ itemId, url }) => {
|
||||
const item = this.session.items[itemId];
|
||||
if (!item) return;
|
||||
|
||||
let check = checkedUrls.get(url);
|
||||
if (!check) {
|
||||
check = checkRapidgatorOnline(url);
|
||||
checkedUrls.set(url, check);
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await check;
|
||||
this.applyRapidgatorCheckResult(item, result);
|
||||
} catch (err) {
|
||||
logger.warn(`checkRapidgatorOnline Fehler für ${url}: ${compactErrorText(err)}`);
|
||||
item.onlineStatus = undefined;
|
||||
}
|
||||
this.persistSoon();
|
||||
this.emitState();
|
||||
});
|
||||
|
||||
this.persistSoon();
|
||||
}
|
||||
@@ -3354,8 +3364,11 @@ export class DownloadManager extends EventEmitter {
|
||||
if (result.fileName && looksLikeOpaqueFilename(item.fileName)) {
|
||||
item.fileName = sanitizeFilename(result.fileName);
|
||||
this.assignItemTargetPath(item, path.join(this.session.packages[item.packageId]?.outputDir || this.settings.outputDir, item.fileName));
|
||||
}
|
||||
item.onlineStatus = "online";
|
||||
}
|
||||
if (result.fileSizeBytes !== null && result.fileSizeBytes > 0) {
|
||||
item.totalBytes = result.fileSizeBytes;
|
||||
}
|
||||
item.onlineStatus = "online";
|
||||
item.updatedAt = nowMs();
|
||||
}
|
||||
}
|
||||
@@ -3364,7 +3377,8 @@ export class DownloadManager extends EventEmitter {
|
||||
const uncheckedIds: string[] = [];
|
||||
for (const item of Object.values(this.session.items)) {
|
||||
if (item.status !== "queued") continue;
|
||||
if (item.onlineStatus) continue;
|
||||
if (item.onlineStatus === "offline") continue;
|
||||
if (item.onlineStatus === "online" && item.totalBytes !== null && item.totalBytes > 0) continue;
|
||||
try {
|
||||
const host = new URL(item.url).hostname.toLowerCase();
|
||||
if (host !== "rapidgator.net" && !host.endsWith(".rapidgator.net") && host !== "rg.to" && !host.endsWith(".rg.to")) continue;
|
||||
|
||||
Reference in New Issue
Block a user