From c898c6de65e865801e71f302f44e5b5bb7a0e3c8 Mon Sep 17 00:00:00 2001 From: Sucukdeluxe Date: Mon, 9 Mar 2026 01:36:08 +0100 Subject: [PATCH] Mirror extractor logs to item logs --- src/main/download-manager.ts | 43 ++++++++++++++++++++++++++++++++-- tests/download-manager.test.ts | 14 ++++++++++- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/src/main/download-manager.ts b/src/main/download-manager.ts index 67f332c..4e96db2 100644 --- a/src/main/download-manager.ts +++ b/src/main/download-manager.ts @@ -1159,6 +1159,26 @@ export function resolveArchiveItemsFromList(archiveName: string, items: Download return []; } +export function extractArchiveNameFromExtractorLogMessage(message: string): string | null { + const text = String(message || "").trim(); + if (!text) { + return null; + } + + const patterns = [ + /archive=([^,\s|]+)/i, + /Entpacke Archiv:\s*([^\s]+)\s*->/i, + /Entpack-Fehler\s+([^\s]+)\s+\[/i + ]; + for (const pattern of patterns) { + const match = text.match(pattern); + if (match?.[1]) { + return match[1].trim(); + } + } + return null; +} + 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); @@ -1368,6 +1388,25 @@ export class DownloadManager extends EventEmitter { }); } + private logExtractionForItems( + pkg: PackageEntry, + items: DownloadItem[], + prefix: string, + level: "INFO" | "WARN" | "ERROR", + message: string + ): void { + const fullMessage = `${prefix}: ${message}`; + this.logPackageForPackage(pkg, level, fullMessage); + + const archiveName = extractArchiveNameFromExtractorLogMessage(message); + if (!archiveName) { + return; + } + for (const item of resolveArchiveItemsFromList(archiveName, items)) { + this.logPackageForItem(item, level, fullMessage, { archiveName }); + } + } + private logPackageForItem( item: DownloadItem, level: "INFO" | "WARN" | "ERROR", @@ -8714,7 +8753,7 @@ export class DownloadManager extends EventEmitter { hybridMode: true, maxParallel: this.settings.maxParallelExtract || 2, extractCpuPriority: "high", - onLog: (level, message) => this.logPackageForPackage(pkg, level, `Hybrid-Extractor: ${message}`), + onLog: (level, message) => this.logExtractionForItems(pkg, items, "Hybrid-Extractor", level, message), onArchiveFailure: (failure) => { const failedArchiveKey = readyArchiveKeyByName.get(String(failure.archiveName || "").toLowerCase()); if (failedArchiveKey) { @@ -9193,7 +9232,7 @@ export class DownloadManager extends EventEmitter { // All downloads finished — use NORMAL OS priority so extraction runs at // full speed (matching manual 7-Zip/WinRAR speed). extractCpuPriority: "high", - onLog: (level, message) => this.logPackageForPackage(pkg, level, `Extractor: ${message}`), + onLog: (level, message) => this.logExtractionForItems(pkg, completedItems, "Extractor", level, message), onArchiveFailure: (failure) => { if (autoRecoveredArchives.has(failure.archiveName)) { return; diff --git a/tests/download-manager.test.ts b/tests/download-manager.test.ts index 564d760..6179ffe 100644 --- a/tests/download-manager.test.ts +++ b/tests/download-manager.test.ts @@ -5,7 +5,7 @@ import http from "node:http"; import { EventEmitter, once } from "node:events"; import AdmZip from "adm-zip"; import { afterEach, describe, expect, it } from "vitest"; -import { DownloadManager, getAuthoritativeRealDebridTotal } from "../src/main/download-manager"; +import { DownloadManager, extractArchiveNameFromExtractorLogMessage, getAuthoritativeRealDebridTotal } from "../src/main/download-manager"; import { defaultSettings } from "../src/main/constants"; import { parseDebridLinkApiKeys } from "../src/shared/debrid-link-keys"; import { getProviderUsageDayKey } from "../src/shared/provider-daily-limits"; @@ -15,6 +15,18 @@ import { primeDebridLinkRuntimeCooldownForTests, resetDebridLinkRuntimeStateForT const tempDirs: string[] = []; const originalFetch = globalThis.fetch; +describe("extractArchiveNameFromExtractorLogMessage", () => { + it("detects archive names from extractor log variants", () => { + expect(extractArchiveNameFromExtractorLogMessage("Extract-Backend Start: archive=scn-dhanbs7-S02E008.rar, mode=legacy")).toBe("scn-dhanbs7-S02E008.rar"); + expect(extractArchiveNameFromExtractorLogMessage("Entpacke Archiv: scn-dhanbs7-S02E008.rar -> C:\\target")).toBe("scn-dhanbs7-S02E008.rar"); + expect(extractArchiveNameFromExtractorLogMessage("Entpack-Fehler scn-dhanbs7-S02E008.rar [missing_parts]: Error: boom")).toBe("scn-dhanbs7-S02E008.rar"); + }); + + it("returns null when no archive name is present", () => { + expect(extractArchiveNameFromExtractorLogMessage("Post-Processing Entpacken Ende")).toBeNull(); + }); +}); + async function waitFor(predicate: () => boolean, timeoutMs = 15000): Promise { const started = Date.now(); while (!predicate()) {