feat: self-managing yt-dlp + ffmpeg version display

yt-dlp is now a managed, auto-updating binary rather than a static bundled
one. On launch AeroFetch seeds a writable copy under userData from the
bundled seed, self-heals it if it goes missing, and — throttled to once a
day — self-updates it to the configured channel (default: nightly). This
keeps the binary current so YouTube changes don't silently cause 403s, and
an app reinstall (or portable re-extraction) can no longer roll it back.
Settings shows an auto-update toggle, the live version, last-checked time,
and the channel selector.

Also surface the bundled ffmpeg/ffprobe versions in Settings (display only:
they have no self-update path and, unlike yt-dlp, don't break as sites
change, so there is no auto-update for them).

- binaries: split the read-only bundled seed from the managed userData copy
- ytdlp: ensureManagedYtdlp (seed + self-heal), startup auto-update runner
- ytdlpPolicy: pure once-a-day throttle decision (unit-tested)
- ffmpeg: parse ffmpeg/ffprobe -version for the Settings panel
- settings/ipc/preload/renderer: new settings, IPC channels, types, and UI

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-26 07:27:43 -04:00
parent fa699a803d
commit 4854fcc947
13 changed files with 356 additions and 18 deletions
+39
View File
@@ -15,6 +15,7 @@ export const IpcChannels = {
/** main → renderer push channel for installer download progress */
appUpdateProgress: 'app:update-progress',
ytdlpVersion: 'ytdlp:version',
ffmpegVersion: 'ffmpeg:version',
probe: 'media:probe',
downloadStart: 'download:start',
downloadCancel: 'download:cancel',
@@ -40,6 +41,8 @@ export const IpcChannels = {
templatesRemove: 'templates:remove',
commandPreview: 'command:preview',
ytdlpUpdate: 'ytdlp:update',
/** main → renderer push channel for background yt-dlp auto-update status */
ytdlpAutoUpdateStatus: 'ytdlp:auto-update-status',
errorLogList: 'errorlog:list',
errorLogClear: 'errorlog:clear',
backupExport: 'backup:export',
@@ -196,6 +199,18 @@ export interface YtdlpVersionResult {
error?: string
}
/**
* Versions of the bundled 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,
* unlike yt-dlp, doesn't break as sites change), so there's no ok/error here —
* just "what's installed".
*/
export interface FfmpegVersionResult {
ffmpeg: string | null
ffprobe: string | null
}
/** Result of checking the configured Gitea repo for a newer AeroFetch release. */
export interface AppUpdateInfo {
ok: boolean
@@ -260,6 +275,24 @@ export interface YtdlpUpdateResult {
error?: string
}
/**
* Status of a background (startup) yt-dlp auto-update, pushed main → renderer on
* IpcChannels.ytdlpAutoUpdateStatus so the Settings UI can reflect a check that
* ran on its own — no button click needed.
*/
export interface YtdlpAutoUpdateStatus {
/** 'checking' when the update starts; the rest are terminal */
phase: 'checking' | 'updated' | 'current' | 'error'
/** which channel was checked */
channel: YtdlpUpdateChannel
/** yt-dlp version after the run, when known (phases 'updated' | 'current') */
version?: string
/** epoch ms the check finished (terminal phases) — feeds the "last checked" line */
checkedAt?: number
/** error text when phase === 'error' */
error?: string
}
/** A single selectable output format, derived from `yt-dlp -J`. */
export interface FormatOption {
/** yt-dlp format_id, or BEST_FORMAT_ID for the auto-best option */
@@ -437,6 +470,12 @@ export interface Settings {
restrictFilenames: boolean
/** record completed downloads in a yt-dlp --download-archive file to skip repeats */
downloadArchive: boolean
/** keep the managed yt-dlp.exe auto-updated in the background on launch */
autoUpdateYtdlp: boolean
/** channel the auto-updater (and the manual "Update yt-dlp" button) updates to */
ytdlpChannel: YtdlpUpdateChannel
/** epoch ms of the last automatic yt-dlp update check; 0 = never run */
ytdlpLastUpdateCheck: number
/** apply a named custom-command template's extra args to every new download */
customCommandEnabled: boolean
/** id of the CommandTemplate applied when customCommandEnabled is on; null = none picked yet */