Skip to main content

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 vlc package 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)

Source distribution for ytmusic-player-cli 0.1.0
File Size Uploaded
ytmusic_player_cli-0.1.0.tar.gz 38.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ytmusic-player-cli 0.1.0
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page