Commit Graph
7 Commits
Author SHA1 Message Date
vincent 9e9f71b31b perf: run yt-dlp from a zipapp on a portable Python
Replaces the official yt-dlp_macos sidecar. That build is PyInstaller
one-file and unpacks 37MB on every invocation, costing ~8s per call on
this machine — ruled out signing, xattrs and thinning the universal
binary as causes, and it is 8% CPU over 8s, so it is the unpack itself.
Every stream resolve paid it.

The 3MB zipapp on a trimmed portable CPython starts in ~0.45s, a 17x
improvement; a real stream resolve goes from ~10s to ~2.4s. macOS ships
only Python 3.9 and yt-dlp needs 3.10+, hence bundling an interpreter.

yt-dlp is now resolved as an argv prefix rather than a path, so a system
install (a plain script, instant) still takes precedence.

Bundle grows 153MB -> 219MB, which buys back the responsiveness.
2026-08-29 12:07:09 +02:00
vincent 3296b5436a feat: bundle yt-dlp and ffmpeg as sidecars
The app is now self-contained: yt-dlp, ffmpeg and ffprobe ship inside
the bundle and are resolved beside the executable, so a fresh Mac needs
nothing installed. A system copy still wins when present, which is how
to run a newer yt-dlp than the bundled one. yt-dlp is pointed at the
bundled ffmpeg explicitly, since a bundled app has no reason to have one
on PATH.

The binaries stay out of git (~140MB); scripts/fetch-binaries.sh pulls
them. Homebrew's ffmpeg cannot be used — it links a dozen dylibs and
does not relocate — so the static arm64 build is used instead.

Version checks moved to the async Command. yt-dlp is a PyInstaller
bundle that unpacks ~37MB on first run, and the blocking call was
stalling a runtime worker for roughly twenty seconds.

Bundle size goes from 19MB to 153MB.
2026-08-29 11:41:14 +02:00
vincent 5b09acd28d feat: 4K downloads, watch progress, player controls, new icon
Window chrome: titleBarStyle Overlay (Transparent left the native bar in
place, white in dark mode, with a dead strip beneath it). The app now
paints that strip itself, so it matches the page background.

Player: prev/next through the feed, a full-screen button, a spinner
while a stream resolves, Open on YouTube on downloaded videos too, and
the description collapsed behind a disclosure.

Downloads default to Best, which reaches real 4K — above 1080p YouTube
serves VP9/AV1, verified to play natively in WKWebView here. Audio stays
pinned to AAC because Opus in MP4 would be silent. A Compatible setting
keeps the old 1080p H.264 behaviour. Files are now named
'<ISO date> - <title> [<id>].mp4'.

Watch progress is recorded and drawn under thumbnails like YouTube's,
and reopening a video resumes where it left off.

Tiles clamp every text line to a fixed height so they share a baseline,
and the search field no longer clips its placeholder.

Fixes a Picture-in-Picture leak: WebKit kept a detached video playing
after the player closed, so a second video could play over the first
with no way to stop it. Every exit path now tears the element down.
2026-08-29 04:03:45 +02:00
vincent f47bc3dab1 feat: tile view, in-app streaming, and self-painted title bar
Clicking an undownloaded video now plays it in the app instead of
handing off to the browser. YouTube's iframe embed cannot be used —
it rejects a tauri:// origin with Error 153 — so yt-dlp resolves
YouTube's HLS master playlist instead, whose H.264+AAC variants
AVFoundation streams natively in WKWebView, adaptive up to 1080p.

Adds an optional Tiles view alongside the list, remembered between
launches.

The title bar is now transparent with the title hidden, and the app
paints that strip itself in the page background so it matches instead
of showing macOS chrome beside the traffic lights.
2026-08-29 03:24:24 +02:00
vincent 11331671c5 fix: ffmpeg version detection and player letterboxing
ffmpeg only accepts -version (single dash); it exits 8 on --version and
writes to stderr, so the status panel reported it missing even when
installed. Version flags are now per-tool and both streams are read.

The player used max-h-full inside a grid, which a 1080x1920 Short
overflowed. Switched to absolute fill with object-contain so portrait
and landscape both letterbox correctly.

Also sized the window to 1180x780 centered, which fits a laptop display.
2026-08-29 02:44:39 +02:00
vincent a30423e4b5 feat: Tauri commands, concurrent refresh, download manager
Refresh fetches 8 channel feeds concurrently and mirrors thumbnails to
disk so the offline feed still renders. Downloads are capped at 2 and
shell out to yt-dlp with an absolute binary path, since GUI apps do not
inherit a login shell PATH.
2026-08-29 02:28:26 +02:00
vincent 6fdad60190 feat: scaffold Tauri + React + Tailwind v4 2026-08-29 02:21:25 +02:00