ytmdl (C)
A terminal UI for downloading audio from YouTube / YouTube Music video, playlist, and album URLs, written in C with ncurses. All the actual extraction/downloading is done by shelling out to yt-dlp — this program is the queue, the TUI, and the orchestration around it.
Requirements
- gcc (or another C11 compiler) and
make ncurseswdevelopment headers (libncursesw5-dev/ncurses-develdepending on distro)pthread(part of glibc on Linux)yt-dlpon yourPATH— needs to be reasonablyrecent, since YouTube changes frequently break older releases. An outdated version can fail to resolve any playlist and surface confusing errors (e.g. "YouTube Music is not directly supported" or zero entries found) even though the URL itself is fine — it's the extractor being too old to parse YouTube's current page format, not an actual YouTube Music limitation. Update with
yt-dlp -Uorpip install -U yt-dlp../start.sh(from the repo root) automatically prefers the copy kept up to date in../.venv/binover an older system package, if present.- ffmpeg on your
PATH(yt-dlp uses it for audio extraction,thumbnail embedding, and metadata tagging)
- A Nerd Font in your terminal — the queue status column
and the folder browser use Nerd Font glyphs; without one they'll show as missing-glyph boxes
- A terminal with color support for the best look (color-coded status icons/progress
bars, focus-highlighted panel borders); the UI still works without it, just flatter
Build
make
Produces a single ytmdl binary in this directory.
Run
./ytmdl
or from the repo root: ./start.sh (builds if needed, then runs it).
Paste a video, playlist, or album URL into the URL field and press Enter. Playlists and albums are expanded into individual tracks, each downloaded and tagged separately, with up to 2 downloads running concurrently.
Options
./ytmdl [-o output_dir] [-f audio_format]
-o— where files are saved (default~/Music/ytmdl)-f— output audio format:mp3,m4a,flac,opus,wav,vorbis(defaultmp3)
Keybindings
Global (work no matter what has focus):
Ctrl+O— open a folder browser to change the download directoryUp/Down— move selection,Enter/Right— open the highlighted folder,Backspace/Left— go to the parent folder,s— select the currently browsed folder,Esc— cancel
Ctrl+F— toggle "create a folder per playlist/album" (on by default; named after theplaylist/album title; single videos are never nested)
Ctrl+N— clear finished: remove every successfully completed row from the queue(errored rows stay, so you can still see what failed and retry with
r)Ctrl+Q— quitTab— switch focus between the URL field and the queue table
URL field focused:
Enter— submit the URL
Queue table focused:
Up/Down— move the selected rowd/Delete— remove the selected row (not while it's actively downloading)r— retry a row that errored
Architecture
queue.c/.h— thread-safe queue ofQueueItems (id, url, title, subfolder, status,progress), a fixed-capacity array behind a mutex so worker threads can hold stable ids across UI-driven removals.
downloader.c/.h— spawnsyt-dlpviafork/exec, parsing structured--print-template output (unit-separator-delimited fields) to resolve playlists, and--newlineprogress lines to track download percent/speed/eta.app.c/.h— app state, settings (output dir, audio format, playlist-folder toggle), asmall fixed pool of worker threads pulling from a work queue, and the actions the UI calls into (submit URL, clear finished, remove/retry).
ui.c/.h,input_field.c/.h,dirpicker.c/.h— ncurses rendering and input handling.icons.h— Nerd Font glyph codepoints, written as\uXXXXescapes.
There is no equivalent of Textual's command palette in this version — every action has a direct, always-available keybinding instead.