TTS audio streaming proxy — subscribes to Redis Pub/Sub and forwards audio chunks to WebSocket clients.
- Client connects via WebSocket to
/ws-player?auth=<token>&chatId=<id> - Server subscribes to Redis Pub/Sub channel
tts:stream:{chatId} - When the backend publishes TTS audio, chunks are forwarded as binary WebSocket messages
- Control frames (
tts_start,tts_end,tts_error) are sent as JSON text messages
Messages on tts:stream:{chatId} use a single-byte prefix:
| Byte | Constant | Payload |
|---|---|---|
| 0x01 | TTS_START |
JSON: { messageId, format } |
| 0x02 | TTS_CHUNK |
Raw MP3 audio bytes |
| 0x03 | TTS_END |
JSON: { messageId } |
| 0x04 | TTS_ERROR |
JSON: { messageId, error } |
| Type | Format | Description |
|---|---|---|
tts_start |
JSON | { type: "tts_start", messageId, format } |
| (binary) | Binary | Raw MP3 audio chunk |
tts_end |
JSON | { type: "tts_end", messageId } |
tts_error |
JSON | { type: "tts_error", messageId, error } |
| Variable | Default | Description |
|---|---|---|
REDIS_URL |
redis://redis:6379 |
Redis server for Pub/Sub |
IDLE_TIMEOUT_MS |
300000 |
WebSocket idle timeout (5 minutes) |
# Run with watch mode
deno task dev
# Run tests
deno task testdocker build -t stream-player .
docker run -p 8001:8001 -e REDIS_URL=redis://localhost:6379 stream-playerBackend (Redis publish) → Redis Pub/Sub → StreamPlayer → WebSocket → Browser
StreamPlayer is a stateless proxy. It holds no audio data — it simply bridges Redis Pub/Sub to WebSocket clients. Multiple instances can run behind a load balancer since each subscribes independently to Redis.