Config
Config is TOML. OAuth credentials live in private auth files under the app config directory, not in an OS keyring. On Unix, spotuify writes its config file with mode 0600, the auth directory with mode 0700, and auth files with mode 0600.
spotuify config pathSPOTUIFY_CONFIG=/tmp/spotuify.toml spotuify config pathManaged keys
Section titled “Managed keys”These keys are accepted by spotuify config get and spotuify config set.
| Key | Type | Default | Notes |
|---|---|---|---|
client_id |
string | required | Spotify Developer app client id for the default PKCE flow |
client_secret |
string | none | optional for PKCE; config get redacts it unless --reveal-secret is passed |
redirect_uri |
string | http://127.0.0.1:8888/callback |
must match the Spotify app settings |
player.backend |
enum | embedded |
only embedded (in-process librespot); Spotifyd/Connect-only backends were removed |
player.bitrate |
number | 320 |
96, 160, or 320 |
player.device_name |
string | none | preferred embedded/connect device name |
player.audio_output_device |
string | system default | local audio output the embedded player renders to; match a name from spotuify audio-outputs |
player.normalization |
bool | false |
player normalization |
player.audio_cache_mib |
number | 0 |
embedded playback cache size |
player.pulse_props |
bool | true |
Linux Pulse/PipeWire app props |
player.event_hook |
string | none | legacy alias for analytics.hook_command |
analytics.hook_command |
string | none | shell hook command for qualified listens |
analytics.hook_timeout_ms |
number | 5000 |
hard timeout for the hook command |
cache.cover_cache_mb |
number | 200 |
cover-art cache cap |
cache.cover_cache_ttl_days |
number | 30 |
cover-art TTL |
notifications.enabled |
bool | false |
master switch for desktop notifications (needs the notifications build feature) |
notifications.summary |
string | {track} |
summary template ({track} {artist} {album} tokens) |
notifications.body |
string | {artist} - {album} |
body template |
notifications.on_track_change |
bool | true |
notify when the playing track changes |
notifications.on_pause |
bool | false |
notify on pause |
notifications.on_resume |
bool | false |
notify on resume |
notifications.on_skip |
bool | false |
notify on next/previous skips |
notifications.on_error |
bool | true |
notify on auth errors (deduped) |
discord.enabled |
bool | false |
Discord Rich Presence (needs the discord-rpc build feature + Discord running) |
discord.application_id |
string | none | Discord application id for Rich Presence |
spotuify config get player.bitratespotuify config set player.bitrate 320spotuify config get client_secretspotuify config get client_secret --reveal-secretThe notifications.summary / notifications.body templates expand these
tokens from the current track: {track}, {artist}, {album},
{duration} (m:ss), and {progress} (m:ss). Defaults are
{track} / {artist} - {album}.
File-only sections
Section titled “File-only sections”Some config is loaded from TOML but not yet wired through config set.
[analytics]store_raw_queries = trueretention_progress_days = 90retention_events_days = 365retention_operations_days = 90daily_rollup_hour = 3hook_command = "/Users/me/bin/spotuify-listen-hook"hook_timeout_ms = 5000allow_file_credentials = falselastfm_api_key = "lastfm-api-key"lastfm_user = "your-lastfm-user"
[viz]enabled = truesource = "auto"target_fps = 30smoothing = 0.5noise_gate = 0.005color_scheme = "spotify-green"style = "bars"
[tui]theme = "terminal-default"The visualizer ships on by default. Set enabled = false to opt out.
It animates from the embedded librespot sink tap; when no audio is
playing the spectrum draws a flat baseline. Toggle it off if you want
the player to use that vertical space for queue items instead.
spotuify viz enable / viz disable and spotuify viz source <kind>
write enabled and source back to this file, so the choice survives a
daemon restart. The runtime effect is immediate either way.
style picks the renderer. spotuify viz styles prints the full list
with a one-line description each; spotuify viz style <name> sets it,
and next / prev cycle. In the TUI, ctrl+v opens a picker that
previews each style live, and V blows the visualizer up to fill the
terminal. Available styles:
| Style | What it draws |
|---|---|
bars |
Smooth fractional block bars, one per band (default). |
bars-dot |
Bars stippled with Braille dots. |
bars-outline |
Only the top edge of each bar, as a line graph. |
bricks |
Half-height blocks stacked with gaps. |
columns |
Thin single-column bars interpolated between bands. |
classic-peak |
Thin columns under falling peak caps. |
classic-led |
Winamp-style LED matrix with held peak caps. |
mirror |
Braille bars mirrored about a horizontal axis. |
scatter |
Twinkling Braille particle field. |
rain |
Falling streaks confined to the bar shapes. |
matrix |
Katakana digital rain, density driven by energy. |
flame |
Doom-fire heat field fed by the spectrum. |
retro |
Synthwave sun, horizon wave, and scrolling grid. |
pulse |
Pulsating Braille ellipse with shockwave rings. |
wave |
Braille oscilloscope tracing the raw waveform. |
scope |
Lissajous XY scope from a phase-delayed waveform. |
heartbeat |
ECG monitor trace over a dashed baseline. |
sakura |
Cherry blossom petals drifting down on the breeze. |
firework |
Launching bursts that explode and fall under gravity. |
bubbles |
Hollow bubbles rising and popping at the surface. |
terrain |
Side-on mountain range scrolling in from the right. |
firefly |
Fireflies blinking over a grass silhouette. |
mosaic |
Fixed heatmap tiles igniting and fading in place. |
sand |
Falling-sand automaton that bass kicks blow apart. |
geyser |
Particle fountain erupting on bass transients. |
butterfly |
Symmetric ink-blot wings mirrored about the centre. |
binary |
Columns of 0s and 1s streaming at per-band speeds. |
ascii |
Thin columns drawn with shade blocks (█ ▓ ▒ ░). |
wave, scope, and heartbeat trace the raw waveform rather than the
12-band spectrum. They need a daemon new enough to send one; against an older
daemon they draw a resting trace instead of failing.
Style names are matched case-insensitively and trimmed, and spotuify config set viz.style writes the canonical spelling.
color_scheme still applies to every style. Under NO_COLOR the styles
keep their glyphs but drop colour; bars additionally falls back to #.
source (auto, sink, loopback, none) and color_scheme
(spotify-green, rainbow, monochrome) are matched case-insensitively
and trimmed, and spotuify config set writes the canonical spelling. That
is the same rule viz.style and tui.theme follow.
tui.theme picks the terminal colour theme. terminal-default (the
default) keeps the palette spotuify ships with. spotuify theme list
prints the built-ins plus anything in <config_dir>/themes; see
Themes for the file format. Names are matched
case-insensitively and trimmed, and spotuify config set tui.theme
writes the canonical spelling. Setting a name that does not resolve to a
built-in or an existing file is rejected. Editing the key by hand takes
effect on spotuify reload, which broadcasts the new colours to running
clients.
analytics.lastfm_api_key and analytics.lastfm_user are defaults for
historical Last.fm import. CLI flags override them:
spotuify analytics import lastfm --user your-lastfm-user --from 2024-01-01Environment variables
Section titled “Environment variables”The default auth path is dev-app PKCE. Put client_id in config or set
SPOTUIFY_CLIENT_ID before login. First-party/keymaster auth is opt-in
for experiments with SPOTUIFY_USE_FIRST_PARTY=1.
SPOTUIFY_CLIENT_ID=... spotuify loginSPOTUIFY_CLIENT_SECRET=... spotuify loginSPOTUIFY_REDIRECT_URI=http://127.0.0.1:8888/callback spotuify loginSPOTUIFY_USE_FIRST_PARTY=1 spotuify loginLast.fm historical import also reads environment defaults:
SPOTUIFY_LASTFM_API_KEY=lastfm-api-key \SPOTUIFY_LASTFM_USER=your-lastfm-user \spotuify analytics import lastfm --from 2024-01-01 --format jsonFor local development and tests:
# Run the whole stack against fake Spotify data; never touches live# Spotify auth. Honored by the CLI, daemon, and TUI uniformly.SPOTUIFY_FAKE_SPOTIFY=1 spotuifyThe old proactive scope-drift credential read no longer runs at daemon startup. Scope checks now reuse the first real token read from the auth file.
Media controls (MPRIS on Linux, Now Playing on macOS, SMTC on Windows)
are on by default. Set SPOTUIFY_NO_MEDIA_CONTROLS=1 before starting the
daemon to turn them off entirely; on Windows this also skips the
hidden-window driver.
SPOTUIFY_NO_MEDIA_CONTROLS=1 spotuify daemon restartOn startup the TUI asks the terminal which image protocol it speaks, so
album art can use kitty graphics or sixel where they exist. A terminal
that never answers leaves the query’s reader thread blocked on stdin; it
then swallows the first keystroke and hands back the terminal settings
from before the TUI started, which looks like a frozen TUI. spotuify
skips the query where no answer is likely: piped stdin, a tmux window
nobody is watching, GNU screen. Set SPOTUIFY_NO_TERMINAL_QUERY=1 to
skip it everywhere; album art falls back to half-blocks.
Detection is bounded at 500 ms end to end. If it trips, spotuify says so
and exits rather than hanging; a wedged tmux server is the usual cause,
and tmux kill-server clears it.
SPOTUIFY_NO_TERMINAL_QUERY=1 spotuifyOne-shot overrides
Section titled “One-shot overrides”spotuify -o player.bitrate=160 play "ambient"spotuify -o player.normalization=true play "ambient"Overrides apply only to that command.