A Python terminal UI (TUI) for exploring your Spotify listening data. Built with Rich, Spotipy, and questionary — runs entirely in your terminal with arrow-key navigation, live animations, themed colors, and direct Spotify playback control.
- What It Does
- Prerequisites
- Installation
- Configuration
- Running
- Features
- Theming
- Re-authorization / Scope Changes
- Legacy Scripts
- Project Structure
- Known Caveats
track-operator is a terminal application — launch it, log in with Spotify once, and get an interactive menu with:
- Your top tracks and top artists across three time windows
- Your recent listening history
- Playlist diffing — compare two playlists side by side
- Live Now Playing screen with a progress bar and full transport controls (play/pause/skip/volume/shuffle/repeat), plus a Matrix rain backdrop mode
- A Matrix digital rain easter egg with live controls
To use Track Operator, you need a Spotify Developer App. It takes about 2 minutes to set up:
- Log in to the Spotify Developer Dashboard.
- Click "Create App":
- App name:
Track Operator(or anything you like) - App description:
Personal Spotify TUI - Redirect URI:
http://127.0.0.1:8888/callback(This is critical for local authentication)
- App name:
- Select Web API as the API you are using.
- Save the app.
- Click "Settings" on your new app to find your Client ID and Client Secret.
git clone https://github.com/allcapsjoe/track-operator.git
cd track-operator
# Install the package
pip install -e .
# Run the app
trackopOn your first run, trackop will detect that you haven't configured your credentials yet. It will launch an interactive wizard in your terminal to help you save your Client ID and Client Secret to a .env file automatically.
If you prefer to do it yourself, create a .env file in the project root:
CLIENT_ID=your_client_id_here
CLIENT_SECRET=your_client_secret_here
REDIRECT_URI=http://127.0.0.1:8888/callbackconfig.toml in the project root controls visual settings:
[palette]
theme = "phosphor_green" # options: phosphor_green | amber | red_alert
[transitions]
on_boot = "matrix_rain" # animation played at startup
on_menu_select = "none"
on_results = "none"
on_exit = "none"All settings are optional — defaults are used if the file is missing or incomplete.
# If installed via pip install -e .
trackop
# Or directly
python -m spotify_tuiOn first run, your browser will open a Spotify authorization page. Approve it and the app will continue. Subsequent runs skip this step.
Fetch your most-played tracks for Last 4 Weeks, Last 6 Months, or All Time. Choose how many (1–50). Export the list as .txt, .md, or .json.
Same as Top Tracks but for artists. Shows each artist's top genres and a popularity bar. Export supported.
Your listening history for the past week, month, or year. Paginated so it goes beyond the 50-track API limit. Export supported.
Live polling view (updates every ~1.5s). Shows:
- Track name, artists, album
- Progress bar with elapsed / total time
- Shuffle state, repeat mode, active device name, volume
Key controls while in this view:
| Key | Action |
|---|---|
p |
Play / pause |
n |
Next track |
b |
Previous track |
+ |
Volume +5% |
- |
Volume -5% |
s |
Toggle shuffle |
r |
Cycle repeat (off → context → track) |
g |
Toggle Matrix rain backdrop |
q or ESC |
Exit |
Rain mode (g): fills the terminal with Matrix rain, with the Now Playing panel centered and fixed. The rain palette automatically matches your app theme (green / amber / red). All transport controls remain active while rain is running.
Playback controls (play, pause, skip, volume, shuffle, repeat) require Spotify Premium. Reading the current track works on free accounts.
Pulls your top tracks and fetches audio feature data from the Spotify API. Displays a table with BPM (tempo), key/mode, energy, valence, and danceability — plus an averages row.
Note: Spotify restricted the audio features API for new developer apps in late 2024. If you see a 403 error on this screen, your app does not have access to this endpoint. See developer.spotify.com/blog/2024-11-27-changes-to-the-web-api.
Pick two of your playlists. Renders a three-column table: tracks only in A, tracks shared by both, tracks only in B.
- Pick any of your playlists
- See all tracks sorted by BPM (tempo), with an energy bar per track
- Optionally filter by a BPM range (e.g. 120–140)
- Preview the filtered list
- Optionally save those tracks as a brand-new private Spotify playlist — named by you
Note: Also affected by the Spotify audio features API restriction described above.
Both Playlist screens use a shared selector with:
- Sort options: Spotify order, A→Z, Z→A, Most tracks, Fewest tracks
- Type-ahead autocomplete: start typing any part of the name to filter the list instantly
Fullscreen Matrix-style animation. Live controls:
| Key | Action |
|---|---|
d / D |
Density down / up |
s / S |
Speed down / up |
t / T |
Tail length down / up |
c |
Cycle charsets (Katakana / Latin / Digits / Binary / Symbols) |
p |
Cycle color palettes |
q or ESC |
Exit |
Three built-in color themes set via config.toml:
| Theme | Color |
|---|---|
phosphor_green |
Classic green terminal (default) |
amber |
Warm orange-amber |
red_alert |
Deep red |
The Now Playing rain backdrop automatically uses the palette that matches the active app theme.
If you update the app after a scope change (e.g. adding playlist or playback permissions), Spotify's cached token won't have the new scopes. Fix it by deleting the cache and re-running:
rm .cache # macOS/Linux
del .cache # Windows CMDThe browser authorization page will open again on next launch.
Two older scripts remain in the root and are no longer the primary interface, but still work:
top_tracks.py— argparse CLI with--time-range,--limit,--outputflagsinteractive_top_tracks.py— guided prompts using inquirer + yachalk styling
These predate the TUI and don't use the new scopes or features.
track-operator/
├── spotify_tui/
│ ├── app.py # Entry point, main loop
│ ├── config.py # TOML config loader with defaults
│ ├── spotify_client.py # All Spotipy API calls
│ │
│ ├── screens/
│ │ ├── main_menu.py # Arrow-key menu
│ │ ├── top_tracks.py # Top tracks view + export
│ │ ├── top_artists.py # Top artists view + export
│ │ ├── recent_plays.py # Recent plays view + export
│ │ ├── audio_features.py # BPM / feature fingerprint table
│ │ ├── playlist_diff.py # Two-playlist diff
│ │ ├── playlist_bpm.py # Playlist BPM viewer + playlist creator
│ │ ├── now_playing.py # Live now playing + transport controls + rain mode
│ │ ├── rain.py # Matrix rain easter egg
│ │ ├── splash.py # Boot / auth screen
│ │ └── under_construction.py
│ │
│ ├── ui/
│ │ ├── theme.py # Color palettes and Style factories
│ │ ├── components.py # Reusable renderers (track_list, etc.)
│ │ ├── panels.py # Panel/border helpers
│ │ ├── export.py # Export prompts and formatters
│ │ ├── playlist_select.py # Shared playlist selector (type-ahead + sort)
│ │ └── logo.py # Figlet ASCII art logo
│ │
│ └── transitions/
│ ├── manager.py # TransitionManager (registry pattern)
│ ├── fade.py # Fade-to-black
│ ├── matrix_rain.py # Matrix rain (auto-play, no controls)
│ └── none.py # No-op
│
├── config.toml # User config (theme, transitions)
├── pyproject.toml # Package metadata + entry point
├── requirements.txt # Legacy dep list
├── FEATURES.md # Full feature roadmap with API details
├── TODO.md # Bug and feature tracking
├── .env # NOT committed — your Spotify credentials
└── .cache # NOT committed — Spotify OAuth token cache
- Playlist BPM / Audio Features use the
/audio-featuresendpoint which Spotify restricted for new developer apps in late 2024. These screens show a clear error message if your app doesn't have access. - Playlist BPM / Diff / Now Playing require the OAuth scopes added in the v2 update. If you get a 403 or scope error, delete
.cacheand re-authorize. - Playback controls silently fail on free Spotify accounts (the API returns 403). The view still works read-only.
- Local files in playlists (tracks not on Spotify's servers) are automatically skipped — they have no Spotify ID or audio features.
- The app uses
force_terminal=Truein Rich so it renders ANSI colors correctly in Git Bash / MINGW64 on Windows. If colors look wrong in another terminal, check yourTERMenvironment variable. - The OAuth token cache is pinned to the project root regardless of what directory you run
trackopfrom — no more "Couldn't write cache" warnings when running from other directories.
Created by ALLCAPS with help from Microchip