Spotify terminal UI — album art, cava audio visualizer, palette theming, lyrics. macOS/Linux, experimental Windows.
Project description
Disco Terminal
Turn your terminal into a disco. A Spotify TUI with album art, a real-time cava audio visualizer, full-app color theming, synced lyrics, and playback control — without leaving your shell or focusing the Spotify window.
pipx install discoterminal
See it move
Pick a palette and the whole app rethemes — visualizer, borders, buttons, labels:
Eight visualizer styles (area, bars, mirror, peaks, outline, dots, led, rain):
Synced lyrics follow the song, Spotify-style; the card flips between now-playing, artist info, and lyrics:
More demos & screenshots (search, playlists, playback, cards…)
Search anything
Playlists sidebar
Playback controls
Cards & pickers
Visualizer on / off
Built with Textual. Playback control picks the best backend for your platform automatically:
| Platform | Local control | Visualizer audio |
|---|---|---|
| macOS | shpotify (AppleScript) | BlackHole loopback (setup script included) |
| Linux | playerctl (MPRIS) | PulseAudio/PipeWire monitor — works out of the box |
| Windows (experimental) | Spotify Web API | cava's native WASAPI loopback |
The Spotify Web API (OAuth PKCE) powers your library, search, devices, queue,
and is the universal control fallback. Override backend selection with
{"backend": "shpotify" | "playerctl" | "webapi"} in config.
Features
- 🎨 Now playing card with album art (sixel/TGP/halfcell), live progress bar, and click-to-seek
- 📊 Audio visualizer — embedded cava, 8 render styles (area, bars, mirror, peaks, outline, dots, led, rain), 14 color palettes
- 🌈 Whole-app theming — pick a palette and every border, button, and label recolors to match
- 🔀 Card flip — artist info (genres, followers, popularity, top tracks) with artist photo
- 📻 Your playlists in a collapsible sidebar, fetched live from your account
- 🔍 Search anything — tracks, albums, playlists, artists — pick from a results modal
- 🎤 Lyrics via lrclib.net (no API key)
- ♥ Like/unlike the current track, ⏭ queue viewer, 📱 device switcher
- ⌨️ Everything keyboard-driven; playback via Web API so the Spotify app never steals focus
Install
macOS:
brew install shpotify cava # local control + visualizer
pipx install discoterminal # or: pip install discoterminal
discoterminal
Linux:
sudo apt install playerctl cava # or your distro's equivalent
pipx install discoterminal
discoterminal
Windows (experimental — testers wanted):
pip install discoterminal # control goes through the Web API
discoterminal # cava optional; needs a sixel-capable terminal
Requires Python 3.11+ and the Spotify desktop app. The Web API features (playlists, search, queue, lyrics card, artist info) need a Premium account — a Feb 2026 Spotify policy requires Premium for Web-API dev-mode apps. Local playback control via shpotify/playerctl works without Premium.
Spotify API setup (one time)
-
Go to the Spotify developer dashboard and create an app.
- If the Web API checkbox is greyed out on the create form (a known dashboard bug): create the app without it, then Edit the app and add Web API there — it won't be greyed out on edit.
-
Add redirect URI:
http://127.0.0.1:8888/callback -
Copy the Client ID into
~/.config/discoterminal/config.json:{ "client_id": "your-client-id-here" }
(or
export SPOTIPY_CLIENT_ID=...) -
First launch opens a browser to log in once; the token is cached after. No client secret needed — discoterminal uses the PKCE flow.
Visualizer audio setup (macOS only, optional)
Linux and Windows: nothing to do — cava captures system audio natively (PulseAudio/PipeWire monitor, WASAPI loopback).
macOS has no built-in way for apps to hear system output, so cava needs a loopback device:
brew install blackhole-2ch
sudo killall coreaudiod # load the driver
swift scripts/setup-audio.swift # create + activate a Multi-Output device
The script builds a "Disco Terminal Multi-Out" device (your speakers + BlackHole) via CoreAudio and switches the system output to it. discoterminal auto-detects BlackHole and points cava at it. Skip all this and the visualizer simply shows a hint instead.
Note: with a Multi-Output device active, macOS volume keys are disabled (aggregate-device limitation). Use discoterminal's
+/-— they control Spotify's own volume.
Turn it off / undo:
swift scripts/setup-audio.swift off # back to speakers (device kept)
swift scripts/setup-audio.swift remove # back to speakers + delete the device
Re-running without arguments turns it back on. Or just pick any output in System Settings → Sound.
Automatic mode: once the Multi-Out device exists, discoterminal switches to it
on launch and restores your previous output on quit — you only run the
setup script once, ever. Opt out with {"auto_multiout": false} in
config.json.
Keys
| Key | Action |
|---|---|
space |
Play / pause |
n / p |
Next / previous track |
+ / - |
Volume up / down |
l |
♥ Like / unlike current track |
i |
Flip card: song ↔ artist info |
y |
Lyrics |
u |
Queue viewer |
d |
Device switcher |
/ |
Search (Enter → results picker) |
b |
Toggle playlist sidebar |
v |
Toggle visualizer |
shift+V |
Visualizer style picker |
c |
Color palette / theme picker |
| click progress bar | Seek |
q |
Quit |
Startup arguments: discoterminal next, discoterminal <playlist name>, discoterminal play artist NF — opens the TUI and runs the action.
Config reference
~/.config/discoterminal/config.json:
| Key | Values | Default |
|---|---|---|
client_id |
Spotify app client ID | — |
visualizer_style |
area bars mirror peaks outline dots led rain |
area |
visualizer_colors |
aurora synthwave matrix fire ocean mono sunset vaporwave rainbow ice lava candy gold cyberpunk |
aurora |
art_renderer |
auto sixel tgp halfcell unicode |
auto |
auto_multiout |
true / false — switch to Multi-Out on launch, restore on quit (macOS) |
true |
backend |
shpotify playerctl webapi — override auto-selection |
auto |
Development
pip install -e ".[dev]"
pytest
ruff check src tests
mypy src
Tests run headless with mocked Spotify backends — no account or audio setup needed. CI covers Linux, macOS, and Windows.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file discoterminal-0.1.1.tar.gz.
File metadata
- Download URL: discoterminal-0.1.1.tar.gz
- Upload date:
- Size: 9.9 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d36e7ace61290f86c3405c3528d5695dacf9f930461c1620e5d12bd13cd9b61e
|
|
| MD5 |
16e3e2353fbbd7a2cfb98ddcc626415d
|
|
| BLAKE2b-256 |
d6b906c6d85e3b120487e687e67de9d1b5daaa393f4ca0679891181619202989
|
Provenance
The following attestation bundles were made for discoterminal-0.1.1.tar.gz:
Publisher:
publish.yml on VinnyVanGogh/discoterminal
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
discoterminal-0.1.1.tar.gz -
Subject digest:
d36e7ace61290f86c3405c3528d5695dacf9f930461c1620e5d12bd13cd9b61e - Sigstore transparency entry: 2064768847
- Sigstore integration time:
-
Permalink:
VinnyVanGogh/discoterminal@39019f9e1a09b92bf3550846cf5c8280b5552c6d -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/VinnyVanGogh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@39019f9e1a09b92bf3550846cf5c8280b5552c6d -
Trigger Event:
release
-
Statement type:
File details
Details for the file discoterminal-0.1.1-py3-none-any.whl.
File metadata
- Download URL: discoterminal-0.1.1-py3-none-any.whl
- Upload date:
- Size: 32.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
70926ec207c30016852a958476620df568bbd96035c94740f6b0d9c6c5ee5575
|
|
| MD5 |
1193a572bbd1a59950bf39123f93d3e7
|
|
| BLAKE2b-256 |
7970cd00aecd86d1e1594d6cb5bc59839ae92d67d57ee4e1a2a9505e9103dad4
|
Provenance
The following attestation bundles were made for discoterminal-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on VinnyVanGogh/discoterminal
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
discoterminal-0.1.1-py3-none-any.whl -
Subject digest:
70926ec207c30016852a958476620df568bbd96035c94740f6b0d9c6c5ee5575 - Sigstore transparency entry: 2064768860
- Sigstore integration time:
-
Permalink:
VinnyVanGogh/discoterminal@39019f9e1a09b92bf3550846cf5c8280b5552c6d -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/VinnyVanGogh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@39019f9e1a09b92bf3550846cf5c8280b5552c6d -
Trigger Event:
release
-
Statement type: