Per review of the first radio-container cut:
- radio_conjurer.liq: replace the cargo-culted /home/pi/Conjurer and
/home/pi/MediaFolder paths with the container layout -
/srv/betoniarka/data (playlists, script.params, persistence/radio logs),
/srv/betoniarka/music (library + emergency track) and
/srv/betoniarka/secrets/icecast_credentials.json;
interactive.persistent now uses an absolute path;
output.icecast targets host="localhost" (icecast lives in the container)
- Icecast2 now runs INSIDE the radio container: entrypoint renders
/etc/icecast2/icecast.xml from docker/icecast.xml.tpl, filling
source/admin/relay passwords from the secret provisioned at install time
(same pattern as the bot's netrc); optional admin_password/relay_password
fields default to password; icecast starts as its unprivileged user;
port 8000 exposed for listeners; extra_hosts hack removed
- compose.radio.yaml: same-path mounts for /srv/betoniarka/{data,music,secrets}
- ffmpeg.pref removed from the repo (the pin only mattered against the
RPi OS repo; bookworm ships the right FFmpeg 5.x natively) - verdict
preserved in the docs and the legacy installer note updated
- docs: updated radio section (volumes, secret provisioning, wire-up)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Dockerises the radio_conjurer.liq environment on Debian bookworm:
- Dockerfile.radio builds Liquidsoap through opam with build args for
OPAM_VERSION (static binary), OCAML_VERSION (default 4.14.2; 2.1.x needs
OCaml 4.x - prod ran 4.13.0, pass it for parity) and
LIQUIDSOAP_VERSION (default 2.1.4, matching the script)
- LIQ_OPAM_PACKAGES installs exactly the features the script uses:
mad+lame (mp3), cry (icecast), taglib (tags/replaygain), pulseaudio
(mic/out), samplerate, inotify (watch-reload), ffmpeg
- ffmpeg.pref verdict: the Pi pin forced Debian's FFmpeg 5.x family
(libavcodec59...) over RPi OS repo builds because the ocaml-ffmpeg
bindings are compiled against those sonames; bookworm ships them
natively so the container needs no pin (documented, not copied)
- pulse-access/audio group memberships baked in (were manual on the Pi);
PULSE_MODE=internal runs a system-wide pulse with a null sink so the
unmodified script works on a headless VM (mic = silence), host mode
mounts the real socket, none for edited scripts
- mounts mirror the script's hardcoded paths (/home/pi/Conjurer,
/home/pi/MediaFolder/mp3) so radio_conjurer.liq runs unmodified;
entrypoint seeds script+params on first run (never overwrites), creates
missing playlists, seeds placeholder icecast credentials (loud warning)
and generates a silent emergency mp3 when the single() file is missing
- compose.radio.yaml: extra_hosts maps the script's host="radio" to
ICECAST_HOST_IP; exposes 54321 (bot's RADIO_HARBOR /skip) and 9999
- docs: radio section with build args, pulse modes and the ffmpeg.pref /
groups verdicts
Part 2 of this PR (splitting bot<->radio comms out of the musician into a
radio+betoniarka container) follows on this branch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>