Initial commit: AeroFetch — yt-dlp/YouTube downloader for Windows

Electron + React (Fluent UI) desktop frontend for yt-dlp:
- Download queue with live progress, concurrency cap, cancel/retry
- Format/quality picker via yt-dlp probe; audio extraction to MP3
- Settings + history persistence (electron-store / JSON)
- Clipboard link auto-detect; persisted light/dark theme
- NSIS + portable packaging (binaries bundled at build time, not in git)

UI: "Studio" sidebar layout with a toffee-brown theme (light + neutral dark).
Dropdowns use a native <select> and tooltips a CSS-only Hint, to avoid a
dev-machine GPU overlay flicker/blank issue.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 18:17:41 -04:00
commit 1a2270c95e
39 changed files with 11351 additions and 0 deletions
+144
View File
@@ -0,0 +1,144 @@
/**
* Shared contract between main and renderer.
* IPC channel names + payload types live here so both sides stay in sync.
*/
export const IpcChannels = {
ytdlpVersion: 'ytdlp:version',
probe: 'media:probe',
downloadStart: 'download:start',
downloadCancel: 'download:cancel',
defaultFolder: 'download:default-folder',
chooseFolder: 'download:choose-folder',
openPath: 'shell:open-path',
showInFolder: 'shell:show-in-folder',
clipboardRead: 'clipboard:read',
settingsGet: 'settings:get',
settingsSet: 'settings:set',
historyList: 'history:list',
historyAdd: 'history:add',
historyRemove: 'history:remove',
historyClear: 'history:clear',
/** main → renderer push channel for live download events */
downloadEvent: 'download:event'
} as const
/** Sentinel format id meaning "let yt-dlp pick the best video+audio". */
export const BEST_FORMAT_ID = '__best__'
export type MediaKind = 'video' | 'audio'
export interface YtdlpVersionResult {
ok: boolean
/** yt-dlp version string, present when ok is true */
version?: string
/** human-readable error, present when ok is false */
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 */
id: string
/** human label, e.g. '1080p60 · mp4 · 124 MB' */
label: string
height?: number
ext?: string
filesizeLabel?: string
/** true when this format already carries an audio track */
hasAudio: boolean
}
/** Metadata + available formats returned by a probe. */
export interface MediaInfo {
title: string
channel?: string
durationLabel?: string
thumbnail?: string
/** video format options, best-first (audio is transcoded, so not listed) */
formats: FormatOption[]
}
export interface ProbeResult {
ok: boolean
info?: MediaInfo
error?: string
}
/** Sent renderer → main to kick off a download. The renderer owns the id. */
export interface StartDownloadOptions {
id: string
url: string
kind: MediaKind
/** one of the UI quality labels (e.g. '1080p', 'Best (MP3)') */
quality: string
/** absolute output directory; main falls back to the OS Downloads folder */
outputDir?: string
/** exact yt-dlp format_id chosen from a probe; omit for the preset path */
formatId?: string
/** whether the chosen format already includes audio (skips +bestaudio) */
formatHasAudio?: boolean
}
export interface StartDownloadResult {
ok: boolean
error?: string
}
export interface DownloadMeta {
title?: string
channel?: string
durationLabel?: string
}
export interface DownloadProgress {
/** yt-dlp progress status: 'downloading' | 'finished' | … */
status: string
/** 0..1 (0 when total size is unknown) */
progress: number
/** human-readable, e.g. '4.7 MB/s' */
speed?: string
/** human-readable, e.g. '1:12' */
eta?: string
/** human-readable total size, e.g. '512 MB' */
sizeLabel?: string
}
/** Discriminated union pushed on IpcChannels.downloadEvent. */
export type DownloadEvent =
| { type: 'meta'; id: string; meta: DownloadMeta }
| { type: 'progress'; id: string; progress: DownloadProgress }
| { type: 'done'; id: string; filePath?: string }
| { type: 'error'; id: string; error: string }
/** Persisted user settings (electron-store). */
export interface Settings {
/** absolute output directory (empty string resolves to the OS Downloads folder) */
outputDir: string
defaultKind: MediaKind
defaultVideoQuality: string
defaultAudioQuality: string
/** how many downloads run at once */
maxConcurrent: number
/** yt-dlp output template, e.g. '%(title)s.%(ext)s' */
filenameTemplate: string
/** UI color theme */
theme: 'light' | 'dark'
/** auto-detect video links from the clipboard on window focus */
clipboardWatch: boolean
}
/** A completed download, persisted to history.json (newest-first). */
export interface HistoryEntry {
id: string
title: string
channel?: string
url: string
kind: MediaKind
quality: string
filePath?: string
sizeLabel?: string
thumbnail?: string
/** epoch milliseconds */
completedAt: number
}