blastoise-archive/AGENTS.md

51 lines
2.5 KiB
Markdown

# 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, reset `startedAt`
- A 1s `setInterval` checks 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** — `Stream` class. 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
```ts
interface Track { filename: string; title: string; duration: number }
// Stream.getState() returns:
{ track: Track | null, currentTimestamp: number, streamName: string }
```
## Client sync logic
On WebSocket message:
1. New track → load audio, seek to server timestamp, play
2. Same track, drift < 2s ignore
3. 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).