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:
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user