2.5 KiB
2.5 KiB
MusicRoom
Synchronized music streaming server built with Bun. Manages "streams" (virtual radio stations) that play through playlists sequentially. Clients connect, receive now-playing state, download audio, and sync playback locally.
Architecture
The server does NOT decode or play audio. It tracks time:
currentTimestamp = (Date.now() - stream.startedAt) / 1000- When
currentTimestamp >= track.duration, advance to next track, resetstartedAt - A 1s
setIntervalchecks if tracks need advancing and broadcasts state every 30s
Routes
GET / → Serves public/index.html
GET /api/streams → List active streams (id, name, trackCount)
GET /api/streams/:id → Current stream state (track, currentTimestamp, streamName)
WS /api/streams/:id/ws → WebSocket: pushes state on connect, every 30s, and on track change
GET /api/tracks/:filename → Serve audio file from ./music/ with Range request support
Files
- server.ts — Bun entrypoint. Loads playlist config, reads track metadata via
music-metadata, sets up HTTP routes and WebSocket handlers. Auto-discovers audio files in./music/when playlist tracks array is empty. - stream.ts —
Streamclass. Holds playlist, current index, startedAt timestamp, connected WebSocket clients. Manages time tracking, track advancement, and broadcasting state to clients. - playlist.json — Config file. Array of stream definitions, each with id, name, and tracks array (empty = auto-discover).
- public/index.html — Single-file client with inline JS/CSS. Connects via WebSocket, receives state updates, fetches audio, syncs playback. Has progress bar, track info, play/pause button, volume slider.
- music/ — Directory for audio files (.mp3, .ogg, .flac, .wav, .m4a, .aac).
Key types
interface Track { filename: string; title: string; duration: number }
// Stream.getState() returns:
{ track: Track | null, currentTimestamp: number, streamName: string }
Client sync logic
On WebSocket message:
- New track → load audio, seek to server timestamp, play
- Same track, drift < 2s → ignore
- Same track, drift >= 2s → seek to server timestamp
Progress bar updates from audio.currentTime when playing, from extrapolated server time when not playing (grey vs green color).
Config
Default port 3001 (override with PORT env var). Track durations read from file metadata on startup with music-metadata (duration: true for full-file scan, needed for accurate OGG durations).