ytcli
first off, thank you for taking the time to check out the project. posting because i genuinely thought people would find it useful and/or neat. it’s literally a free TUI ytmusic player.
ytcli repo: github

what the heck does it do and why?
ytcli searches, browses, and plays from ytmusic right from your terminal; with the use of the innertube api key. requires no login. type, watch autocomplete fill in, hit enter, it streams to a “now-playing” footer with a little spectrum visualizer. single binary.
something interesting i learned while putting this thing together; there’s no public “search music” api, this isn’t the interesting part. however, the youtubei/v1 endpoints that the web app utilizes for devices has actually been pretty heavily reverse engineered to the point it was capable of being implemented as the WEB_REMIX client. this key can actually be found in every youtube music page. i thought that was neat. anyway, the filters for searching (songs, videos, albums, artists) are just ripped base64 protobuf tokens.
.songs => “EgWKAQIIAWoKEAkQBRAKEAMQBA%3D%3D”
.albums => “EgWKAQIYAWoKEAkQChAFEAMQBA%3D%3D”
obviously the innertube json is fucking insanely massive, heavily nested, and pretty undocumented (not publicly by google anyway). google can also alter it whenever they please. instead of taking on the task of modeling the scheme as zig structs, the extractor just walks the std.json.Value recursively and when it hits a musicResponsiveListItemRenderer tries to pull a track from it. if it fails you get ?Track. google can also restructure their surrounding envelope all they want; though having the renderer key allows search to theoretically keep working.
as for the “why?” uh.. because i wanted one idk. i’m also fairly new to zig, come from C and was looking for somewhat of an introductory project. i also spend a lot of time in the terminal and i spend a lot of time with a browser widow pulled up on ytmusic, i figured why not shell out processes and run this thing from a terminal as well, less overhead and it’s neat.
so it probably has dependencies right?
so, build.zig.zon has an empty dependency set. libmpv is linked C, and yt-dlp and curl are subprocesses.
- for now; as it seems std.http in 0.16 is still moving, libcurl linking was more than required and http is a curl subprocess atm.
- everything takes explicit allocation (arena=per request/gpa=long term)
- the temp writes’ request bodies that travel through
curl --data-binary @fileare written viamkstemp(using a random name, mode 0600) and are unlinked after use, as opposed to a predictable/tmp/ytcli_bodypath.
fun fact; the visualizer is total lie. it mimics a spectrum analyzer but there’s no FFT to be found. mpv runs astats lavfi filter on the audio graph. it reads the overall rms level via the api as a string: af-metadata/vis/lavfi.astats.Overall.RMS_level. dB is converted to linear; each of the 28 bars is modulated off the single envelope value. basically cheap spectrum using scalar.
supported zig versions
zig 0.16
ai / llm usage disclosure
claude code (sonnet 4.6) was used carefully and sparingly for drafting and refactoring implementation at my direction. it did also help serve as a sounding board while i sorted out the innertube implementation, architecture, design decisions; as i’m trying to better learn zig. every implemented line was read and tested by myself before it shipped. this program was created by a human for humans to serve a very particular purpose.