isi-music is a terminal audio player for Spotify streaming and local file playback, built in Rust. It replaces resource-heavy desktop clients with a native TUI that runs in any terminal emulator.
- Spotify streaming via librespot -- no official Spotify app required
- Local file playback -- MP3 and FLAC with automatic metadata extraction
- Real-time audio visualizer using braille characters (Spotify + local files)
- Full-text search across tracks, albums, artists, playlists, and podcasts
- Queue management with cross-player support (mix Spotify and local tracks)
- Shuffle and repeat modes (off / queue / track)
- Album art rendered via Kitty / Sixel / half-block (terminal auto-detected)
- Embedded cover art support for local files
- MPRIS2 D-Bus -- media keys, Waybar widget, playerctl support
- Last.fm scrobbling -- now playing + automatic scrobble at 50% or 4 minutes
- Discord Rich Presence -- shows current track in Discord activity
- Daemon mode -- keep playback after closing the terminal, control via CLI
- Playlist management -- add and remove tracks via keyboard (tiling picker)
- Command mode --
:prefix commands likeap <search>,newplaylist <name> - Seek support for all audio formats
Spotify Premium is required for streaming. Local file playback works without any Spotify account. See the Spotify Setup section below.
A Nerd Font is required for proper album art and UI rendering.
Linux:
mkdir -p ~/.local/share/fonts
unzip NerdFont.zip -d ~/.local/share/fonts
fc-cache -fvmacOS:
brew tap homebrew/cask-fonts
brew install font-fira-code-nerd-fontWindows: Download from https://www.nerdfonts.com/font-downloads, extract, right-click and install the .ttf files.
Configure your terminal to use the font (e.g. "FiraCode Nerd Font" or "JetBrains Mono Nerd Font").
Linux:
curl -L https://github.com/glrmrissi/isi_music/releases/latest/download/isi-music-linux-x86_64 -o isi-music
chmod +x isi-music
sudo mv isi-music /usr/local/bin/macOS:
curl -L https://github.com/glrmrissi/isi_music/releases/latest/download/isi-music-macos-arm64 -o isi-music
chmod +x isi-music
sudo mv isi-music /usr/local/bin/Linux audio dependencies:
| Distro | Command |
|---|---|
| Debian / Ubuntu | sudo apt install libasound2t64 libpulse0 |
| Arch Linux | sudo pacman -S alsa-lib libpulse |
| Fedora | sudo dnf install alsa-lib pulseaudio-libs |
isi-musicOn first launch, run the setup wizard to configure Spotify, Last.fm, and theme:
isi-music setupIndividual setup commands:
isi-music setup-spotify # Spotify Client ID + PKCE OAuth
isi-music setup-lastfm # Last.fm API key + sessionAll config lives under ~/.config/isi-music/ on Linux, ~/Library/Application Support/isi-music/ on macOS, and %APPDATA%\isi-music\ on Windows.
[spotify]
client_id = "your_client_id_here"
[local]
music_dir = "~/Music"
[lastfm]
api_key = "your_lastfm_api_key"
api_secret = "your_lastfm_api_secret"
session_key = "obtained_via_setup-lastfm"
[discord]
enabled = true
[musixmatch]
musixmatch_api_key = "your_musixmatch_api_key"See Spotify Setup for obtaining a Client ID.
Create ~/.config/isi-music/theme.toml to customize colors and layout.
border_active = "#00d4ff"
border_inactive = "#ffffff"
highlight_bg = "#004b7a"
text_primary = "#ffffff"
accent_color = "#ffeb3b"
background = "#141414"
text_secondary = "#888888"
status_bar = "#1e1e1e"
show_ascii_art = false
highlight_symbol = "> "
options_panel_symbol = "▶ "Color reference:
| Variable | Purpose |
|---|---|
border_active |
Focused panel borders, active indicators |
border_inactive |
Unfocused borders, secondary text |
highlight_bg |
Selected list items background |
text_primary |
Titles, artists, primary text |
accent_color |
Progress bars, icons, seek bar |
background |
Root background fill |
text_secondary |
Subtle text, timestamps, metadata |
status_bar |
Bottom status bar background |
highlight_symbol |
List selection indicator (default: "> ") |
options_panel_symbol |
Options panel selection indicator (default: "▶ ") |
Colors can be specified as hex (#rrggbb), named (white, red, green, etc.), or RGB function (rgb(r,g,b)).
The theme file supports custom layout trees, widget styles, and ASCII art:
border_active = "#00d4ff"
border_inactive = "#ffffff"
highlight_bg = "#004b7a"
text_primary = "#ffffff"
accent_color = "#ffeb3b"
background = "#141414"
text_secondary = "#888888"
status_bar = "#1e1e1e"
ascii_art_inline = [
" .---. ",
' /|66_\ ',
' \| ^ /---. ',
" |'-'| UI | ",
" | |____| ",
" | | ",
" '---' ",
" _________ ",
" / /| ",
" /________/ | ",
" | | | ",
" | ISI | / ",
" |________|/ ",
]
show_ascii_art = false
[widget_styles]
[layout_tree]
direction = "vertical"
[[layout_tree.constraints]]
length = 3
[[layout_tree.constraints]]
fill = 1
[[layout_tree.constraints]]
length = 1
[[layout_tree.constraints]]
length = 1
[[layout_tree.children]]
direction = "horizontal"
[[layout_tree.children.constraints]]
fill = 1
[[layout_tree.children.constraints]]
length = 40
[[layout_tree.children.children]]
widget = "header"
[[layout_tree.children.children]]
widget = "visualizer"
[[layout_tree.children]]
direction = "horizontal"
[[layout_tree.children.constraints]]
percentage = 20
[[layout_tree.children.constraints]]
fill = 1
[[layout_tree.children.children]]
direction = "vertical"
[[layout_tree.children.children.constraints]]
length = 7
[[layout_tree.children.children.constraints]]
length = 15
[[layout_tree.children.children.constraints]]
fill = 1
[[layout_tree.children.children.children]]
widget = "library"
[[layout_tree.children.children.children]]
widget = "playlists"
[[layout_tree.children.children.children]]
widget = "ascii_art"
[[layout_tree.children.children]]
direction = "vertical"
[[layout_tree.children.children.constraints]]
fill = 1
[[layout_tree.children.children.constraints]]
length = 8
[[layout_tree.children.children.children]]
widget = "main_content"
[[layout_tree.children.children.children]]
widget = "queue"
[[layout_tree.children]]
direction = "horizontal"
[[layout_tree.children.constraints]]
percentage = 30
[[layout_tree.children.constraints]]
fill = 1
[[layout_tree.children.children]]
widget = "marquee"
[[layout_tree.children.children]]
widget = "progress"
[[layout_tree.children]]
widget = "help"
[compact_layout]
direction = "vertical"
[[compact_layout.constraints]]
length = 1
[[compact_layout.constraints]]
fill = 1
[[compact_layout.constraints]]
length = 1
[[compact_layout.children]]
widget = "header"
[[compact_layout.children]]
direction = "horizontal"
[[compact_layout.children.constraints]]
percentage = 35
[[compact_layout.children.constraints]]
fill = 1
[[compact_layout.children.children]]
widget = "ascii_art"
[[compact_layout.children.children]]
widget = "main_content"
[[compact_layout.children]]
direction = "horizontal"
[[compact_layout.children.constraints]]
percentage = 30
[[compact_layout.children.constraints]]
fill = 1
[[compact_layout.children.children]]
widget = "marquee"
[[compact_layout.children.children]]
widget = "progress"
[fullscreen_layout]
direction = "vertical"
[[fullscreen_layout.constraints]]
length = 18
[[fullscreen_layout.constraints]]
length = 8
[[fullscreen_layout.constraints]]
min = 0
[[fullscreen_layout.children]]
widget = "now_playing"
[[fullscreen_layout.children]]
widget = "fullscreen_lyrics"
[[fullscreen_layout.children]]
widget = "visualizer"Run the wizard (isi-music setup) to choose from 8 color presets that preserve your existing layout settings.
Create ~/.config/isi-music/keybinds.toml to override default keybindings. See the full action list in the Keybindings section.
The February 2026 Spotify Web API changes require a Client ID for all API requests.
- Go to the Spotify Developer Dashboard and click "Create app"
- Set the Redirect URI to
http://127.0.0.1:8888/callback - Copy the Client ID
- Run
isi-music setup-spotifyor add it toconfig.tomlmanually:
[spotify]
client_id = "your_client_id_here"The setup wizard uses PKCE OAuth -- no client_secret is required. Your browser will open for authorization.
| Key | Action |
|---|---|
Tab / Shift+Tab |
Next / previous panel |
↑ / ↓ or k / j |
Navigate within a panel |
Ctrl+↑ / Ctrl+↓ |
First / last item |
Enter |
Play selected / open album or artist |
Space |
Play / pause |
n / p |
Next / previous track |
s |
Toggle shuffle |
r |
Cycle repeat (off -> queue -> track) |
+ / - |
Volume up / down |
← / → |
Seek +/- 5s (hold for +/- 10s) |
/ |
Search |
Esc |
Back / close search / exit fullscreen |
q or Ctrl+C |
Quit |
Playlist & Library:
| Key | Action |
|---|---|
l |
Like current track |
A |
Add selected track to playlist (tiling picker) |
D |
Remove selected track from playlist / unlike |
: |
Command prompt (ap <search>, newplaylist <name>) |
a |
Add track to queue |
Delete |
Remove selected item from queue |
o |
Sort tracks (default / title / artist / album / duration) |
R |
Toggle radio mode (auto-recommendations) |
Alt+r |
Get similar tracks for selection |
Ctrl+y |
Copy track link to clipboard |
Display:
| Key | Action |
|---|---|
z |
Toggle fullscreen player |
m |
Toggle compact mode |
v |
Toggle visualizer |
y |
Toggle lyrics |
d |
Toggle debug overlay |
Shift+b |
Toggle breadcrumb |
t |
Open options panel |
? |
Open help panel |
Ctrl+f |
Quick search (filter current track list) |
PgUp / PgDown |
Scroll lyrics |
All keybindings are customizable in keybinds.toml. |
Keep playback running in the background, controlled from the command line.
# Start the daemon
isi-music --daemon
# Load and play
isi-music --play spotify:playlist:37i9dQZF1DXcBWIGoYBM5M
isi-music --liked
# List loaded tracks
isi-music --ls
# Play by index
isi-music --play-id 2
# Playback controls
isi-music --toggle # play / pause
isi-music --next # next track
isi-music --prev # previous track
isi-music --vol+ # volume +5%
isi-music --vol- # volume -5%
# Query status
isi-music --status # shows current track and progress
# Stop the daemon
isi-music --quit-daemonLogs are written to ~/.cache/isi-music/isi-music.log (Linux) or the equivalent cache path on other platforms. Clear them with isi-music --clear-logs.
Daemon mode currently supports Spotify playback only. Local file playback works in TUI mode.
isi-music can play local audio files without a Spotify account. Point it at your music directory in config.toml:
[local]
music_dir = "~/Music"Supported formats: MP3, FLAC.
Navigate to Local Files in the library panel and press Enter to scan. The first scan extracts metadata and embedded cover art, cached in SQLite for instant subsequent loads.
You can mix local and Spotify tracks in the same queue -- isi-music routes each track to the appropriate player automatically.
isi-music registers on D-Bus as org.mpris.MediaPlayer2.isi_music, enabling media keys, Waybar widgets, and playerctl.
Waybar config:
"mpris": {
"format": "{player_icon} {title} -- {artist}",
"player-icons": { "isi_music": "" },
"status-icons": { "playing": ">", "paused": "||" }
}Hyprland media key bindings:
bind = , XF86AudioPlay, exec, playerctl play-pause
bind = , XF86AudioNext, exec, playerctl next
bind = , XF86AudioPrev, exec, playerctl previous
MPRIS works in both TUI and daemon modes.
Run isi-music setup-lastfm to configure. The wizard will prompt for your API credentials and open your browser for authorization.
Scrobbling behavior:
- Track starts:
track.updateNowPlaying - Track reaches 50% or 4 minutes (whichever comes first):
track.scrobble
To disable, remove the [lastfm] section from your config.
Enable in config.toml:
[discord]
enabled = trueOptional: use a custom app ID (default: isi-music official app):
[discord]
enabled = true
app_id = "your_custom_app_id"Your status will show "Listening to [Track] by [Artist]".
Requires Rust 1.85+ (edition 2024).
git clone https://github.com/glrmrissi/isi_music.git
cd isi_music
# Linux build dependencies
sudo apt install libasound2-dev libpulse-dev libdbus-1-dev pkg-config
cargo build --release
# Run with debug logging
RUST_LOG=isi_music=debug cargo run
# Run tests (154 tests)
cargo testPre-built binaries come in two variants:
| Variant | Size | Features | Use Case |
|---|---|---|---|
isi-music-<platform> |
~10 MB | All features (album art, visualizer, wizard, lyrics, MPRIS, Discord) | Full experience (MPRIS included via -F mpris in CI) |
isi-music-<platform>-minimal |
~9 MB | Spotify streaming, Discord, Last.fm, setup (no album art, visualizer, lyrics, MPRIS) | Headless daemon or minimal TUI |
Build with specific features using the -F flag:
# Minimal build (streaming + Discord only)
cargo build --release --no-default-features -F spotify,discord
# Add MPRIS back if needed
cargo build --release --no-default-features -F spotify,discord,mpris
# Exclude album art (smaller binary, fewer deps)
cargo build --release --no-default-features -F spotify,discord,mpris,lastfm,wizard,visualizer,lyricsAvailable features:
| Feature | Default | Description |
|---|---|---|
spotify |
yes | Spotify streaming via librespot |
discord |
yes | Discord Rich Presence |
mpris |
no | MPRIS2 D-Bus media controls |
lastfm |
yes | Last.fm scrobbling |
wizard |
yes | Interactive setup wizard |
visualizer |
yes | Real-time audio FFT visualizer |
lyrics |
yes | Synced and unsynced lyrics fetching |
album-art |
yes | Album art rendering (Kitty/Sixel/half-block) |
isi-music uses multiple audio backends depending on the source:
- librespot -- Spotify authentication and audio streaming via the Spotify Connect protocol
- rodio + symphonia -- Local audio decoding (MP3 and FLAC)
- Custom HTTP client -- Spotify Web API for search, metadata, playlists, and album art
The TUI is built with ratatui. The event loop polls player state, processes keyboard input, and renders at ~60 fps.
Tests live in tests/ mirroring the src/ structure, referenced via #[path] attributes.
This project follows Semantic Versioning derived from conventional commits:
| Commit type | Version bump |
|---|---|
fix: |
Patch (1.0.x) |
feat: |
Minor (1.x.0) |
BREAKING CHANGE footer |
Major (x.0.0) |
Delete the SQLite cache and covers, then restart:
rm ~/.local/share/isi-music/library.db
rm -rf ~/.cache/isi-music/covers/- Ensure your terminal supports true color:
echo $COLORTERM - Verify a Nerd Font is installed and configured
- Check that embedded artwork exists in your audio files
- Ensure D-Bus is running:
systemctl --user status dbus - Check that
DBUS_SESSION_BUS_ADDRESSis set:echo $DBUS_SESSION_BUS_ADDRESS
MIT