Bundle ffprobe.exe and complete the real-download smoke-test pass
A live smoke test of buildArgs' argv against the bundled yt-dlp + ffmpeg found that ffprobe.exe was never bundled (resources/bin/ shipped only ffmpeg.exe). yt-dlp resolves both from --ffmpeg-location, so duration-aware post-processing failed at runtime for every user with "ffprobe not found": --sponsorblock-remove, --force-keyframes-at-cuts, and --split-chapters (CODE-AUDIT C1). - Bundle ffprobe.exe (matching n8.1.2 LGPL build, sha256-verified against the already-bundled ffmpeg.exe) and document it in resources/bin/README.md as a required binary; correct the stale "not committed to git" note. - Guard startDownload against a missing ffmpeg.exe/ffprobe.exe up front so the failure is a clear AeroFetch error, not a cryptic yt-dlp postprocessing one (new getFfprobePath() in binaries.ts). - Expand test/real-download.integration.test.ts from 2 to 8 live cases: crop, sponsorblock-remove, audio opus re-encode, mkv+vp9 merge, subtitle+chapter embed, restrict-filenames, download-archive skip, and Phase C extra-args. All 8 pass against live yt-dlp + ffmpeg. - ROADMAP: mark the Phase A/B/C smoke-test caveats done. CODE-AUDIT: add C1. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,16 @@ export function getFfmpegPath(): string {
|
||||
return join(getBinDir(), 'ffmpeg.exe')
|
||||
}
|
||||
|
||||
/**
|
||||
* yt-dlp finds ffprobe via --ffmpeg-location (the bin dir), so the app never
|
||||
* spawns it directly — but it must be present, or duration-aware post-processing
|
||||
* (SponsorBlock-remove, --force-keyframes-at-cuts, --split-chapters) fails. This
|
||||
* accessor exists so startDownload can assert its presence up front.
|
||||
*/
|
||||
export function getFfprobePath(): string {
|
||||
return join(getBinDir(), 'ffprobe.exe')
|
||||
}
|
||||
|
||||
/** Optional bundled external downloader; absent unless dropped into resources/bin. */
|
||||
export function getAria2cPath(): string {
|
||||
return join(getBinDir(), 'aria2c.exe')
|
||||
|
||||
+17
-1
@@ -2,7 +2,7 @@ import { spawn, execFile, type ChildProcess } from 'child_process'
|
||||
import { existsSync } from 'fs'
|
||||
import { join } from 'path'
|
||||
import { app, BrowserWindow, Notification, type WebContents } from 'electron'
|
||||
import { getYtdlpPath, getBinDir, getAria2cPath } from './binaries'
|
||||
import { getYtdlpPath, getBinDir, getAria2cPath, getFfmpegPath, getFfprobePath } from './binaries'
|
||||
import { getSettings, getDownloadArchivePath } from './settings'
|
||||
import { getCookiesFilePath } from './cookies'
|
||||
import { listTemplates } from './templates'
|
||||
@@ -213,6 +213,22 @@ export function startDownload(
|
||||
error: `yt-dlp.exe not found at ${ytdlp}\nDrop it into resources/bin/ (see the README there).`
|
||||
}
|
||||
}
|
||||
// ffmpeg is used by nearly every download (merge, audio extract, thumbnail/
|
||||
// metadata embed) and ffprobe by duration-aware post-processing (SponsorBlock-
|
||||
// remove, --force-keyframes-at-cuts, --split-chapters). Assert both up front so a
|
||||
// missing binary is a clear AeroFetch message, not a cryptic mid-download yt-dlp
|
||||
// postprocessing error (e.g. "Unable to determine video duration: ffprobe not found").
|
||||
const missingBins: string[] = []
|
||||
if (!existsSync(getFfmpegPath())) missingBins.push('ffmpeg.exe')
|
||||
if (!existsSync(getFfprobePath())) missingBins.push('ffprobe.exe')
|
||||
if (missingBins.length > 0) {
|
||||
return {
|
||||
ok: false,
|
||||
error:
|
||||
`${missingBins.join(' and ')} not found in ${getBinDir()}\n` +
|
||||
`Add the ffmpeg build's binaries to resources/bin/ (see the README there).`
|
||||
}
|
||||
}
|
||||
// Reject anything that isn't an http(s) URL before it reaches yt-dlp's argv.
|
||||
try {
|
||||
assertHttpUrl(opts.url)
|
||||
|
||||
Reference in New Issue
Block a user