# YT Player A lightweight, **ad-free YouTube player** with on-device playlists. No login, no tracking, no ads — it extracts direct video/audio streams with `yt-dlp` and plays them in a native window. Built on [**zero-native**](https://github.com/vercel-labs/zero-native) (Zig + the system WebView) instead of Electron, so the binary is tiny — no bundled Chromium or Node runtime. ## Features - 🔍 **Search** YouTube without an account - 🚫 **No ads** — plays the raw stream, never the YouTube player - 📺 **High quality** via a dual-stream engine: a muted video track synced with a separate audio track (1080p+) with no `ffmpeg` muxing required; progressive formats are used as a fallback - 🎵 **Audio-only mode** — great for music, saves bandwidth - 📂 **On-device playlists** — create, rename, delete, add/remove videos. Stored locally as JSON; nothing leaves your machine - 🕘 **Watch history** - ⏯ Full controls: seek, volume, playback speed, quality switching, fullscreen, next/prev, and keyboard shortcuts (`space`, `←/→`, `f`, `m`) ## Architecture ``` frontend/ Static web UI (no framework, no build step) index.html styles.css app.js Player engine + UI; calls window.zero.invoke(...) src/ main.zig App definition + bridge handler registration bridge.zig Native handlers: spawn yt-dlp, slim its JSON, local store scripts/ setup-ytdlp.js Downloads the standalone yt-dlp binary into ./bin bin/ yt-dlp lands here (gitignored) app.zon App manifest (window, engine, permissions, frontend dir) ``` The web UI talks to the Zig side over the zero-native bridge: | `window.zero.invoke(...)` | Native handler | Returns | |---|---|---| | `yt.search { query }` | `ytSearch` | `{ ok, results:[…] }` | | `yt.streams { videoId }` | `ytStreams` | `{ ok, data:{ meta, audioUrl, qualities[] } }` | | `store.load {}` | `storeLoad` | playlists / history / settings | | `store.save { data }` | `storeSave` | `{ ok }` | Because the bridge is size-limited, the Zig handlers parse `yt-dlp`'s large JSON and return only the compact fields the UI needs. ## Prerequisites - **Zig** (0.14.x recommended) — - **zero-native** CLI: `npm install -g zero-native` - **Node.js** (only to run the yt-dlp downloader script) - No system `yt-dlp` needed — the setup script bundles it. (On Linux/macOS the bundled build is self-contained; Python is not required.) ## Setup & run ```bash # 1. Download the yt-dlp binary into ./bin npm run setup # or: node scripts/setup-ytdlp.js # 2. Generate the zero-native build files for your installed version # (build.zig, build.zig.zon, src/runner.zig). Run this in a scratch dir and # copy the generated build.zig / build.zig.zon next to this project, OR run # init here and keep your files: zero-native init ytplayer --frontend none # 3. Merge: keep THIS repo's frontend/, src/main.zig, src/bridge.zig and app.zon. # (src/main.zig shows exactly how the handlers are registered — fold that into # the generated App if the scaffold differs.) # 4. Build & launch the native window zig build run ``` To refresh yt-dlp later (YouTube changes often): `npm run update-ytdlp`. ## Notes & caveats - **zero-native is pre-1.0.** The bridge handler signature used here follows its documented contract (`fn(context, invocation, output) anyerror![]const u8`). If your installed version exposes the `Invocation` type or `BridgeDispatcher` fields slightly differently, only the wiring in `src/main.zig` and the `payloadField` helper need adjusting — the handler logic is self-contained. - Stream URLs from `yt-dlp` are IP-locked and expire after a few hours; the app re-fetches them each time you play a video, so this is transparent. - This is for **personal use**. Respect YouTube's Terms of Service and the rights of content creators. ## License MIT