# Radio Conjurer - Liquidsoap runtime built through opam, so the OCaml, # opam and Liquidsoap versions are all selectable at build time. # # Build from the repository root, e.g.: # docker build -f docker/Dockerfile.radio -t conjurer-radio . # docker build -f docker/Dockerfile.radio -t conjurer-radio \ # --build-arg LIQUIDSOAP_VERSION=2.1.4 --build-arg OCAML_VERSION=4.14.2 . # # Version notes (matched to radio_conjurer.liq, which targets 2.1.4): # * Liquidsoap 2.1.x requires OCaml 4.x - it does NOT build on OCaml 5. # Production ran the 4.13.0 switch; 4.14.x is the maintained 4.x line and # builds 2.1.4 fine. Pass OCAML_VERSION=4.13.0 for bit-for-bit parity. # * The ocaml-ffmpeg bindings compatible with 2.1.4 target the FFmpeg 5.x # library family (libavcodec59/libavformat59/libavutil57...). Debian # bookworm ships exactly that, which is why this image needs NO ffmpeg # pinning: the old ffmpeg.pref (Pin-Priority 1001 on deb.debian.org) only # existed to force those Debian builds over the Raspberry Pi OS repo's # conflicting ones. Single-repo container = the pin is redundant. FROM debian:bookworm-slim ARG OPAM_VERSION=2.1.5 ARG OCAML_VERSION=4.14.2 ARG LIQUIDSOAP_VERSION=2.1.4 # Optional liquidsoap features, resolved together with liquidsoap by opam. # These cover everything radio_conjurer.liq uses: # mad+lame - mp3 decode/encode (%mp3 icecast stream) # cry - output.icecast # taglib - tag/replaygain metadata # pulseaudio- input.pulseaudio (mic) + output.pulseaudio # samplerate- resampling # inotify - playlist(reload_mode="watch") # ffmpeg - decode fallback + replaygain computation ARG LIQ_OPAM_PACKAGES="mad lame cry taglib pulseaudio samplerate inotify ffmpeg" # System libraries: build deps for the opam packages above plus the matching # runtime libs. The libav*-dev list mirrors the old ffmpeg.pref family. RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential m4 pkg-config git curl ca-certificates unzip rsync \ libpcre3-dev libgmp-dev zlib1g-dev \ libmad0-dev libmp3lame-dev libtag1-dev \ libpulse-dev libsamplerate0-dev \ libavcodec-dev libavformat-dev libavutil-dev libavfilter-dev \ libavdevice-dev libswresample-dev libswscale-dev libpostproc-dev \ ffmpeg \ pulseaudio pulseaudio-utils \ icecast2 jq \ && rm -rf /var/lib/apt/lists/* # Group membership for PulseAudio/ALSA access. On the Pi this was done by # hand for the system-wide pulse socket; in the image it is baked in. The # 'pulse-access' group comes with the pulseaudio package. RUN usermod -aG audio,pulse-access root # opam as a static binary so OPAM_VERSION is a real choice (apt would pin us # to whatever bookworm ships). RUN ARCH=$(uname -m) \ && curl -fsSL -o /usr/local/bin/opam \ "https://github.com/ocaml/opam/releases/download/${OPAM_VERSION}/opam-${OPAM_VERSION}-${ARCH}-linux" \ && chmod +x /usr/local/bin/opam # OCaml switch + liquidsoap and its optional feature libraries in one solve, # so liquidsoap is compiled WITH those features enabled. RUN opam init -y --bare --disable-sandboxing \ && opam switch create default "${OCAML_VERSION}" \ && opam install -y "liquidsoap.${LIQUIDSOAP_VERSION}" ${LIQ_OPAM_PACKAGES} \ && opam clean -a -c -s --logs ENV PATH="/root/.opam/default/bin:${PATH}" # Build-time smoke test: the binary runs and reports the requested version. RUN liquidsoap --version # Minimal system-wide pulse config for headless VMs (PULSE_MODE=internal): # a null sink so output.pulseaudio()/input.pulseaudio() work with no sound # hardware (mic becomes silence, which blank.strip already gates out). COPY docker/pulse-system.pa /etc/pulse/system.pa # The script and its persistent params are seeded into the data volume on # first run (never overwritten), so live edits survive image rebuilds. The # icecast config is rendered from the template at startup with passwords # taken from the secrets volume (never baked into the image). WORKDIR /app COPY conjurer_musician/radio_conjurer.liq ./radio_conjurer.liq COPY conjurer_musician/script.params ./script.params COPY docker/icecast.xml.tpl ./icecast.xml.tpl COPY docker/entrypoint.radio.sh /usr/local/bin/entrypoint.sh RUN chmod +x /usr/local/bin/entrypoint.sh # Volume layout matches the paths inside radio_conjurer.liq: # /srv/betoniarka/data - playlists, script.params, persistence/radio logs # /srv/betoniarka/music - the mp3 library # /srv/betoniarka/secrets - icecast_credentials.json (provisioned at install) VOLUME ["/srv/betoniarka/data", "/srv/betoniarka/music", "/srv/betoniarka/secrets"] # 8000 = icecast (listeners), 54321 = harbor /skip (the bot's RADIO_HARBOR), # 9999 = interactive harbor. EXPOSE 8000 54321 9999 ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] CMD ["liquidsoap", "/srv/betoniarka/data/radio_conjurer.liq"]