Phase C had been implemented in the working tree but never committed: named custom-command templates (CRUD + per-download override), command preview, and the in-app yt-dlp self-updater. Phase D adds: history search/kind-filter/multi-select/bulk-delete/ re-download; native OS notifications on completion/failure; a private/ incognito download mode that skips history; settings+template backup/ restore via JSON; and a persistent error log surfaced as a Diagnostics report in Settings. See ROADMAP.md for the full per-item breakdown. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
11 KiB
AeroFetch Roadmap — feature parity with Seal
AeroFetch is the Windows/Electron counterpart to Seal, the Android Material-You frontend for yt-dlp. The core loop (probe → format pick → queue → download → history) is already in place. This document tracks the remaining gap: everything Seal does that AeroFetch doesn't yet.
Sources: Seal GitHub · Seal on F-Droid · It's FOSS overview.
Already at parity ✅
Video + audio download · audio → mp3 with embedded thumbnail/metadata · interactive format picker (probe) · quality presets · queue + concurrency cap + cancel · download history (open / show-in-folder) · clipboard auto-detect · light/dark theme · output folder + filename template · yt-dlp version check · NSIS installer + portable build.
Phase A — Core download power ✅ COMPLETE
A shared DownloadOptions model (src/shared/ipc.ts) carries the post-processing
choices end-to-end. Persisted defaults are editable in Settings → Format &
post-processing via a reusable DownloadOptionsForm component, which the download
bar also hosts in a collapsible per-download Options panel. The main process emits
the flags in buildArgs (src/main/download.ts). All items verified in the UI preview
(typecheck clean). Caveat: the option flags themselves were validated by reasoning +
typecheck, not yet by live yt-dlp downloads — worth a real-download smoke test pass.
- Playlist downloads. Probe now uses
yt-dlp -J --flat-playlistand returns either a single video (with formats) or a flat playlist. The download bar shows a selection panel (scrollable checkbox list, select-all/none, live count) and enqueues each chosen entry as its own single-video download — reusing the existing queue/concurrency/history. - Subtitles. Download + embed (
--write-subs,--write-auto-subs,--sub-langs,--embed-subs,--convert-subs srt); language field + auto-caption toggle. - SponsorBlock.
--sponsorblock-mark/--sponsorblock-removewith a category multi-select (sponsor, intro, outro, selfpromo, music_offtopic…) +--force-keyframes-at-cuts. - More audio formats. Was mp3-only. Now best / mp3 / m4a / opus / flac / wav / aac
via
--audio-format. - Video container + codec preference. Container choice (mp4/mkv/webm) +
preferred codec (
-S res,fps,vcodec:…) to nudge toward av1 / vp9 / h264 without overriding the requested resolution. - Embed chapters.
--embed-chapterstoggle. (--split-chaptersstill TODO.) - Embed thumbnail crop-to-square for audio (Seal's "crop artwork") via thumbnail post-processor args. Implemented; needs a real-download smoke test of the ffmpeg crop recipe.
- Per-download overrides. Collapsible Options panel in the download bar (shared
DownloadOptionsForm) so one download — or one playlist batch — can deviate from the persisted defaults; "Reset to defaults" clears it. ThreadsoptionsthroughaddFromUrl → DownloadItem → startDownloadand is carried on retry.
Phase B — Access & networking ✅ COMPLETE
buildArgs's former NetworkOptions param is now AccessOptions (still in
src/main/buildArgs.ts) — it grew past pure networking once cookies and
filename/archive flags joined proxy/rate-limit/aria2c. Caveat: the sign-in
window's cookie export is implemented per the Electron session-cookie and
Netscape cookie-jar formats and verified by reasoning + typecheck, not yet by
an actual live login → yt-dlp download against a gated video — worth a real
smoke test.
- Cookies. Settings → Cookies has a source picker:
- From a browser's cookie store →
--cookies-from-browser <name>(chrome/edge/ firefox/brave/opera/vivaldi —COOKIE_BROWSERSinsrc/shared/ipc.ts). - Sign-in window (built into AeroFetch) → opens an ElectronBrowserWindowon a persisted session partition (persist:aerofetch-login, seesrc/main/cookies.ts); OAuth/SSO popups are allowed through onto the same partition. Closing the window exports its cookies to a Netscape-format file (userData/cookies.txt) whichbuildArgspasses via--cookies. Settings → Cookies shows last-saved time + a "Clear saved cookies" button (also clears the partition's storage). - aria2c external downloader. Settings → Network has a "Use aria2c downloader"
toggle; when on and
resources/bin/aria2c.exeexists,buildArgsemits--downloader <path> --downloader-args(ARIA2C_ARGSinsrc/main/buildArgs.ts:-x 16 -s 16 -k 1M). Missing binary falls back silently to yt-dlp's own downloader (checked insrc/main/download.ts). Binary is optional and not committed — seeresources/bin/README.md. - Proxy (
--proxy) and rate limit (--limit-rate). Settings → Network fields (Settings.proxy/Settings.rateLimit), threaded throughbuildArgs'sAccessOptionsparam. - Restrict filenames toggle (
--restrict-filenames) + download archive (--download-archive, skip already-downloaded) — both in Settings → Filenames. The archive file lives at a fixeduserData/download-archive.txt(getDownloadArchivePath()insrc/main/settings.ts), not user-configurable.
Phase C — Custom commands & yt-dlp management ✅ COMPLETE
Named CommandTemplates (src/shared/ipc.ts) persist to templates.json
(src/main/templates.ts, mirrors history.ts's plain-JSON pattern) and are managed
inline in Settings → Custom commands via TemplateManager.tsx — no modal, matching
the rest of the app's no-portal-overlay convention (see the Hint/Select comments re:
this dev machine's GPU/driver). A template's extra yt-dlp flags are shell-split
(parseExtraArgs, src/main/buildArgs.ts) and appended to the generated argv
immediately before the -- URL terminator, so a template flag can override anything
chosen earlier. Settings.customCommandEnabled + defaultTemplateId apply a template
to every new download; the download bar's own Custom command panel lets a single
download override that default or opt out entirely (extraArgs on
StartDownloadOptions, mirroring the existing per-download options override).
buildCommand() (src/main/download.ts) now centralises settings/access/
options/extraArgs resolution, shared by both the real startDownload spawn and the new
preview path. All items verified in the UI preview + npm run typecheck +
npm run test (11 new unit tests covering parseExtraArgs, formatCommandLine, and
extraArgs ordering). Caveat: the extra-args splitting and command construction were
validated by reasoning + tests, not yet by a real yt-dlp run with actual custom flags —
worth a real-download smoke test pass, same as Phase A/B.
- Custom command templates. Named, editable templates of extra yt-dlp args
(
CommandTemplate), CRUD'd inline viaTemplateManager.tsx; persisted totemplates.json. Pick a default viaSettings.defaultTemplateId(a Select in the Custom commands card). The "run custom command" mode isSettings.customCommandEnabled, applied to every new download unless overridden per-download in the download bar's Custom command panel. - Command preview.
IpcChannels.commandPreview→previewCommand()(src/main/download.ts) builds the exact argv via the samebuildCommand()path a real download would take, then renders it withformatCommandLine. Surfaced as a Preview command button in the download bar, with a Copy button. - In-app yt-dlp updater.
updateYtdlp(channel)(src/main/ytdlp.ts) runs yt-dlp's own--update-to stable|nightly. Surfaced in Settings → About, right next to the existing version check, with a channel picker + result output.
Phase D — Library, UX & notifications ✅ COMPLETE
History stays plain JSON (history.json, 500-entry cap) — search/filter/select are done
client-side over the already-small array, so the planned better-sqlite3 migration wasn't
needed for this phase and was deliberately skipped to avoid a native-module build step for
no functional gain at this scale. Failed downloads (pre-spawn rejections and in-flight
yt-dlp failures alike) now persist to a new errorlog.json (src/main/errorlog.ts,
200-entry cap) independent of the queue, so a report survives Clear finished. Native
notifications and the backup file both reuse existing settings/templates plumbing
(getSettings/setSettings, listTemplates/the new replaceTemplates) rather than adding
parallel storage. All items verified in the UI preview (typecheck + npm run test clean).
- History search + multi-select + filter (video/audio) + bulk delete + re-download.
HistoryView.tsxgained a search box, an All/Video/Audio filter, a "Select" mode with per-row checkboxes + select-all + Delete selected (newhistoryRemoveManyIPC,src/main/history.ts), and a per-row re-download button that re-queues the entry's URL/kind/quality via the existingaddFromUrl. - Native OS notifications on completion / failure (Electron
Notification,src/main/download.ts). Gated by a new Notify when downloads finish switch (Settings.notifyOnComplete, default on); clicking a notification refocuses the window. - Private / incognito mode — a download bar toggle (
DownloadBar.tsx, sticky like an incognito tab) setsDownloadItem.incognito, whichstore/downloads.tschecks before ever callinguseHistory().add(...). Queue items show an eye-off badge when private. - Backup / restore settings + templates (export / import JSON). New
src/main/backup.tswrites/reads{ settings, templates }via a save/open dialog;replaceTemplates()(src/main/templates.ts) does a full restore rather than a merge-by-id. Surfaced as Settings → Backup & restore. - Error log / copy error report view (Seal's debug report). New
src/main/errorlog.tspersists every failure (pre-spawn rejection, spawn error, or non-zero yt-dlp exit); Settings → Diagnostics lists recent entries with Copy full report and Clear log.
Phase E — Theming & platform polish
Some items are Android-specific in Seal and adapted to Windows here.
- Theme presets + "follow system" and high-contrast (toffee light/dark already exists; add system-follow + a few accent presets). Material-You dynamic color is Android-only; closest Windows analog is accent presets / system accent.
- i18n / language setting (Seal ships ~40 languages). Large but optional.
- Windows "open with" / share integration — register a URL-protocol handler or Explorer "Send to" entry (analog of Android's share sheet). Lower priority, platform-specific.
- Onboarding / welcome screen on first run.
Not portable from Seal (noted for completeness)
- SAF directory picker (Android storage) → already covered by the native folder dialog.
- Android share-sheet / quick-download-on-share → partially mapped to Phase E.
- F-Droid distribution → N/A (AeroFetch ships NSIS + portable).