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...',