Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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)

Installation

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

  1. Get Spotify API Credentials:

    • Visit Spotify Developer Dashboard
    • Create an app
    • Note your Client ID and Client Secret
    • Add http://localhost:8888/callback as a redirect URI
  2. Start the daemon:

    spotiline daemon start
    
  3. 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
  4. 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:47836 for 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

KeyAction
qQuit
SpacePlay / Pause
nNext track
pPrevious track
Seek backward 10s
Seek forward 10s
/ Navigate lists
EnterPlay selected track
TabSwitch focus (playlists ↔ tracks)
/Enter search mode

Search Mode

KeyAction
EscExit search
EnterPlay first result
BackspaceDelete character
/ Navigate results
Any charType 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
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 name
  • artist: Primary artist
  • progress_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: Success
  • 1: 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 rspotify to 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, Queue
    • IPCResponse: 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:

  1. Spotify Premium account
  2. At least one active Spotify device (official app or speaker)
  3. Run spotiline status to 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

  1. Fork the repository
  2. Create a branch for your feature (git checkout -b feature/my-feature)
  3. Write tests for new functionality
  4. Run tests before committing (cargo test)
  5. Format code with cargo fmt
  6. Run clippy for lints (cargo clippy)
  7. Commit with clear, concise messages
  8. Push and create a pull request

Testing

cargo test --all

Acceptance tests (from PRD):

  1. Daemon runs 24h without memory leaks
  2. spotiline status --json | jq . outputs valid JSON
  3. TUI space key responds <100ms
  4. 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.