CLI tool
xdagiz/xytz avatar
xdagiz/xytz

xytz: a terminal YouTube browser built on yt-dlp and Bubble Tea

A Beautiful YouTube Downloader/Player TUI

651 stars36 forksGoMIT

At a glance

What is it?
xytz is a Go terminal interface for searching YouTube, browsing channels and playlists, choosing formats, batch downloading and playing videos through mpv, with the actual fetching left to yt-dlp. It is a front end rather than a downloader, and most of its behaviour is configured in a YAML file.
Who is it for?
Adopt xytz if you live in the terminal and want to search, browse and download video without leaving it, and you are comfortable with yt-dlp and ffmpeg as external dependencies. Do not adopt it expecting a standalone downloader, since it delegates all fetching to yt-dlp and will fail without it.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 7 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A terminal front end that delegates all the work to yt-dlp

xytz presents itself simply: a TUI for searching and downloading or playing videos from YouTube and other sites. The subtitle in the project description is a YouTube downloader and player TUI.

The important architectural point is in the requirements section. yt-dlp is listed as the core video downloader, and ffmpeg is required for the full feature set. So xytz is not implementing a media pipeline. It is the interactive layer, and yt-dlp does the retrieving and ffmpeg does the merging, converting and metadata work.

That split has direct consequences. The feature list includes downloading from any yt-dlp supported site by pasting a URL, which means the set of sites xytz can handle is whatever yt-dlp handles, not a fixed list in this project. And the mpv entry is marked optional, for playing videos directly without downloading, with ffplay marked as an optional fallback bundled with ffmpeg, used automatically when mpv is not installed. The player backend is a config key, with a note that setting player: ffplay in the config forces it explicitly.

There is a second external dependency worth knowing about, and it is easy to miss. The config includes js_runtime and js_runtime_path, described as the JavaScript runtime for yt-dlp, with the options deno, node, bun and quickjs. Some sites need a JS runtime to serve their video data, and xytz lets you point yt-dlp at one.

The project is written in Go, with a go.mod that requires Go 1.26, and the module is github.com/xdagiz/xytz. The user interface comes from the Charm stack, with bubbletea, bubbles, lipgloss and log from the charm.land module path, plus the charmbracelet packages for the terminal itself.

Installing it six different ways, depending on your platform

The distribution story is unusually thorough, which says something about the audience: people who install things.

There is an installer script for Linux and macOS:

bash
curl -fsSL https://raw.githubusercontent.com/xdagiz/xytz/main/install.sh | bash

Homebrew is supported through a tap:

bash
brew tap xdagiz/homebrew-tap
brew install --cask xytz

Arch users have an AUR package, and the README gives both helper names:

bash
paru -S xytz-bin # or yay -S xytz-bin

Windows has a Scoop bucket:

bash
scoop bucket add xdagiz https://github.com/xdagiz/scoop-bucket.git
scoop install xdagiz/xytz

For Go developers there is a direct install, and for Nix users there is a flake with three verbs, run without installing, build in the current repo, and enter a development shell that includes Go, gopls, yt-dlp, ffmpeg and mpv:

bash
# Run without installing
nix run github:xdagiz/xytz

# Build in the current repo
nix build

# Enter a development shell (Go, gopls, yt-dlp, ffmpeg, mpv)
nix develop

And for the source build, Go 1.25 or later is the stated requirement:

bash
# Clone the repository
git clone https://github.com/xdagiz/xytz.git
cd xytz

# Build
go build -o xytz .

# Move to your PATH (optional)
sudo mv xytz /usr/local/bin/

Note the two Go versions in play, the go.mod requiring 1.26 and the build instructions saying 1.25 or later. The lower figure is what the README asks of you, the higher is what the module file declares, and the module file is the one that will refuse to compile on an older toolchain.

The release configuration lives in a .goreleaser.yaml, and there is a .golangci.yaml for linting, a mise.toml for tool versions and a flake.nix with flake.lock for the Nix path.

The command set: search, channels, playlists and the slash commands

The interactive surface is a set of slash commands, and the same capabilities are available as flags for quick access from a script.

The command line examples are short:

bash
# Direct video search
xytz -q "golang tutorial"

# Browse a specific channel
xytz -u @username

# Browse a playlist
xytz -p PLplaylistId

Inside the TUI the commands are spelled out. You browse channel videos with /channel @username, find channels with /channels and a query, and browse and download playlist videos with /playlist and an id. Search results are limited by a config value, search_limit, whose default is 25.

Beyond browsing, the commands cover the workflow of getting media down. You pick from the available video and audio formats with quality indicators, you select multiple videos and download them in one batch, you resume unfinished downloads with /resume, you stash videos for later with /later, and you play a video with mpv without downloading it using /play and a URL. You can switch themes at runtime with /theme and a name.

Two details show real use. Search history is persistent, so a previous query is one keystroke away, and thumbnails preview inline in the terminal, with a configurable protocol, a timeout and a quality setting. The thumbnail protocol options are kitty, sixel, iterm2, halfblocks and auto, with auto as the default, and the timeout defaults to 2500 milliseconds. The auto mode exists because terminal thumbnail support is fragmented, and picking a protocol per terminal is a real annoyance otherwise.

The flag list is not documented in the README beyond the examples, so xytz --help is the reference for the full set.

The YAML config file, and the keys that change what gets fetched

On first run xytz creates the config file with default values if it does not exist, and the location follows the conventions of each platform: the XDG path on Linux, the same path on macOS when XDG_CONFIG_HOME is set and the Application Support path otherwise, and %APPDATA% on Windows.

The default configuration is long enough that it is worth reading as a feature list, because most of the keys are switches someone will want to flip:

yaml
search_limit: 25 # Number of search results
default_download_path: ~/Videos # Download destination
spotify_download_path: ~/Music # Spotify download destination
default_quality: best # Default format selection (480p, 720p, 1080p, 4k...)
sort_by_default: relevance # Default sort: relevance, date, views, rating
theme: catppuccin-mocha # Preset theme name
video_format: mp4 # The format which videos are downloaded
audio_format: mp3 # The format which audio files are downloaded
embed_subtitles: false # Embed subtitles in downloads
embed_metadata: true # Embed metadata in downloads
embed_chapters: true # Embed chapters in downloads
embed_thumbnail: false # Embed thumbnail in downloads
download_lyrics: false # Save .lrc next to Spotify songs and albums via lrclib, off by default
ffmpeg_path: "" # Custom ffmpeg path (optional)
yt_dlp_path: "" # Custom yt-dlp path (optional)
cookies_browser: "" # Browser for cookies: chrome, firefox, etc (optional)
cookies_file: "" # Path to cookies.txt file for authentication (optional)
thumbnail_preview: true # Enable thumbnail preview in video list
list_compact_mode: false # Compact list rows in video, channel, and format lists
thumbnail_timeout_ms: 2500 # Timeout for fetching thumbnails (ms)
thumbnail_protocol: auto # Thumbnail protocol: kitty, sixel, iterm2, halfblocks, auto (default: auto)
thumbnail_quality: max # Thumbnail quality: max, high, medium, low (optional, default: max)
js_runtime: "" # JS runtime for yt-dlp: deno, node, bun, quickjs (optional)
js_runtime_path: "" # Custom path to JS runtime executable (optional)
player: mpv # Playback backend: mpv, ffplay (falls back to ffplay if mpv isn't installed)
background_playback: false # Keep player running when leaving the player view (background playback)
check_for_updates: true # Check for new releases in the TUI (banner)

Several of these deserve attention. The quality setting accepts 480p, 720p, 1080p and 4k, and sort_by_default offers relevance, date, views and rating. The embed options control what ffmpeg writes into the finished file, with metadata and chapters on and subtitles and thumbnails off by default.

The two that change network behaviour are download_lyrics and the thumbnail preview. The explanation for lyrics is given plainly: it adds a network call to every download, so it is off by default, and an existing config keeps the old value until you flip it. The thumbnail preview is on by default, which means xytz is fetching images for the list whether or not you need them, and you can turn that off with thumbnail_preview: false.

The cookie settings are how you reach content that requires a session, either by naming a browser or by pointing at a cookies.txt file. That is the mechanism for authenticated downloads, and it is the setting to be careful with, because a cookies file is a credential.

Spotify support, and the limits of the project

One feature stands out as unusual for a tool framed around video: fetching and downloading Spotify tracks or whole albums, described as multi-disc aware, tagged and with cover art. Downloads go to a separate path configured by spotify_download_path, and the lyrics option writes a .lrc file next to each track via lrclib.

That the same binary handles both a video site and a music streaming service tells you something about how xytz is built. It is a terminal interface over yt-dlp, and Spotify extraction is something the underlying tool provides. The multi-disc awareness and the tagging are xytz adding structure on top.

Now the limits, which are worth stating clearly.

First, xytz depends entirely on tools that are maintained elsewhere. When a site changes, yt-dlp is what has to change, and xytz inherits the result. When a site changes, you wait for a yt-dlp release, not an xytz release. The dependency is real and it is a feature for the user, since the hard work is not duplicated, but it also means the release cadence of xytz and the capability of the tool are decoupled.

Second, the README does not describe a plugin or extension mechanism, a headless or non-interactive mode beyond the flags, or a way to add sites. The site list is yt-dlp's.

Third, there is no API and no library surface. This is an application. The go.mod shows cobra for the command line and the Charm libraries for the interface, plus a fuzzy matching library for search, a clipboard helper, an image library for terminal images, a YAML parser and a self-update library, which pairs with the check_for_updates option that shows a banner when a new release is out.

The releases themselves are frequent: v0.9.3 on 2026-09-07, v0.9.2 on 2026-07-26 and v0.9.1 on 2026-06-22, with the last push on 2026-10-16. The licence is MIT, and the project states that it works on Linux, macOS and Windows.

Editorial conclusion

Adopt xytz if you live in the terminal and want to search, browse and download video without leaving it, and you are comfortable with yt-dlp and ffmpeg as external dependencies. Do not adopt it expecting a standalone downloader, since it delegates all fetching to yt-dlp and will fail without it. Before your first run, install yt-dlp and ffmpeg, decide whether you want mpv or the bundled ffplay as the player, and check the config file that xytz creates on first run, since options such as download_lyrics and embed_thumbnail each add a network call or a processing step.

Frequently asked questions

What does xytz need installed to work?

yt-dlp as the core downloader and ffmpeg for the full feature set. mpv is optional and used to play videos without downloading, with ffplay bundled with ffmpeg as an automatic fallback, and a JavaScript runtime such as deno, node, bun or quickjs can be configured for yt-dlp.

How do I install xytz?

There is a curl installer script for Linux and macOS, a Homebrew cask, an AUR package named xytz-bin, a Scoop bucket, a Go install with go install github.com/xdagiz/xytz@latest, and a Nix flake with nix run. Building from source needs Go and runs go build -o xytz .

Where is the xytz config file?

On Linux it is ~/.config/xytz/config.yaml, or $XDG_CONFIG_HOME/xytz/config.yaml. On macOS it is the same path if XDG_CONFIG_HOME is set and otherwise ~/Library/Application Support/xytz/config.yaml. On Windows it is %APPDATA%/xytz/config.yaml. xytz creates it with defaults on first run.

Can xytz play a video without downloading it?

Yes. The /play command with a URL plays through mpv, and the player key in the config selects the backend, either mpv or ffplay, with ffplay used automatically when mpv is not installed.

Does xytz support Spotify?

Yes, the feature list includes fetching and downloading Spotify tracks or whole albums, with multi-disc awareness, tagging and cover art, and an optional .lrc lyrics file saved next to each track via lrclib, off by default.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. xdagiz/xytz on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/xdagiz-xytz.svg)](https://hysenlabs.com/projects/xdagiz-xytz)

Was this useful?