Introduction
Welcome to Spotiline — the headless, native Spotify client engineered for terminal power users and autonomous AI agents.
Why Spotiline?
Graphical interfaces consume system resources and can’t be easily scripted or controlled by AI agents. Spotiline provides:
- Native Performance: <50MB RAM idle, near-zero CPU usage
- Dual Interface: Beautiful TUI for humans, clean JSON API for agents
- Terminal-First: No GUI dependencies, works over SSH
- Scriptable: Every action accessible via CLI
Requirements
- Spotify Premium account (required by underlying audio libraries)
- Active internet connection
- Rust 1.70+ (for building from source)
Quick Links
Installation
Via Cargo (Recommended)
cargo install spotiline
From Source
git clone https://github.com/ejafee/spotiline.git
cd spotiline
cargo build --release
# Binary at target/release/spotiline
Pre-compiled Binaries
Download from GitHub Releases:
spotiline-x86_64-unknown-linux-gnu(Linux x64)spotiline-aarch64-unknown-linux-gnu(Linux ARM)spotiline-x86_64-apple-darwin(macOS Intel)spotiline-aarch64-apple-darwin(macOS Apple Silicon)spotiline-x86_64-pc-windows-msvc.exe(Windows x64)
Verify Installation
spotiline --version
Authentication
Spotiline uses Spotify’s OAuth 2.0 flow to authenticate with your account.
First-Time Setup
-
Get Spotify API Credentials:
- Visit Spotify Developer Dashboard
- Create an app
- Note your Client ID and Client Secret
- Add
http://localhost:8888/callbackas a redirect URI
-
Start the daemon:
spotiline daemon start -
Follow the prompts:
- Enter your Client ID and Client Secret
- Open the authorization URL in your browser
- After authorizing, paste the full redirect URL back
-
Credentials are stored securely:
- Tokens saved to OS keychain (Keychain on macOS, Credential Manager on Windows, Secret Service on Linux)
- No passwords stored in plain text
Environment Variables (Optional)
export SPOTIFY_CLIENT_ID="your_client_id"
export SPOTIFY_CLIENT_SECRET="your_client_secret"
If set, Spotiline will use these instead of prompting.
The Background Daemon
Spotiline runs a background daemon that manages your Spotify connection.
Starting the Daemon
spotiline daemon start
The daemon:
- Connects to Spotify Web API
- Listens on
127.0.0.1:47836for IPC commands - Runs in the background until stopped
Stopping the Daemon
spotiline daemon stop
Auto-Start Behavior
When you launch the TUI (spotiline with no args), it automatically starts the daemon if it’s not running.
Logs
Daemon logs are written to:
- Linux:
~/.local/state/spotiline/spotiline.log - macOS:
~/Library/Logs/spotiline/spotiline.log - Windows:
%LOCALAPPDATA%\spotiline\logs\spotiline.log
Keyboard Bindings
Navigation Mode
| Key | Action |
|---|---|
q | Quit |
Space | Play / Pause |
n | Next track |
p | Previous track |
← | Seek backward 10s |
→ | Seek forward 10s |
↑ / ↓ | Navigate lists |
Enter | Play selected track |
Tab | Switch focus (playlists ↔ tracks) |
/ | Enter search mode |
Search Mode
| Key | Action |
|---|---|
Esc | Exit search |
Enter | Play first result |
Backspace | Delete character |
↑ / ↓ | Navigate results |
| Any char | Type to search (live results) |
Customization
Config File
Optional config at:
- Linux/macOS:
~/.config/spotiline/config.toml - Windows:
%APPDATA%\spotiline\config.toml
If the file doesn’t exist, hardcoded defaults are used.
Example config.toml
[daemon]
port = 47836
audio_backend = "auto" # or "alsa", "pulseaudio", "pipewire"
[ui]
color_bg = "#121212"
color_highlight = "#1DB954"
Colors
Hex color values for TUI theme:
color_bg: Background color (default:#121212— dark charcoal)color_highlight: Accent color (default:#1DB954— Spotify green)
Restart the TUI to see changes.
Command Reference
All commands require the daemon to be running.
Daemon Management
spotiline daemon start # Start background daemon
spotiline daemon stop # Stop background daemon
Playback Control
spotiline play # Resume playback
spotiline pause # Pause playback
spotiline next # Skip to next track
spotiline prev # Previous track
spotiline seek <seconds> # Jump to position
spotiline volume <0-100> # Set volume
Search
spotiline search "query" [--type track|album|artist|playlist] [--play]
Examples:
spotiline search "lofi beats"
spotiline search "Daft Punk" --type artist
spotiline search "Starboy" --play # Play first result
Queue
spotiline queue <spotify:track:...>
Status
spotiline status # Human-readable
spotiline status --json # Pure JSON (for AI agents)
AI & JSON Outputs
The --json flag outputs strict, machine-parseable JSON with no ANSI colors or extra text.
Status JSON Schema
spotiline status --json
{
"state": "playing",
"track": "Starboy",
"artist": "The Weeknd",
"progress_ms": 75000,
"duration_ms": 230000
}
Fields:
state:"playing","paused", or"stopped"track: Track nameartist: Primary artistprogress_ms: Current position (milliseconds)duration_ms: Total duration (milliseconds)
Search Results JSON
spotiline search "lofi" --json
[
{
"name": "Lofi Study",
"artist": "Chillhop Music",
"uri": "spotify:track:...",
"duration_ms": 180000
}
]
Error JSON
{
"error": "Daemon not running (no PID file)"
}
Exit Codes
0: Success1: Error (check stderr or JSON output)
Shell Scripting Examples
Bash Alias for Now Playing
alias now="spotiline status --json | jq -r '.track + \" - \" + .artist'"
Auto-tweet Now Playing
#!/bin/bash
TRACK=$(spotiline status --json | jq -r '.track')
ARTIST=$(spotiline status --json | jq -r '.artist')
twitter tweet "🎵 Now listening: $TRACK by $ARTIST"
Check if Daemon is Running
if spotiline status &>/dev/null; then
echo "Daemon is running"
else
spotiline daemon start
fi
Play Random Search Result
spotiline search "chill vibes" --json | jq -r '.[0].uri' | xargs spotiline queue
spotiline next
Loop Through Search Results
spotiline search "synthwave" --json | jq -r '.[] | .name + " by " + .artist'
System Architecture
High-Level Overview
User (Human / AI Agent)
↓
spotiline binary
├─ No args → TUI (ratatui)
└─ Args → CLI (clap)
↓
TCP IPC (127.0.0.1:47836)
↓
Background Daemon
├─ rspotify → Spotify Web API
└─ Playback state sync
Components
1. CLI Parser (clap)
Routes commands to TUI or CLI mode based on arguments.
2. Background Daemon
- Runs detached process
- TCP server on port 47836
- Handles IPC commands (play, pause, status, etc.)
- Uses
rspotifyto communicate with Spotify Web API
3. TUI (ratatui + crossterm)
- Connects to daemon via TCP
- Polls status every 1s for real-time updates
- Live search queries daemon
4. IPC Protocol
- Serialized via
bincode - Command/Response enums:
IPCCommand: Play, Pause, Next, Seek, Status, Search, QueueIPCResponse: Ok, State, SearchResults, Error
Data Flow Example
User presses Space in TUI
→ TUI sends IPCCommand::Play over TCP
→ Daemon receives, calls rspotify.resume_playback()
→ Daemon responds IPCResponse::Ok
→ TUI updates UI state
Logging & Debugging
Log Files
Logs written via tracing to OS-specific paths:
- Linux:
~/.local/state/spotiline/spotiline.log - macOS:
~/Library/Logs/spotiline/spotiline.log - Windows:
%LOCALAPPDATA%\spotiline\logs\spotiline.log
Log Levels
Default: INFO
Override with environment variable:
export RUST_LOG=debug
spotiline daemon start
Levels: trace, debug, info, warn, error
Common Issues
“Daemon not running”
spotiline daemon start
“Failed to parse redirect URL”
Ensure you paste the full URL from the browser after authorizing, including http://localhost:8888/callback?code=...
Network errors in logs
Check internet connection. Spotiline requires active connection to Spotify servers.
Playback not working
Ensure:
- Spotify Premium account
- At least one active Spotify device (official app or speaker)
- Run
spotiline statusto verify connection
Debug Mode
RUST_LOG=debug spotiline 2>&1 | tee debug.log
Captures all debug output to debug.log.
Contributing
Thanks for your interest in contributing to Spotiline!
Code of Conduct
Be respectful, inclusive, and professional in all interactions.
Development Setup
git clone https://github.com/ejafee/spotiline.git
cd spotiline
cargo build
cargo test
PR Guidelines
- Fork the repository
- Create a branch for your feature (
git checkout -b feature/my-feature) - Write tests for new functionality
- Run tests before committing (
cargo test) - Format code with
cargo fmt - Run clippy for lints (
cargo clippy) - Commit with clear, concise messages
- Push and create a pull request
Testing
cargo test --all
Acceptance tests (from PRD):
- Daemon runs 24h without memory leaks
spotiline status --json | jq .outputs valid JSON- TUI space key responds <100ms
- Network disconnect logs
[ERROR]with timestamp
Roadmap (v1.1+)
- Synced lyrics (lrclib.net integration)
- Podcast support
- Real-time FFT visualizer
- Playlist management
- Spotify Free tier support (if possible)
Questions?
Open an issue on GitHub.