3.2 KiB
3.2 KiB
radio-go — AI Radio broadcast pipeline
Endless AI-generated radio for your trusty MP3 internet stereo.
Three stages, two machines:
┌─────────────────────┐ FLAC ┌──────────┐ mp3+playlist ┌───────────────────────────┐
│ generator (anywhere)│ ────────► │ shipper │ ──────scp────► │ streaming VPS │
│ LLM + ComfyUI/YuE2 │ library │ (Go bin) │ │ streamer ─► icecast ◄─Caddy│
└─────────────────────┘ └──────────┘ │ │ now-playing │
│ ▼ MQTT │
└───────────────────────────┘
│
your stereo: https://…/ai-radio.mp3
- shipper (generator machine): reads the ai-radio app's
library/index.json, builds a rolling newest-biased playlist, transcodes FLAC→MP3 (ID3-tagged so the stereo display shows the title), ships*.mp3 + playlist.m3u8 + titles.jsonto the VPS. - streamer (VPS): plays the shipped files into an icecast mount as one endless live stream, publishing a now-playing JSON to MQTT per track. Survives the generator being asleep — replays what's on disk.
- VPS bundle: icecast (HTTP :8000 for old stereos) + Caddy (automatic HTTPS)
- streamer, all docker compose.
Quick start
# build & test
go build ./... && go test ./...
# generator side (needs ffmpeg):
cp .env.example ship.env && $EDITOR ship.env
set -a; . ./ship.env; set +a
go run ./cmd/shipper # loops; --once for a single cycle
# VPS side: see vps/DEPLOY.md
Docs
| Level | File | Audience |
|---|---|---|
| Overview | this README | everyone |
| Architecture & design decisions | docs/architecture.md | contributors |
| Configuration reference | docs/config.md | operators |
| VPS deployment | vps/DEPLOY.md | deploying the server |
| Stereo setup | docs/stereo-setup.md | the listener |
| Code-level | godoc comments in internal/ |
developers |
Repo layout
cmd/shipper/ generator-side binary (library -> transcode -> scp)
cmd/streamer/ VPS-side binary (playlist -> icecast + MQTT now-playing)
internal/domain/ pure model: Track, playlist policy (no I/O)
internal/app/ use-cases: library load, transcode, ship
internal/infra/ adapters: m3u, icecast source proto, MQTT 3.1.1 client
vps/ docker-compose bundle (icecast + streamer + caddy)
tests/ e2e fakes (fake icecast + MQTT broker)
Testing
go test ./... # unit tests (domain/infra/app)
python3 tests/fakes.py & # fake icecast:18000 + mqtt:18883
STREAM_WEBROOT=... ICECAST_ADDR=127.0.0.1:18000 MQTT_ADDR=127.0.0.1:18883 \
go run ./cmd/streamer # e2e against the fakes