export interface MediaFile {
name: string;
path: string;
size: number;
created: number;
duration: number;
thumbnailPath: string | null;
ruforgePosterPath: string | null;
subtitlePath: string | null;
chapters: Chapter[] | null;
downloadMetadataHint: string | null;
sourceUrl: string | null;
/** yt-dlp video id from sidecar `.info.json` `id` (when `sourceUrl` is missing). */
sourceId: string | null;
/** yt-dlp `playlist_index` from sidecar when downloaded as part of a playlist. */
playlistIndex?: number | null;
}
Built with
TypeScript
Frontend domain types and IPC boundaries are TypeScript so refactors fail at compile time, not in the webview.
src/types.ts defines MediaFile, VideoInfo, DownloadJob, chapter shapes, and progress payloads returned from Rust. Gallery entries discriminate kind: "media" vs playlist stacks. Sidecar fields like sourceId and playlistIndex flow from yt-dlp JSON into the player and SponsorBlock hooks.
src/store/types.ts holds RuforgeSettings (download format, SponsorBlock modes, auto-advance audio, auto scrub previews, max concurrent jobs, cookie paths). Settings UI and store persist the same object shape.
npm run build runs tsc -b before Vite bundles. Nullable playingFile, queue rows removed mid-download, and gallery scans in flight are the usual places strict null checks catch real bugs.
The website under website/src/lib/*.ts is also TypeScript: builtWithPages.ts, sitePages.ts, nav helpers. App and site share naming (RuForge, rf-* tokens) but not a shared npm package. Copy types manually when they must align.
In the repo
Where it shows up
-
src/types.ts,src/store/types.ts - Typed
invokeat download, gallery, SponsorBlock, and media commands -
website/src/lib/*.tscontent registries and page defs