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:
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
import { ElectronAPI } from '@electron-toolkit/preload'
|
||||
import type { Api } from './index'
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
electron: ElectronAPI
|
||||
api: Api
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import { contextBridge, ipcRenderer, type IpcRendererEvent } from 'electron'
|
||||
import { electronAPI } from '@electron-toolkit/preload'
|
||||
import {
|
||||
IpcChannels,
|
||||
type YtdlpVersionResult,
|
||||
type ProbeResult,
|
||||
type StartDownloadOptions,
|
||||
type StartDownloadResult,
|
||||
type DownloadEvent,
|
||||
type Settings,
|
||||
type HistoryEntry
|
||||
} from '@shared/ipc'
|
||||
|
||||
// The surface exposed to the renderer. Keep this thin: it only forwards to IPC.
|
||||
const api = {
|
||||
getYtdlpVersion: (): Promise<YtdlpVersionResult> =>
|
||||
ipcRenderer.invoke(IpcChannels.ytdlpVersion),
|
||||
|
||||
probe: (url: string): Promise<ProbeResult> => ipcRenderer.invoke(IpcChannels.probe, url),
|
||||
|
||||
startDownload: (opts: StartDownloadOptions): Promise<StartDownloadResult> =>
|
||||
ipcRenderer.invoke(IpcChannels.downloadStart, opts),
|
||||
|
||||
cancelDownload: (id: string): Promise<void> =>
|
||||
ipcRenderer.invoke(IpcChannels.downloadCancel, id),
|
||||
|
||||
getDefaultFolder: (): Promise<string> => ipcRenderer.invoke(IpcChannels.defaultFolder),
|
||||
|
||||
chooseFolder: (): Promise<string | null> => ipcRenderer.invoke(IpcChannels.chooseFolder),
|
||||
|
||||
openPath: (path: string): Promise<string> => ipcRenderer.invoke(IpcChannels.openPath, path),
|
||||
|
||||
showInFolder: (path: string): Promise<void> =>
|
||||
ipcRenderer.invoke(IpcChannels.showInFolder, path),
|
||||
|
||||
readClipboard: (): Promise<string> => ipcRenderer.invoke(IpcChannels.clipboardRead),
|
||||
|
||||
getSettings: (): Promise<Settings> => ipcRenderer.invoke(IpcChannels.settingsGet),
|
||||
|
||||
setSettings: (partial: Partial<Settings>): Promise<Settings> =>
|
||||
ipcRenderer.invoke(IpcChannels.settingsSet, partial),
|
||||
|
||||
listHistory: (): Promise<HistoryEntry[]> => ipcRenderer.invoke(IpcChannels.historyList),
|
||||
|
||||
addHistory: (entry: HistoryEntry): Promise<HistoryEntry[]> =>
|
||||
ipcRenderer.invoke(IpcChannels.historyAdd, entry),
|
||||
|
||||
removeHistory: (id: string): Promise<HistoryEntry[]> =>
|
||||
ipcRenderer.invoke(IpcChannels.historyRemove, id),
|
||||
|
||||
clearHistory: (): Promise<HistoryEntry[]> => ipcRenderer.invoke(IpcChannels.historyClear),
|
||||
|
||||
/** Subscribe to live download events. Returns an unsubscribe function. */
|
||||
onDownloadEvent: (cb: (ev: DownloadEvent) => void): (() => void) => {
|
||||
const listener = (_e: IpcRendererEvent, ev: DownloadEvent): void => cb(ev)
|
||||
ipcRenderer.on(IpcChannels.downloadEvent, listener)
|
||||
return () => ipcRenderer.removeListener(IpcChannels.downloadEvent, listener)
|
||||
}
|
||||
}
|
||||
|
||||
export type Api = typeof api
|
||||
|
||||
if (process.contextIsolated) {
|
||||
try {
|
||||
contextBridge.exposeInMainWorld('electron', electronAPI)
|
||||
contextBridge.exposeInMainWorld('api', api)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
} else {
|
||||
// @ts-ignore (defined in index.d.ts)
|
||||
window.electron = electronAPI
|
||||
// @ts-ignore (defined in index.d.ts)
|
||||
window.api = api
|
||||
}
|
||||
Reference in New Issue
Block a user