From d2a4ba215460b7658cedc642d8d9a2b5e711ddad Mon Sep 17 00:00:00 2001 From: Sucukdeluxe <259325684+Sucukdeluxe@users.noreply.github.com> Date: Fri, 14 Aug 2026 18:28:21 +0200 Subject: [PATCH] feat(queue): announce tool preparation before every download starts On a fresh installation the first download repairs the managed Streamlink and FFmpeg copies, which downloads and extracts roughly two hundred megabytes before the actual VOD transfer begins. The queue item previously sat on a generic starting status for that entire window - the old preparing hint only appeared when the streamlink command had not been verified yet, and a prior System Check verifies the command without making the managed repair any cheaper, so users saw no explanation for delays of a minute or more. All three download paths - VOD, live recording and merge groups - now report a dedicated localized "Preparing download tools" status before the tool gates run, and the misleading conditional hint was removed. --- src/main-runtime.production-path.test.ts | 14 ++++++++ src/main.ts | 46 +++++++++++++++--------- src/main/domain/i18n-backend.ts | 4 +-- 3 files changed, 46 insertions(+), 18 deletions(-) diff --git a/src/main-runtime.production-path.test.ts b/src/main-runtime.production-path.test.ts index 67eac41..6a95717 100644 --- a/src/main-runtime.production-path.test.ts +++ b/src/main-runtime.production-path.test.ts @@ -85,6 +85,20 @@ describe('main runtime safety production paths', () => { expect(closeHandler.indexOf('finish({ success: true, filename })')).toBeGreaterThan(closeHandler.indexOf('partialDownloadRegistry.commit')); }); + it('announces tool preparation before every download tool gate', () => { + const source = mainSource(); + const vod = source.slice(source.indexOf('async function downloadVOD'), source.indexOf('async function processDownloadMergeGroup')); + const live = source.slice(source.indexOf('async function downloadLiveStream'), source.indexOf('async function downloadVOD')); + const mergeGroup = source.slice(source.indexOf('async function processDownloadMergeGroup'), source.indexOf('// ---- PHASE 2: MERGING ----')); + for (const [label, fragment] of [['vod', vod], ['live', live], ['merge', mergeGroup]] as const) { + const announce = fragment.indexOf("tBackend('statusPreparingTools')"); + const gate = fragment.indexOf('await ensureStreamlinkInstalled()'); + expect(announce, `${label} announces tool preparation`).toBeGreaterThan(-1); + expect(gate, `${label} gates on streamlink`).toBeGreaterThan(announce); + } + expect(source).not.toContain("tBackend('statusCheckingTools')"); + }); + it('reports the auto-install failure guidance on every download tool gate', () => { const source = mainSource(); const mergeGroup = source.slice(source.indexOf('async function processDownloadMergeGroup'), source.indexOf('// ---- PHASE 2: MERGING ----')); diff --git a/src/main.ts b/src/main.ts index 6723515..82d3189 100644 --- a/src/main.ts +++ b/src/main.ts @@ -129,7 +129,7 @@ import { refreshBundledToolPaths, ensureStreamlinkInstalled, ensureFfmpegInstalled, getManagedToolStatuses, repairManagedTools, resetManagedTools, canExecute, canExecuteCommand, - cacheVerifiedStreamlinkCommand, isVerifiedStreamlinkCommand, + cacheVerifiedStreamlinkCommand, cacheVerifiedFfmpegCommands, isVerifiedFfmpegCommands, invalidateVerifiedToolCaches, setManagedToolExecutionObserver } from './tools'; @@ -5885,6 +5885,16 @@ async function downloadLiveStream( item: QueueItem, onProgress: (progress: DownloadProgress) => void ): Promise { + onProgress({ + id: item.id, + progress: -1, + speed: '', + eta: '', + status: tBackend('statusPreparingTools'), + currentPart: 0, + totalParts: 0 + }); + const streamlinkReady = await ensureStreamlinkInstalled(); if (!streamlinkReady) { return { success: false, error: tBackend('streamlinkAutoInstallFailed') }; @@ -6203,21 +6213,15 @@ async function downloadVOD( }; } - const streamlinkCmd = getStreamlinkCommand(); - const streamlinkVersionArgs = [...streamlinkCmd.prefixArgs, '--version']; - const streamlinkAlreadyVerified = isVerifiedStreamlinkCommand(streamlinkCmd.command, streamlinkVersionArgs); - - if (!streamlinkAlreadyVerified) { - onProgress({ - id: item.id, - progress: -1, - speed: '', - eta: '', - status: tBackend('statusCheckingTools'), - currentPart: 0, - totalParts: 0 - }); - } + onProgress({ + id: item.id, + progress: -1, + speed: '', + eta: '', + status: tBackend('statusPreparingTools'), + currentPart: 0, + totalParts: 0 + }); const streamlinkReady = await ensureStreamlinkInstalled(); if (!streamlinkReady) { @@ -6469,6 +6473,16 @@ async function processDownloadMergeGroup( // ---- PHASE 1: DOWNLOADING ---- if (mg.mergePhase === 'downloading') { + onProgress({ + id: item.id, + progress: -1, + speed: '', + eta: '', + status: tBackend('statusPreparingTools'), + currentPart: 0, + totalParts: 0 + }); + const streamlinkReady = await ensureStreamlinkInstalled(); if (!streamlinkReady) { return { success: false, error: tBackend('streamlinkAutoInstallFailed') }; diff --git a/src/main/domain/i18n-backend.ts b/src/main/domain/i18n-backend.ts index 1922c69..21a20f0 100644 --- a/src/main/domain/i18n-backend.ts +++ b/src/main/domain/i18n-backend.ts @@ -32,7 +32,7 @@ export const BACKEND_MESSAGES = { diskSpaceShortGeneric: 'Zu wenig Speicherplatz.', attemptFailed: 'Versuch {attempt}/{max} fehlgeschlagen ({errorClass}): {error}', retryingIn: 'Neuer Versuch in {seconds}s ({errorClass})...', - statusCheckingTools: 'Prüfe Download-Tools...', + statusPreparingTools: 'Download-Tools werden vorbereitet …', statusDownloadStarted: 'Download wird gestartet …', statusBytesDownloaded: '{bytes} heruntergeladen', statusFetchingChatReplay: 'Chat-Replay wird heruntergeladen...', @@ -73,7 +73,7 @@ export const BACKEND_MESSAGES = { diskSpaceShortGeneric: 'Not enough disk space.', attemptFailed: 'Attempt {attempt}/{max} failed ({errorClass}): {error}', retryingIn: 'Retrying in {seconds}s ({errorClass})...', - statusCheckingTools: 'Checking download tools...', + statusPreparingTools: 'Preparing download tools …', statusDownloadStarted: 'Starting download …', statusBytesDownloaded: '{bytes} downloaded', statusFetchingChatReplay: 'Fetching chat replay...',