Real-time Spotify dashboard built with Rust backend and SvelteKit frontend. Tracks current playback state, displays queue, handles search requests, and includes a basic chat system.
Backend compiles to single binary with static assets embedded. Frontend builds to static files served by backend in production.
cd backend && cargo run
cd frontend && npm run devWith docker:
docker-compose up --buildCreate a .env file in the backend directory:
RSPOTIFY_CLIENT_ID=your_spotify_client_id_here
RSPOTIFY_CLIENT_SECRET=your_spotify_client_secret_here
RSPOTIFY_REDIRECT_URI=http://localhost:6412/callbackGet credentials at: https://developer.spotify.com/dashboard
The backend runs on Axum 0.8 with tokio async runtime. Key components:
- Spotify API Integration:
rspotifycrate handles OAuth2 flow and Web API requests - SSE Stream: Broadcasts playback state changes via Server-Sent Events
- State Management: Shared
AppStatewrapped inArc<RwLock<T>>for concurrent access - Background Service: Polls Spotify API every 500ms to detect playback changes
- Static File Serving: Serves media files from
/backend/static/ - CORS: Configured for frontend origin on development
// Core state structure
pub struct AppState {
spotify: AuthCodeSpotify,
is_auth: bool,
chat: Chat,
tx: broadcast::Sender<AppEvent>,
listening_state: ListeningState,
}SvelteKit app with static adapter for deployment:
- EventSource: Connects to
/api/eventsSSE endpoint for live updates - Component Architecture: Modular Svelte components for each UI section
- State Reactivity: Svelte stores manage application state
- HTTP Client: Fetch API for Spotify search and control endpoints
- CSS: Custom styling with Atkinson Hyperlegible font
- Spotizerr: Third-party service for track downloads (separate container)
- Redis: Used by Spotizerr for job queuing and caching
- Docker Compose: Multi-container orchestration
- Volume Mounts: Shared storage between containers for media files
GET / # Serve frontend
GET /api/auth # Initiate Spotify OAuth
GET /api/callback # OAuth callback handler
GET /api/events # SSE stream for real-time updates
GET /api/search?q= # Search Spotify catalog
POST /api/chat # Add chat message
GET /api/queue/add?uri= # Add track to Spotify queue
The backend broadcasts events via tokio broadcast channel:
pub enum AppEvent {
PlaybackChanged(Option<Playback>),
QueueChanged(Option<Tracks>),
ChatMessage(String),
}Frontend receives these as SSE and updates UI reactively.