spotuify is a Spotify player you drive from your terminal: a keyboard-native TUI and a fully scriptable CLI for the same thing. Search, play, queue, switch devices, build playlists, read synced lyrics, see album art, all without leaving the shell. It also ships an MCP server so a coding agent can run your music the way you do, but that's a bonus. The TUI and CLI are the point.
Run spotuify and you're in. If you're not configured yet, it creates a config file and tells you what to add. The default path uses your own Spotify Developer app with the PKCE browser flow; the experimental first-party/keymaster path is still opt-in.
GA scope: spotuify is BYO Spotify app GA for terminal users who are comfortable creating their own Spotify Developer app. It is not broad consumer no-developer setup yet; that would require a reviewed/shared Spotify app or a product decision to make first-party/keymaster auth the default. If writes return 403, your app is probably still in Spotify Development Mode; apply for Extended Quota Mode in the Spotify dashboard.
Fair question. spotify-player, ncspot, and the original spotify-tui already proved a terminal Spotify client is worth living in, and spotuify builds on what they shipped: embedded librespot for playback, a keyboard TUI, local library search. It pushes hard in one direction the others treat as a side feature. The CLI is the product, not a wrapper around the TUI.
- Pipeable everywhere, not just on one command. Every read, list, status, and search surface speaks
--format json,jsonl,csv, orids.spotuify search "lo-fi beats" --type playlist --format idsreturns bare URIs you can pipe into anything, in any language. Other terminal clients give you JSON on a command or two, or an interactive UI you can't script at all. - Your agents can run it. Because the CLI is the contract, an LLM controls Spotify through ordinary commands (or the built-in MCP server). Writes are preview-first:
--dry-runshows what would change,--yescommits, andspotuify ops undoreverses the last one. A back button for your library, which you want the moment an agent is the one clicking. - The music keeps playing after you close the window. A background daemon owns playback, queue, and devices; the TUI, CLI, and agents are all just views of it. Quit the TUI and the song keeps going. Run a command from another shell and it shows up instantly.
- Search runs off a local cache. A SQLite store plus a rebuildable index answer library and search queries from disk, so navigation is instant and an agent gets the same results twice.
Want the most polished desktop experience? Use the official app. Want Spotify as something you can type at, pipe, script, and hand to an agent? That's this.
- Browser login through Spotify OAuth PKCE.
- OAuth tokens stored in private auth files under the app config directory so detached daemons never wait on OS credential prompts.
- Owned config file at
~/.config/spotuify/spotuify.toml. - Config commands for
path,init,get, andset. - Embedded librespot registers spotuify as a Spotify Connect device at daemon start.
- Playback controls: play, pause, next, previous, seek, volume, shuffle, repeat.
- Playback actions take the daemon hot path first; embedded play/pause/next/seek/volume try local transport before waiting on Spotify Web API reconciliation.
- Search across tracks, episodes, shows/podcasts, albums, artists, and playlists.
- Queue viewing and add-to-queue support, with background warming for queued track metadata, cover art, lyrics, and next-track audio.
- Playlist browsing and quick add-current-to-playlist flow.
- Artist discography browser: list followed artists and browse an artist's full catalog grouped into albums, singles & EPs, compilations, and appears-on, with an in-library-only filter (
spotuify artist albums <uri> --library-only,spotuify artist followed, or the TUILtoggle). - Device list and Spotify Connect transfer.
- Cover art rendering through Kitty, iTerm2, Sixel, or half-block fallback.
- Manual current-track media refresh via
spotuify refresh-mediaorUin the TUI. - Synced lyrics in the TUI and terminal:
spotuify lyrics show,spotuify lyrics follow, LRC export, and per-track offset tuning. - Fully keyboard navigable with vim-style movement, pane switching, help overlay, paging, and back navigation.
- Local analytics:
listen_factsplusspotuify analytics top/habits/rediscoveryfor Wrapped-style insights, with shell-hook recipes for ListenBrainz, Last.fm, and Discord. - Operation log + undo: mutating commands are recorded;
spotuify ops undo --dry-runpreviews andspotuify ops undo --yesapplies reversible undo. MCP exposesundo_lastas a safety net for agent runs. - MCP server over stdio or loopback HTTP for agents.
- Audio visualization through embedded sink taps or loopback capture.
- A Spotify Premium account (required for librespot streaming).
- A Spotify Developer app for the default BYO Spotify app GA auth path. Use redirect URI
http://127.0.0.1:8888/callback; apply for Extended Quota Mode if you want playlist/library writes beyond Spotify's Development Mode limits. - A terminal with good image support for best visuals. Kitty works well; other terminals fall back through
ratatui-imagesupport.
Prebuilt binaries ship for macOS (Apple Silicon and Intel), Linux x86_64, and Windows x64 on each GitHub Release; the Homebrew tap is the quickest macOS path. Other targets (Linux musl or arm, other distros) build from source with cargo install or Nix. Pick your platform:
brew tap planetaryescape/spotuify
brew trust --formula planetaryescape/spotuify/spotuify
brew install planetaryescape/spotuify/spotuify
spotuify daemon install-service # registers a launchd LaunchAgent
spotuify # first run kicks off onboardingTo update an existing Homebrew install:
brew update
brew upgrade planetaryescape/spotuify/spotuifyRelease archives include SHA256 checksums and GitHub artifact provenance attestations. macOS binaries are not notarized today, so Gatekeeper may still block the first launch:
xattr -d com.apple.quarantine /opt/homebrew/bin/spotuifyPrefer a native window? The latest GitHub Release can also include Spotuify.dmg, a SwiftUI app that bundles the spotuify daemon+CLI binary and installs it to ~/.local/bin/spotuify on first launch:
curl -fsSLO https://github.com/planetaryescape/spotuify/releases/latest/download/Spotuify.dmg
curl -fsSLO https://github.com/planetaryescape/spotuify/releases/latest/download/Spotuify.dmg.sha256
shasum -a 256 -c Spotuify.dmg.sha256The DMG is built locally with clients/macos/scripts/build-dmg.sh and attached to the release manually because the app currently needs the macOS 26 SDK. The build script signs and notarizes only when the release machine has a Developer ID identity and SPOTUIFY_NOTARY_PROFILE configured; otherwise macOS may ask for the usual first-launch approval.
Install the latest prebuilt archive with checksum verification:
curl -fsSLO https://raw.githubusercontent.com/planetaryescape/spotuify/main/install.sh
bash install.sh
spotuifyGrab the linux-x86_64 tarball from Releases and put spotuify on your PATH:
tar xzf spotuify-v*-linux-x86_64.tar.gz
install -Dm755 spotuify ~/.local/bin/spotuify
spotuify daemon install-service # registers a systemd --user unit
spotuifySpotify auth files live under the private app config directory on every platform. On Unix, spotuify writes the auth directory with mode 0700 and auth files with mode 0600.
cargo install --git https://github.com/planetaryescape/spotuify --locked spotuify
spotuify daemon install-serviceDownload spotuify-v*-windows-x86_64.zip from Releases, unzip it, and put spotuify.exe on your PATH:
spotuify.exe --help
spotuify daemon install-service # registers a Task Scheduler logon triggerWindows x64 binaries are beta until login, daemon startup, playback, and Task Scheduler install are verified on a real Windows machine. Source installs still work:
cargo install --git https://github.com/planetaryescape/spotuify --locked spotuifyDaemon-mode media-key handling on Windows is currently limited: SMTC requires a foreground window handle, so background-only operation cannot register media keys. Workaround: keep the TUI process alive.
nix run github:planetaryescape/spotuify
# or in a flake:
inputs.spotuify.url = "github:planetaryescape/spotuify";Plain first-time brew install spotuify only works after brew tap planetaryescape/spotuify. Without that tap, Homebrew searches homebrew/core, where spotuify is not published. Homebrew's tap-trust checks can also ignore third-party taps unless the formula is trusted; use brew trust --formula planetaryescape/spotuify/spotuify after tapping.
From this repository:
cargo build
./target/debug/spotuify --helpInstall into your Cargo bin path:
cargo install --path .
spotuify --helpIf ~/.cargo/bin is not on your PATH, either add it or run ./target/release/spotuify directly from the repo.
For platform-specific embedded librespot builds, pick the right audio backend feature flag:
# Linux (alsa, routes through pipewire-alsa shim on modern distros):
cargo install --git https://github.com/planetaryescape/spotuify --locked \
--features 'embedded-playback,system-integrations,loopback-cpal,alsa-backend'
# Linux musl (pure-Rust rodio backend):
cargo install --git https://github.com/planetaryescape/spotuify --locked \
--no-default-features \
--features 'embedded-playback,system-integrations,loopback-cpal,rodio-backend'
# macOS (PortAudio bridge to CoreAudio):
cargo install --git https://github.com/planetaryescape/spotuify --locked \
--features 'embedded-playback,system-integrations,loopback-cpal,portaudio-backend'
# Windows (rodio writes to WASAPI):
cargo install --git https://github.com/planetaryescape/spotuify --locked \
--features 'embedded-playback,system-integrations,loopback-cpal,rodio-backend'Releases are managed by Release Please and GitHub Actions.
The flow:
- Merge normal feature/fix PRs into
mainusing conventional commit messages such asfix: improve diagnosticsorfeat: add playlist view. - Release Please opens or updates a release PR with the next version,
CHANGELOG.md,.release-please-manifest.json, andCargo.toml. - The release-lockfile workflow runs on that release PR and commits
Cargo.lockifcargo update --workspacechanges the workspace package versions. - Merge the Release Please PR once CI is green.
- Release Please creates the GitHub release and tag.
- The tag-driven release workflow builds Linux x86_64, macOS arm64, macOS Intel, and Windows x64 binaries, uploads them to the GitHub release, generates a Homebrew formula, and updates the tap.
- The macOS app DMG is a separate local artifact: build it with
clients/macos/scripts/build-dmg.sh, then attachSpotuify.dmg,Spotuify.dmg.sha256,Spotuify-<version>.dmg, andSpotuify-<version>.dmg.sha256to the same release.
Required GitHub setup:
Secret: RELEASE_PLEASE_TOKEN
Secret: HOMEBREW_TAP_TOKEN
RELEASE_PLEASE_TOKEN should be a PAT with contents:write and workflow permission so Release Please tags trigger the downstream release workflow. The workflows fall back to GITHUB_TOKEN, but tags created with the default token do not trigger other workflows.
HOMEBREW_TAP_TOKEN must be a token with write access to the Homebrew tap repository:
planetaryescape/homebrew-spotuify
The tap repo should contain a Formula/ directory. The release workflow will create or update Formula/spotuify.rb.
Manual release rebuild:
GitHub Actions -> Release -> Run workflow -> select an existing v* tag
Run the app:
spotuifyThat is the low-friction path. If config or OAuth are missing, spotuify starts setup automatically, syncs Spotify data, then opens the TUI.
You can also run setup intentionally:
spotuify onboardThe onboarding flow does this in order:
- Creates
~/.config/spotuify/spotuify.tomlif it does not exist. - Asks you to add a Spotify
client_idfrom your Spotify Developer app. - Opens your browser to log in to Spotify.
- Stores the resulting OAuth token under
<config_dir>/auth/token.json. - The daemon refreshes the access token as needed and starts syncing.
Use redirect URI http://127.0.0.1:8888/callback in the Spotify dashboard. A client secret is optional for PKCE. Premium is required for playback.
This is the BYO Spotify app GA path, not broad consumer no-developer setup. Apply for Extended Quota Mode in the Spotify dashboard if playlist/library writes return 403.
After setup succeeds, plain spotuify opens the TUI directly on later runs:
spotuifyAfter config is present, plain spotuify and spotuify onboard open the browser for you. To rerun the login by hand:
spotuify loginThat opens Spotify in your browser, and once you approve, spotuify stores the OAuth token under <config_dir>/auth/token.json.
The default auth path uses your own Spotify Developer app. Create one at https://developer.spotify.com/dashboard with redirect URI http://127.0.0.1:8888/callback, then set client_id in config or export SPOTUIFY_CLIENT_ID:
export SPOTUIFY_CLIENT_ID=your-app-client-id
spotuify loginApps in Spotify's Development Mode can be limited by Spotify policy. Apply for Extended Quota Mode if writes such as playlist creation or library saves return 403.
Default config path:
~/.config/spotuify/spotuify.toml
Default config template:
# spotuify config
# Copy your Spotify app credentials from https://developer.spotify.com/dashboard.
# Apply for Extended Quota Mode on the dashboard to lift the dev-app
# 25-user cap and unlock playlist/library writes.
client_id = ""
# Optional, only for your own Spotify app. Prefer SPOTUIFY_CLIENT_SECRET
# when you do not want a client secret written to disk.
# client_secret = ""
redirect_uri = "http://127.0.0.1:8888/callback"
[player]
backend = "embedded"
bitrate = 320
[notifications]
enabled = false
summary = "{track}"
body = "{artist} - {album}"
on_track_change = true
on_pause = false
on_resume = false
on_skip = false
on_error = trueSupported environment overrides:
SPOTUIFY_CONFIG=/path/to/spotuify.toml
SPOTUIFY_CLIENT_ID=...
SPOTUIFY_CLIENT_SECRET=...
SPOTUIFY_REDIRECT_URI=http://127.0.0.1:8888/callbackConfig commands:
spotuify config path
spotuify config init
spotuify config get client_id
spotuify config get redirect_uri
spotuify config get player.backend
# Prints <redacted> unless you pass --reveal-secret.
spotuify config get client_secret
spotuify config set client_id "..."
spotuify config set client_secret "..."
spotuify config set redirect_uri "http://127.0.0.1:8888/callback"
spotuify config set player.device_name "spotuify"
spotuify config set notifications.enabled trueValid config keys:
client_id
client_secret
redirect_uri
player.backend
player.bitrate
player.device_name
player.normalization
player.audio_cache_mib
player.pulse_props
player.event_hook # legacy alias for analytics.hook_command
analytics.hook_command
analytics.hook_timeout_ms
cache.cover_cache_mb
cache.cover_cache_ttl_days
notifications.enabled
notifications.summary
notifications.body
notifications.on_track_change
notifications.on_pause
notifications.on_resume
notifications.on_skip
notifications.on_error
analytics.hook_command is executed by the shell exactly as configured. Track data is passed through SPOTUIFY_* environment variables, not interpolated into the command string.
spotuify
spotuify onboard
spotuify login
spotuify logout
spotuify doctor
spotuify logs path
spotuify logs tail
spotuify config path
spotuify config init
spotuify config get <key>
spotuify config set <key> <value>
spotuify status --format json
spotuify devices
spotuify search "query" --type track --source local
spotuify play "query"
spotuify play-uri spotify:track:...
spotuify pause
spotuify resume
spotuify next
spotuify previous
spotuify seek +30s
spotuify volume 50
spotuify shuffle on
spotuify repeat track
spotuify queue
spotuify queue add spotify:track:...
spotuify playlists
spotuify playlist plan "brief" --format json
spotuify playlist create "Name" --from candidates.jsonl --dry-run
spotuify library tracks
spotuify analytics top --kind tracks --format json
spotuify lyrics show --track spotify:track:...
spotuify lyrics follow --lines 3
spotuify refresh-media
spotuify viz enable
spotuify hooks test
spotuify mpris status
spotuify sync search-cache --prune --older-than 7d
spotuify cache status
spotuify cache reset --confirm
spotuify ops log
spotuify ops undo --dry-run
spotuify reload
spotuify reconnect
spotuify bug-report --include-logs 200
spotuify generate completions zsh
spotuify mcpCommand behavior:
spotuifyopens the TUI. If config or OAuth are missing, it starts setup first, syncs, then opens the TUI.spotuify onboardruns the same setup flow intentionally: a browser login, then the first sync.spotuify loginopens the browser to log in and stores the OAuth token under<config_dir>/auth/token.json.spotuify logoutremoves the stored auth files.spotuify doctorchecks config, token status, API access timings, visible devices, recent playback, queue, playlists, logs, cache version, lyrics, MCP, and player backend state.spotuify logs pathprints the log file path.spotuify logs tail --follow --format jsonstreams structured log lines.spotuify config ...reads or writesspotuify.tomlwithout requiring valid Spotify credentials.- One-shot commands talk to the daemon over local IPC. Use
--no-daemon-startwhen scripts should fail instead of starting the daemon.
Global keys:
? open or close help
q quit
1 home
2 search
3 library
4 playlists
5 queue
6 devices
7 diagnostics
8 lyrics
Tab next pane
Shift-Tab previous pane
j / Down move down
k / Up move up
gg jump to top
G jump to bottom
Ctrl-d / PageDown page down
Ctrl-u / PageUp page up
Enter activate selected item
Esc / b back from playlist tracks
u refresh Spotify data
U refresh current cover art and lyrics
Playback keys:
Space play or pause
Space when idle/ended, play the selected Home, Search, Library, or Playlist item
n next
p previous
Left seek backward 15 seconds
Right seek forward 15 seconds
+ / = volume up
- volume down
s toggle shuffle
r cycle repeat mode
l save current track or episode
Search and library keys:
/ focus search input
Enter search when input is focused
Enter play selected home, search, library, or playlist item
e add selected item to queue
A or a add current track or episode to selected playlist
Playlist keys:
Enter open selected playlist
Enter play selected playlist or selected playlist track
a add current item to selected playlist
Esc / b return from playlist tracks to playlist list
Device keys:
Enter transfer playback to selected device
x transfer playback to selected device
spotuify registers itself as a Spotify Connect device through an in-process librespot session at daemon start — no separate subprocess. Premium account required (librespot streaming constraint).
Prefer a specific device name visible to other Spotify clients:
spotuify config set player.device_name "spotuify"Audio cache (disk), bitrate, normalization, and PulseAudio property hints are all on the [player] config section.
After you log in, the daemon mints a Web API token from your session and pulls your Spotify state into the local cache:
- Current playback state.
- Visible Spotify Connect devices.
- Queue state.
- User playlists.
Run spotuify doctor any time to confirm auth, daemon, device visibility, and Spotify API access are working.
spotuify exposes its daemon as a Model Context Protocol server so LLM clients (Claude Code, Cursor, Continue, agent harnesses) can use it as a tool.
# Claude Code
claude mcp add spotuify --command spotuify --args mcp
# Cursor: add to .cursor/mcp.json
{
"mcpServers": {
"spotuify": { "command": "spotuify", "args": ["mcp"] }
}
}
# Continue: add to ~/.continue/config.json under mcpServers
"spotuify": { "command": "spotuify", "args": ["mcp"] }Tools exposed:
- Read:
search,now_playing,devices_list,queue_show,playlists_list,playlist_tracks,library_list - Transport:
play,play_uri,pause,resume,next,previous,seek,volume,shuffle,repeat - Destructive (require
confirm: true):queue_add,transfer_device,playlist_create,playlist_add,playlist_remove,library_save,library_unsave - Lyrics:
lyrics - Analytics:
analytics_top,analytics_habits,analytics_search,analytics_rediscovery - Ops:
ops_log,undo_last(undo_lastis the safety net)
Resources:
spotuify://playback— current playback statespotuify://devices— visible Spotify Connect devicesspotuify://playlists— user playlistsspotuify://doctor— latest health-check report
Destructive tools called without confirm: true return a preview the LLM can show to the user. The LLM is expected to relay it and ask before retrying with confirm: true. Patterns adopted from spotify-player commit #966.
| If you want... | spotuify chooses... |
|---|---|
| A terminal-first controller for scripts and agents | CLI and MCP surfaces first; the TUI is another client |
| Playback that keeps running after the UI exits | Daemon-backed control through local IPC |
| A local library/search runtime | SQLite cache plus rebuildable search index |
| Maximum desktop integration polish today | Use an official Spotify client or a desktop-first app instead |
| The smallest possible binary with no daemon | spotuify is not optimizing for that trade-off |
Spotify's public Web API exposes queue viewing and add-to-queue, but not queue remove or queue reorder. spotuify shows the queue honestly and does not pretend those unsupported actions exist.
Playback control requires Spotify Premium. Without Premium, auth can succeed but playback API calls may fail.
Run the doctor first:
spotuify doctorThen check the log file:
spotuify logs path
spotuify logs tailOn macOS, logs are written to:
~/Library/Logs/spotuify/spotuify.log
If the TUI looks frozen, press Ctrl+C. The event loop listens for OS Ctrl+C directly. Regular Spotify refresh runs in a background task, so slow Spotify endpoints should not block keyboard input.
Generic Spotify request failure:
error: Spotify request failed
Run:
spotuify doctor
spotuify logs taildoctor prints each API section with timing and the full endpoint/status error, for example playback, devices, queue, playlists, and recently played.
Spotify API timeouts:
operation timed out
Check whether the default route to Spotify is broken:
curl -I --max-time 8 https://api.spotify.com/v1
curl -4 -I --max-time 8 https://api.spotify.com/v1If the first command times out and the -4 command returns 401, IPv6 routing to Spotify is broken on your network. spotuify forces Spotify API calls over IPv4 to avoid that path.
Redirect URI mismatch:
redirect_uri: Not matching configuration
Fix:
spotuify config get redirect_uriMake sure the exact value is listed in Spotify Dashboard -> App -> Settings -> Redirect URIs. Use 127.0.0.1, not localhost, and avoid trailing slashes.
Not logged in:
not logged in; run `spotuify login`
Fix:
spotuify loginThat opens the browser login. Plain spotuify does the same automatically after config is present. If playlist writes return 403, the Spotify app is probably still in Development Mode; apply for Extended Quota Mode in the Spotify dashboard.
No token or expired token:
spotuifyThat opens the browser login automatically when no token is stored. Use spotuify login if you only want to re-run the login without the full setup flow.
No devices visible:
spotuify doctor
spotuify devices --format jsonThen check that the embedded librespot device is visible. It registers at daemon start under player.device_name (spotuify-hume on this machine). Premium account required.
Art does not render:
Use Kitty or another terminal supported by ratatui-image. The app falls back to block rendering when terminal image protocols are unavailable.
Visualizer shows no bars on macOS loopback:
macOS does not expose system-output loopback devices by default. Use the embedded playback backend for sink-tap visualization, or install BlackHole/Loopback Audio and select it as the loopback input source. Without a virtual loopback device, spotuify may fall back to the microphone input so the visualizer stays quiet while music plays.
Windows daemon media keys:
Windows media-key integration needs a UI/window handle. The headless daemon still handles CLI, TUI, and MCP playback commands, but global Windows media keys may require a foreground TUI session until the hidden-window driver is complete.
Embedded librespot fails to register:
spotuify doctor
spotuify daemon statusIf librespot cannot bind an audio backend, the daemon reports a player failure/degraded state through doctor/status and logs. Rebuild with one of --features alsa-backend / pipewire-backend / rodio-backend / portaudio-backend for your platform.
- The default dev-app OAuth token is stored under
<config_dir>/auth/token.jsonwith mode0600on Unix. It is guarded by<config_dir>/auth/token.lockso daemon and CLI refreshes do not race. - First-party/keymaster auth is experimental and opt-in via
SPOTUIFY_USE_FIRST_PARTY=1; that path stores only refresh token + scopes under<config_dir>/auth/first-party.json. spotuify logoutremoves the stored auth files.- To re-authenticate, run
spotuify loginagain. spotuify auth bearerandspotuify config get client_secretrequire--reveal-secretbefore printing secrets.- Config files are written with mode
0600on Unix. PreferSPOTUIFY_CLIENT_SECRETwhen you do not want a client secret written to disk.
Common checks:
cargo fmt --check
scripts/cargo-test -p spotuify-spotify --tests
cargo clippy -p spotuify-spotify --all-targets -- -D warningsUse package-scoped checks while iterating. Full workspace clippy/tests and release builds are release gates, not the default edit/verify loop. To smoke an already-built binary with the fake provider:
scripts/smoke.shTo have smoke build the release binary first:
SPOTUIFY_SMOKE_BUILD=1 scripts/smoke.shBefore calling a release GA-ready, run the live opt-in gate against the exact binary you plan to ship:
SPOTUIFY_BIN=spotuify scripts/ga-live-smoke.sh
SPOTUIFY_ALLOW_PROD_INSTANCE_FROM_TARGET=1 SPOTUIFY_INSTANCE=spotuify SPOTUIFY_BIN=./target/release/spotuify scripts/ga-live-smoke.sh
SPOTUIFY_GA_LIVE_PLAYBACK=1 SPOTUIFY_BIN=spotuify scripts/ga-live-smoke.sh
SPOTUIFY_GA_LIVE_PLAYLIST=1 SPOTUIFY_BIN=spotuify scripts/ga-live-smoke.shRun locally:
cargo run
cargo run -- onboard
cargo run -- doctorsrc/main.rs unified binary entrypoint and legacy adapters
crates/spotuify-protocol daemon IPC protocol and shared wire types
crates/spotuify-daemon daemon state, handlers, diagnostics, sync coordination
crates/spotuify-cli reusable CLI argument/output helpers
crates/spotuify-tui Ratatui app state, actions, and rendering
crates/spotuify-spotify Spotify Web API, OAuth, config, rate limits
crates/spotuify-player embedded librespot backend (sole supported runtime)
crates/spotuify-store SQLite cache, migrations, operation log
crates/spotuify-search local search index
crates/spotuify-mcp MCP stdio/HTTP server
crates/spotuify-lyrics Spotify/LRCLIB lyrics providers
crates/spotuify-system cover cache, notifications, media-control helpers
crates/spotuify-audio audio analyzer and visualization sources
