feat(setup): fetch ffmpeg on first run instead of bundling it
Ship a smaller installer that no longer carries ffmpeg.exe/ffprobe.exe (the bulk of its size). On first run they are downloaded from a pinned upstream archive (gyan 8.1.2), verified against a pinned SHA-256, and unpacked with the OS tar.exe into the managed userData/bin dir -- the same place as the self-updating yt-dlp, so they survive app updates and portable re-extraction. A hard onboarding gate blocks the app until they are present, with a "locate existing ffmpeg" folder-picker fallback for offline machines and a Repair action in Settings > About. The updater's streaming/checksum/redirect/idle-timeout download loop is extracted to lib/verifiedDownload.streamVerifiedFile and shared by both the app-installer download and the ffmpeg fetch; the updater's public behaviour and error strings are unchanged (its boundary tests still pass). No new npm dependency: extraction uses the System32 bsdtar resolved by absolute path (audit F3). Note: this commit also carries pre-existing, in-progress aria2c/network and updater-token work that was already uncommitted in the working tree and is entangled with the above in shared files (updater.ts, ipc.ts, preload/index.ts, mockApi.ts, shared/ipc.ts, plus the settings/network files and aria2c.exe). It was not cleanly separable by path, so it is included here rather than split out. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+42
-11
@@ -18,6 +18,18 @@ export const IpcChannels = {
|
||||
appUpdateProgress: 'app:update-progress',
|
||||
ytdlpVersion: 'ytdlp:version',
|
||||
ffmpegVersion: 'ffmpeg:version',
|
||||
/** readiness (both binaries present) + versions of the managed ffmpeg/ffprobe */
|
||||
ffmpegSetupStatus: 'ffmpeg:setup-status',
|
||||
/** download + unpack ffmpeg/ffprobe from the pinned upstream archive (first-run setup) */
|
||||
ffmpegSetupDownload: 'ffmpeg:setup-download',
|
||||
/** abort the in-flight ffmpeg archive download */
|
||||
ffmpegSetupCancel: 'ffmpeg:setup-cancel',
|
||||
/** pick a folder that already holds ffmpeg.exe + ffprobe.exe and install from it */
|
||||
ffmpegSetupLocate: 'ffmpeg:setup-locate',
|
||||
/** main → renderer push channel for ffmpeg setup progress (download %, then extract) */
|
||||
ffmpegSetupProgress: 'ffmpeg:setup-progress',
|
||||
/** whether the optional aria2c.exe external downloader is present in the bin dir */
|
||||
aria2cAvailable: 'aria2c:available',
|
||||
probe: 'media:probe',
|
||||
downloadStart: 'download:start',
|
||||
downloadCancel: 'download:cancel',
|
||||
@@ -275,9 +287,9 @@ export interface YtdlpVersionResult {
|
||||
}
|
||||
|
||||
/**
|
||||
* Versions of the bundled ffmpeg + ffprobe, for display in Settings. Each is the
|
||||
* Versions of the managed ffmpeg + ffprobe, for display in Settings. Each is the
|
||||
* parsed version token, or null when that binary is missing or unreadable. These
|
||||
* ship with AeroFetch and aren't auto-updated (ffmpeg has no self-update and,
|
||||
* are fetched on first run (not bundled) and aren't auto-updated (ffmpeg has no self-update and,
|
||||
* unlike yt-dlp, doesn't break as sites change), so there's no ok/error here —
|
||||
* just "what's installed".
|
||||
*/
|
||||
@@ -286,6 +298,33 @@ export interface FfmpegVersionResult {
|
||||
ffprobe: string | null
|
||||
}
|
||||
|
||||
/** Readiness of the managed ffmpeg/ffprobe: `ready` gates the first-run setup screen. */
|
||||
export interface FfmpegSetupStatus {
|
||||
/** true once both ffmpeg.exe and ffprobe.exe are present in the managed dir */
|
||||
ready: boolean
|
||||
/** installed versions (null when the binary is absent), for the About card */
|
||||
ffmpeg: string | null
|
||||
ffprobe: string | null
|
||||
}
|
||||
|
||||
/** Live progress of the first-run ffmpeg acquisition (main → renderer). */
|
||||
export interface FfmpegSetupProgress {
|
||||
/** 'downloading' the archive, then a brief indeterminate 'extracting' phase */
|
||||
phase: 'downloading' | 'extracting'
|
||||
/** bytes received so far (downloading phase) */
|
||||
received?: number
|
||||
/** total archive bytes, when Content-Length is known */
|
||||
total?: number
|
||||
/** 0..1 download fraction, when total is known */
|
||||
fraction?: number
|
||||
}
|
||||
|
||||
/** Result of a download / locate setup action. */
|
||||
export interface FfmpegSetupResult {
|
||||
ok: boolean
|
||||
error?: string
|
||||
}
|
||||
|
||||
/** Result of checking the configured Gitea repo for a newer AeroFetch release. */
|
||||
export interface AppUpdateInfo {
|
||||
ok: boolean
|
||||
@@ -684,13 +723,6 @@ export interface Settings {
|
||||
* modern GPUs can turn this on for smoother high-DPI/large-window rendering.
|
||||
*/
|
||||
useHardwareAcceleration: boolean
|
||||
/**
|
||||
* Optional Gitea access token for the in-app updater. Empty = anonymous (works
|
||||
* only where the release repo allows anonymous access). When the release repo
|
||||
* is private / the instance requires sign-in, paste a read-only token so the
|
||||
* updater can check + download. Stored locally in settings, never shipped.
|
||||
*/
|
||||
updateToken: string
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -753,8 +785,7 @@ export const DEFAULT_SETTINGS: Settings = {
|
||||
minimizeToTray: false,
|
||||
launchAtStartup: false,
|
||||
isSidebarCollapsed: false,
|
||||
useHardwareAcceleration: false,
|
||||
updateToken: ''
|
||||
useHardwareAcceleration: false
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user