YTMusic TUI
A keyboard-driven, audio-only terminal music player for YouTube, built with Python, Textual, yt-dlp, and VLC.
Search YouTube, queue tracks, and keep local profiles and playlists — without leaving the terminal and without loading video.
Unofficial. This is a personal hobby project and is not affiliated with, endorsed by, or sponsored by Google LLC, YouTube, or YouTube Music. Please read the Disclaimer before using it.
Features
- Audio-only streaming: resolves and plays the audio stream, so no bandwidth is spent on video.
- Search-first TUI: search is the landing mode; now-playing chrome stays visible in every mode.
- Playback queue: play, append, skip, remove, and save the queue as a playlist.
- Local playlists: create, edit, and open playlists backed by a local SQLite database.
- Local profiles: multiple profiles, each with its own preferences and playlists.
Roadmap
- Offline downloads and local-file playback.
- Algorithmic recommendations, possibly.
Not implemented
- Seeking within a track, and metadata embedding.
- YouTube sign-in and library sync. Profiles are local; they are not YouTube accounts, so your YouTube library, likes, and history are out of reach.
Requirements
- Python 3.11 or newer
- libVLC on the system — the
vlcpackage on Linux, VLC on macOS and Windows - pipx (recommended) to install the player in its own environment
python-vlc is only a binding; playback fails without a system libVLC and a working audio device. You do not need uv to install or run the player; uv is for contributors.
Installation
pipx install ytmusic-player-cli
ytmusic-tui
From git, still without uv:
pipx install git+https://github.com/hakanayaz14159/ytmusic-tui.git
pip works the same way (pip install git+https://... or pip install .) if you would rather put the package in an existing virtualenv.
Usage
ytmusic-tui # launch the TUI
ytmusic-tui --version
ytmusic-tui --help
Press ? in the app for the keymap: 1–5 switch modes and / jumps to the query field.
When YouTube breaks stream extraction, refresh yt-dlp inside the pipx environment:
pipx upgrade ytmusic-player-cli
# or only yt-dlp:
pipx runpip ytmusic-player-cli install -U yt-dlp
Your data
Everything lives on your machine. Profiles, playlists, and the track metadata they reference are stored in a single SQLite file in the platform data directory (~/.local/share/ytmusic-tui/ytmusic.db on Linux). There is no account, no server, and no telemetry.
File logging is off by default. Enable it when you want to report a bug:
YTMUSIC_LOG=1 ytmusic-tui # writes a timestamped log next to the database
Logs redact query strings from stream URLs and the Cookie and Authorization headers, but skim a log before attaching it to an issue.
Architecture
The project follows a hexagonal (ports and adapters) layout — the domain core does not import Textual, VLC, yt-dlp, or Peewee:
ytmusic_tui/
├── db/ # Persistence adapters (Peewee SQLite models & repositories)
│ ├── user.py # Profile & user account entities
│ ├── playlist.py # Playlists & track associations
│ └── song.py # Track metadata persistence
├── music/ # Domain types, ports, services, external adapters
│ ├── types.py # Domain models and TypedDicts
│ ├── ports.py # Protocols implemented by the adapters
│ ├── services.py # Search, playback, playlist, account, settings use cases
│ ├── youtube.py # yt-dlp adapter for search and stream extraction
│ ├── stream_proxy.py # Local HTTP bridge for stream request headers
│ └── state.py # Reactive application state
├── tui/ # Presentation layer (Textual shell, modes, widgets)
│ ├── shell.py # Persistent chrome: modes, now-playing, status
│ ├── modes/ # Search, Queue, Playlists, Profiles, Settings
│ ├── widgets/ # Mode bar, song table, now playing
│ └── modals/ # Help, prompts, confirmations
└── main.py # Application entry point and CLI command
YoutubeDoc.md explains how search, stream resolution, and the local stream proxy actually work, including why VLC is pointed at a localhost proxy. AGENTS.md is the engineering spec the codebase is held to.
Development
Contributors can use uv:
uv sync --all-groups
make test # uv run pytest
make test-cov # coverage report
make lint # ruff format --check, ruff check, mypy
make format # ruff format + ruff check --fix
make dev # textual run --dev for the live console
The suite runs fully offline: YouTube and audio adapters are mocked, databases are in-memory SQLite, and the TUI is driven by Textual's headless pilot. The handful of live YouTube contract checks are opt-in because they depend on the network and on YouTube not having changed:
uv run pytest tests/test_youtube_contract.py -m network
Contributions follow test-first development, strict mypy, and Conventional Commits; the details are in AGENTS.md.
Releasing
PyPI accepts each version once. A GitHub Release whose tag matches __version__ (for example v0.1.1 after bumping ytmusic_tui/__init__.py to 0.1.1) runs .github/workflows/publish.yml and uploads the wheel.
Project status
Alpha, and a personal project I maintain for my own listening. Bug reports and pull requests are welcome, but there is no roadmap, no release schedule, and no support commitment. Since playback depends on yt-dlp keeping up with YouTube, expect the occasional breakage and keep yt-dlp up to date (pipx upgrade ytmusic-player-cli, or uv lock --upgrade-package yt-dlp in a contributor checkout).
Disclaimer
Not affiliated with Google. YTMusic TUI is an independent, unofficial project. It is not affiliated with, endorsed by, sponsored by, or in any way officially connected to Google LLC, YouTube, or YouTube Music. "YouTube" and "YouTube Music" are trademarks of Google LLC and are used here only to describe what this software interoperates with.
No accounts, no API keys. The player does not use the YouTube Data API and never signs you in. It resolves publicly available audio streams through yt-dlp, exactly as yt-dlp would on the command line.
Nothing is downloaded or redistributed. Audio is streamed for playback only — no media files are written to disk, and this repository contains no media content.
You are responsible for your own use. Accessing YouTube is subject to YouTube's Terms of Service and to the copyright law of your jurisdiction. This project is published for personal, educational use; make sure the way you use it is permitted where you are.
Provided as-is. The software comes with no warranty of any kind, as stated in the MIT License. If YouTube changes how streams are served, playback can stop working without notice.
Acknowledgements
Built on Textual, yt-dlp, python-vlc, Peewee, and Click.
License
MIT — see LICENSE.
Metadata
Release files for ytmusic-player-cli 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ytmusic_player_cli-0.1.0.tar.gz | 38.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ytmusic_player_cli-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 93.9 kB
Release files / ytmusic_player_cli-0.1.0.tar.gz
| Download URL | ytmusic_player_cli-0.1.0.tar.gz |
|---|---|
| Size | 38.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0836898f6eab83bac02c7283d261b108ad16ac6cf15996dc52f405cbffb84673
|
|
BLAKE2b-256 checksum How to use checksums |
57e5d8cb757cd5507cdfa3f567b5cb467cd2491c68f2a6d4ad17e401c3786e92
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 8, 2026.
Transparency logRelease files / ytmusic_player_cli-0.1.0-py3-none-any.whl
| Download URL | ytmusic_player_cli-0.1.0-py3-none-any.whl |
|---|---|
| Size | 55.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
de34a66be949155fbe6274e72fdb9baeeb6bc3fa89b0039d7dfda08c45b2c80e
|
|
BLAKE2b-256 checksum How to use checksums |
d3ea2624b409ee8ab14ca7877c3e7835c80bb2fd48b844871f76e580c9d355ee
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 8, 2026.
Transparency log