denpa-radio/README.md

80 lines
3.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# denpa-radio
a tiny independent multi-station internet radio. cassette-deck aesthetic, real audio, no algorithms.
live at **<https://denpa.femboy.page>**.
## what's playing
| station | description | mp3 | opus |
| --------- | ---------------------------------------- | --------------------------------------------------- | ----------------------------------------------------- |
| minecraft | C418, Lena Raine, Aaron Cherof — vol α+β | <https://denpa.femboy.page/minecraft.mp3> (192k) | <https://denpa.femboy.page/minecraft.opus> (96k) |
drop a stream URL into VLC, mpv, foobar, or any internet-radio app.
## endpoints
- `/<station>.mp3` — MP3 192 kbps stream
- `/<station>.opus` — Opus 96 kbps stream
- `/now-playing/<station>.json` — currently playing (artist, title, album, duration, started_at)
- `/now-playing/<station>.history.json` — last 50 tracks for the station
- `/api/stations.json` — list of all stations with metadata
- `/api/stations/<station>/cover` — cover art (when present)
- `/status-json.xsl` — raw Icecast status (listener counts, mount info)
CORS is open on all of the above; build your own scrobbler / dashboard / overlay.
## architecture
```
Caddy (host) ─┬─ /<station>.{mp3,opus}, /status-json.xsl → icecast (libretime/icecast)
├─ /now-playing/*.json → caddy file_server
├─ /api/* → frontend (astro 5 + react)
└─ / → frontend
└─ liquidsoap (savonet/liquidsoap)
│ reads station folders
│ writes now-playing JSON
Hetzner Storage Box (CIFS, mounted RO)
```
three docker containers (icecast, liquidsoap, frontend) and a Caddy host service. each station is a folder on the storage box with a `_meta.yml`, a `tracks/` subdir, and an optional `cover.{jpg,png,webp}`. the frontend reads `_meta.yml` files at request time, so adding a station does not require a rebuild.
## adding a station
on the storage box (mounted at `/mnt/trashbox/denpa-radio/library/` on summer):
```
mkdir -p <station-id>/{tracks,jingles}
cat > <station-id>/_meta.yml <<EOF
name: My Station
description: short description
color: '#5cae34'
tags: [tag1, tag2]
EOF
# drop audio files into <station-id>/tracks/
# subdirs supported: tracks/Volume Alpha/, tracks/Singles/, etc — Liquidsoap recurses
# (optional) drop cover.jpg into <station-id>/ (NOT inside tracks/ — would fail to decode)
```
then on summer:
```
# duplicate the existing minecraft station block in /srv/denpa-radio/config/liquidsoap.liq
# rename references to your new id, then:
docker compose -f /srv/denpa-radio/docker-compose.yml restart liquidsoap
```
the frontend picks up the new station within ~30s. no Caddy reload, no Icecast restart.
station ids must match `[a-z0-9-]+`.
## tags
- **v0.1.0** — radio backend live (Minecraft station, MP3 + Opus).
- **v0.2.0** — frontend live (cassette deck UI, real WebAudio spectrum, recently-played).
## working on this repo
see [`CLAUDE.md`](./CLAUDE.md) for the full operator's manual: SSH conventions, deploy quirks, frontend stack notes, Liquidsoap gotchas, and the things we've already learned the hard way.