ytm-player

ytm-player

YouTube Music in your terminal — synced lyrics, vim keys, mpv backend.

Linux · macOS · Windows · Free-tier supported

▶ ytm-player
16.1k
516 this week
Downloads
419
GitHub stars
34 forks · 0 open issues
v1.9.5
latest release
today
contributors
synced lyrics vim keys free-tier MPRIS / macOS / Win Spotify import 18+ themes
$pip install ytm-player

Install mpv (Linux)

sudo pacman -S mpv   # Arch / Manjaro / CachyOS
sudo apt install mpv     # Ubuntu / Debian
sudo dnf install mpv     # Fedora

Install ytm-player

pipx install ytm-player

Launch

ytm

Notes

  • pipx isolates ytm-player in its own venv and adds the `ytm` command to PATH.
  • Required on PEP 668 distros (Debian 12+, Ubuntu 23.04+, Fedora 38+, recent Arch) where system pip is locked.
  • Install pipx first: `sudo apt install pipx` (Debian) / `sudo pacman -S python-pipx` (Arch) / `sudo dnf install pipx` (Fedora).

Configuration

ytm-player reads configuration from TOML files in ~/.config/ytm-player/ (respects $XDG_CONFIG_HOME):

File Purpose
config.toml General settings, playback, cache, UI, integrations
keymap.toml Custom keybinding overrides (full key list: docs/keybindings.md)
theme.toml App-specific color overrides on top of the active Textual theme
auth.json YouTube Music credentials (auto-generated by ytm setup)

Open the config directory in your editor:

ytm config

config.toml

Every section is optional — anything you don't set falls back to defaults.

[general]

[general]
startup_page = "library"     # library, search, browse
playback_bar_position = "bottom"  # "bottom" or "top"
brand_account_id = ""        # YouTube Brand Account ID (21-digit; find at myaccount.google.com/brandaccounts)
check_for_updates = true     # check PyPI once per 24h, surface a one-time toast on new version

[playback]

[playback]
audio_quality = "high"       # high, medium, low
prefer_audio = true          # prefer audio-only streams over video (audio uses less bandwidth)
default_volume = 80          # 0-100
autoplay = true              # auto-play next on track end
gapless = true               # preload the next track for gapless transitions
seek_step = 5                # seconds per +/- seek
api_timeout = 15             # seconds for ytmusicapi calls before failover
resume_on_launch = true      # restore last-playing track + position on app start; press space to continue

resume_on_launch (added v1.7.0) stages the last-playing track + position into the playback bar on startup. Press space to continue from where you were. Set to false to start fresh every time.

[cache]

[cache]
enabled = true
max_size_mb = 1024           # 1GB default LRU audio cache
prefetch_next = true         # resolve next track's stream URL in background for instant skip
location = ""                # blank = default cache dir; set a path to override

[yt_dlp]

[yt_dlp]
cookies_file = ""            # Optional: path to yt-dlp Netscape cookies.txt
ca_bundle = ""               # Optional: path to a custom CA cert bundle for SSL-inspecting
                             # corporate proxies (Zscaler, Netskope, etc.)
remote_components = ""       # Optional: ejs:npm/ejs:github (enables remote JS component downloads)
js_runtimes = ""             # Optional: bun, bun:/path/to/bun, node, quickjs, etc.
[search]
default_mode = "music"       # "music" (songs only) or "all" (all result types)
max_history = 500            # number of past search queries to remember
predictive = true            # show search suggestions as you type

[ui]

[ui]
theme = "ytm-dark"           # startup default Textual theme; runtime changes are saved in session.json
album_art = true             # show colored half-block album art in playback bar
progress_style = "block"     # block or line
sidebar_width = 30
col_index = 4                # 0 = auto-fill width
col_title = 0                # 0 = auto-fill
col_artist = 0               # 0 = auto-fill
col_album = 0                # 0 = auto-fill
col_duration = 8
bidi_mode = "auto"           # auto, reorder, passthrough — RTL text handling
region = "ZZ"                # ISO 3166-1 alpha-2 (or "ZZ" = Global, default) — Browse → Charts. 68 regions selectable; locale-style codes like "ES-ES" auto-normalise to "ES".
home_shelves = 3             # number of recommendation shelves on Browse → For You (1–25)
show_selection_info = true   # show focused-item full name in the row above the playback bar
sidebar_overflow = "truncate"  # "truncate" (1-row + ellipsis) or "wrap" (multi-line names)
show_queue_source = true     # show "Generated from: …" header on radio/discovery queues

theme is the startup default. Changing theme from Textual's command palette (Ctrl+PTheme) updates the current session and is saved in session.json, not config.toml. To make the active theme the new default, run Ctrl+PSet Current Theme as Default.

Per-playlist Shuffle lock state (set via the Shuffle lock toggle in the Library page playlist header) is persisted separately to ~/.config/ytm-player/shuffle_prefs.json. There's nothing to configure in config.toml for it.

[notifications]

[notifications]
enabled = true
timeout_seconds = 5
format = "{title} — {artist}"  # template for the now-playing notification body

[mpris]

[mpris]
enabled = true

[discord]

[discord]
enabled = false              # requires `pip install ytm-player[discord]`
client_id = ""               # blank uses the bundled app; set your own
                             # Discord application ID to publish under it

[lyrics]

[lyrics]
transliteration = false      # transliterate non-Latin lyrics to ASCII (requires
                             # `pip install ytm-player[transliteration]`)

[lastfm]

[lastfm]
enabled = false              # requires `pip install ytm-player[lastfm]`
api_key = ""
api_secret = ""
session_key = ""
username = ""

[logging]

[logging]
level = "WARNING"            # DEBUG, INFO, WARNING, ERROR, CRITICAL
max_bytes = 5242880          # 5 MB per log file before rotation
backup_count = 3             # number of rotated logs to keep
keep_crashes = 10            # max number of crash files to retain in crashes/

theme.toml

Base colors (primary, background, etc.) come from the active Textual theme. Choose the startup default with [ui] theme, switch the current session with Ctrl+PTheme, and use theme.toml for app-specific color overrides:

[colors]
playback_bar_bg = "#1a1a1a"
selected_item = "#2a2a2a"
progress_filled = "#ff0000"
progress_empty = "#555555"
lyrics_played = "#999999"
lyrics_current = "#ff4e45"   # defaults to the theme accent if unset
lyrics_upcoming = "#aaaaaa"
active_tab = "#ffffff"
inactive_tab = "#999999"

The lyrics_current color falls back to the active theme's accent (and then to #ff4e45 red as the absolute last-resort default). Override only if you want something different from your theme's accent.

keymap.toml

For custom keybinding overrides, see docs/keybindings.md for the full key list and the customization syntax.

68 matches

Keyboard

Playback

KeyAction
spacePlay / Pause
nNext track
pPrevious track
.Play random
Ctrl+rCycle repeat mode (off → all → one)
Ctrl+sToggle shuffle
+Volume up
-Volume down
_Mute
>Seek forward
<Seek backward
^Seek to start
lToggle like on currently playing track
TToggle lyrics transliteration (ASCII)

Navigation

KeyAction
jMove down
kMove up
Ctrl+fPage down
Ctrl+bPage up
gthengGo to top
GGo to bottom
enterSelect / play
tabFocus next panel
Shift+tabFocus previous panel
backspaceGo back
Shift+backspaceGo forward (after a back)
escapeClose popup / overlay
qQuit

Pages

KeyAction
gthenlGo to Library
gthensGo to Search
gthenbGo to Browse
gthenyGo to Liked Songs
gthenrGo to Recently Played
gthenLToggle lyrics sidebar
gthenspaceGo to current context (artist/album/playlist)
gthencJump to currently playing track
zGo to Queue
?Help (full keybinding reference inside the app)
DDiscovery roulette (random mix from 7 sources)

View

KeyAction
Ctrl+eToggle playlist sidebar
Ctrl+aToggle album art in playback bar

Actions

KeyAction
aTrack actions menu
gthenAContext actions (artist/album/playlist menu)
gthenaSelected items actions
ZAdd to queue
AAdd to playlist
deleteDelete current item
dthendDelete current item (vim-style)

Sort & filter

KeyAction
/Filter current list
sthentSort by Title
sthenaSort by Artist
sthenASort by Album
sthendSort by Duration
sthenDSort by Date
sthenrReverse current sort

Misc

KeyAction
cPick chart region (Browse → Charts only)

Mouse

Playback bar

ActionSurfaceEffect
ClickProgress barSeek to position
Scroll up/downProgress barScrub forward/backward (commits after 0.6s pause)
Scroll up/downVolume displayAdjust volume by 5%
ClickRepeat buttonCycle repeat mode (off → all → one)
ClickShuffle buttonToggle shuffle on/off
ClickHeart buttonToggle like on currently playing track
ClickFooter buttonsNavigate pages, play/pause, prev/next

Track tables

ActionSurfaceEffect
ClickTrack rowPlay that track
Right-clickTrack rowOpen track actions popup

Headers & nav

ActionSurfaceEffect
Click← Back / Forward →Navigate history (auto-show/hide based on stack)
ClickPlaylist header Shuffle lockToggle per-playlist forced shuffle
ClickLiked Songs / Recently Played [▶ Start Radio]Seed radio from 5 random tracks

Charts

ActionSurfaceEffect
ClickCharts shelf pillsSwitch between chart shelves (e.g. Top 100 → Trending)

CLI Reference

ytm-player has three modes:

  • TUI (default) — ytm launches the interactive terminal UI.
  • CLI — headless subcommands that work without the TUI running (search, stats, history, cache).
  • IPC — control a running TUI from another terminal (play, pause, next, queue control).

Windows: replace ytm with py -m ytm_player in any of the commands below.

Setup

ytm setup                    # Auto-detect browser cookies
ytm setup --browser firefox  # Target a specific browser (chrome, firefox, brave, edge, chromium, vivaldi, opera, helium)
ytm setup --manual           # Skip detection, paste raw request headers
ytm search "daft punk"
ytm search "bohemian rhapsody" --filter songs --json

Available filters: songs, videos, albums, artists, playlists, community_playlists, featured_playlists.

Stats and history

ytm stats                    # Listening stats summary
ytm stats --json             # Machine-readable
ytm history                  # Recent play history
ytm history search           # Recent search history

Cache management

ytm cache status             # Cache size + entry count
ytm cache clear              # Wipe all cached audio

Playback control (IPC, requires TUI running)

ytm play                     # Resume playback
ytm pause                    # Pause playback
ytm next                     # Skip to next track
ytm prev                     # Previous track
ytm seek +10                 # Seek forward 10 seconds
ytm seek -5                  # Seek backward 5 seconds
ytm seek 1:30                # Seek to 1:30 (m:ss or h:mm:ss)

Like / dislike (IPC)

ytm like                     # Like current track
ytm dislike                  # Dislike current track
ytm unlike                   # Remove like/dislike (sets to INDIFFERENT)

Status (IPC)

ytm now                      # Current track info (JSON)
ytm status                   # Player status (JSON)
ytm queue                    # Queue contents (JSON)
ytm queue add VIDEO_ID       # Add track by video ID
ytm queue clear              # Clear queue

Spotify import

ytm import "https://open.spotify.com/playlist/..."

Interactive flow — see docs/spotify-import.md.

Diagnostics

ytm doctor                   # 8-section diagnostic report (version, paths, config, deps, logs, crashes, env, settings — secrets redacted)
ytm config                   # Open config dir in your editor
ytm --debug                  # Launch with verbose logging

How it works

1
Extract
Reads track names + artists from the Spotify playlist.
2
Match
Searches YouTube Music with fuzzy matching (60% title + 40% artist weighted score).
3
Resolve
Tracks scoring 85%+ are auto-matched. Lower scores prompt you to pick from candidates or skip.
4
Create
Creates a new private playlist on your YouTube Music account with all matched tracks.

Two import modes

Single mode

Up to ~100 tracks. Best for most playlists.

How: paste one Spotify playlist URL.

Multi mode

100+ tracks. For large playlists, the importer splits the URL list across multiple calls.

How: enter a name + number of parts, then paste a URL for each part.

Run from the TUI or the CLI

From the TUI

Click Import in the footer (or press the import button). A popup lets you paste URLs, choose single or multi mode, and watch import progress in real time.

From the CLI

One-shot import without opening the TUI:

ytm import "https://open.spotify.com/playlist/37i9dQZF1DXcBWIGoYBM5M"

Interactive flow: fetches tracks, shows match results, lets you resolve ambiguous matches, names the playlist, then creates it.

Extraction methods

1. Spotify Web API

Full pagination, handles any playlist size. Requires a free Spotify Developer app (you set up client_id + client_secret in ~/.config/ytm-player/spotify.json).

→ falls back to →

2. Scraper fallback

No credentials needed. Limited to ~100 tracks. Used automatically if Spotify API credentials aren't configured.

Try the parser

Paste a Spotify playlist URL — see how the importer extracts the playlist ID locally (no network call):

What's going wrong?

Pick the category that best matches your problem. The wizard will narrow it down step by step.

File tree

Click any file to view its source. Updated at every build from the pinned tag.

Stack

Key patterns

Recent releases

View all →

Built and maintained by naame.co — independent dev shop. ytm-player is one of several open-source projects shipped from there.