Release v1.4.37 with DLC filenames, instant delete, version display

- Parse <filename> tags from DLC XML for proper file names instead of
  deriving from opaque URLs (fixes download.bin display)
- Optimistic UI removal for package/item delete (instant feedback)
- Show app version in header ("Multi Debrid Downloader vX.X.X")

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sucukdeluxe
2026-03-01 02:12:06 +01:00
co-authored by Claude Opus 4.6
parent a0c58aad2c
commit d491c21b97
6 changed files with 101 additions and 12 deletions
+2 -1
View File
@@ -165,7 +165,8 @@ export class AppController {
const packages = await importDlcContainers(filePaths);
const merged: ParsedPackageInput[] = packages.map((pkg) => ({
name: pkg.name,
links: pkg.links
links: pkg.links,
...(pkg.fileNames ? { fileNames: pkg.fileNames } : {})
}));
const result = this.manager.addPackages(merged);
return result;
+43 -7
View File
@@ -94,24 +94,60 @@ function parsePackagesFromDlcXml(xml: string): ParsedPackageInput[] {
}
const links: string[] = [];
const urlRegex = /<url>(.*?)<\/url>/gi;
for (let um = urlRegex.exec(packageBody); um; um = urlRegex.exec(packageBody)) {
const fileNames: string[] = [];
const fileRegex = /<file>([\s\S]*?)<\/file>/gi;
for (let fm = fileRegex.exec(packageBody); fm; fm = fileRegex.exec(packageBody)) {
const fileBody = fm[1] || "";
const urlMatch = fileBody.match(/<url>(.*?)<\/url>/i);
if (!urlMatch) {
continue;
}
try {
const url = Buffer.from((um[1] || "").trim(), "base64").toString("utf8").trim();
if (isHttpLink(url)) {
links.push(url);
const url = Buffer.from((urlMatch[1] || "").trim(), "base64").toString("utf8").trim();
if (!isHttpLink(url)) {
continue;
}
let fileName = "";
const fnMatch = fileBody.match(/<filename>(.*?)<\/filename>/i);
if (fnMatch?.[1]) {
try {
fileName = Buffer.from(fnMatch[1].trim(), "base64").toString("utf8").trim();
} catch {
// ignore
}
}
links.push(url);
fileNames.push(sanitizeFilename(fileName));
} catch {
// skip broken entries
}
}
if (links.length === 0) {
const urlRegex = /<url>(.*?)<\/url>/gi;
for (let um = urlRegex.exec(packageBody); um; um = urlRegex.exec(packageBody)) {
try {
const url = Buffer.from((um[1] || "").trim(), "base64").toString("utf8").trim();
if (isHttpLink(url)) {
links.push(url);
}
} catch {
// skip broken entries
}
}
}
const uniqueLinks = uniquePreserveOrder(links);
const hasFileNames = fileNames.some((fn) => fn.length > 0);
if (uniqueLinks.length > 0) {
packages.push({
const pkg: ParsedPackageInput = {
name: sanitizeFilename(packageName || inferPackageNameFromLinks(uniqueLinks) || `Paket-${packages.length + 1}`),
links: uniqueLinks
});
};
if (hasFileNames) {
pkg.fileNames = fileNames;
}
packages.push(pkg);
}
}
+4 -2
View File
@@ -974,9 +974,11 @@ export class DownloadManager extends EventEmitter {
updatedAt: nowMs()
};
for (const link of links) {
for (let linkIdx = 0; linkIdx < links.length; linkIdx += 1) {
const link = links[linkIdx];
const itemId = uuidv4();
const fileName = filenameFromUrl(link);
const hintName = pkg.fileNames?.[linkIdx];
const fileName = (hintName && !looksLikeOpaqueFilename(hintName)) ? sanitizeFilename(hintName) : filenameFromUrl(link);
const item: DownloadItem = {
id: itemId,
packageId,