docker: dedicated musician entrypoint + fuller runbook section

- docker/entrypoint.musician.sh: on a fresh /data volume, create the
  managed playlists and touch radio_log.log/persistence.log empty so the
  track-forwarding thread waits instead of crashing when Liquidsoap runs
  elsewhere; never overwrites preserved files
- Dockerfile.musician: wire the entrypoint, default CONJURER_MUSICIAN_BASE
  =/data (persist playlists/logs) and CONJURER_STREAM_TEMPLATE=/app/stream.html;
  declare the /data volume
- DOCKER_PROXMOX.md: expand the musician section (two volumes, preserving
  existing playlists, radio-log coupling, stream template)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Michal Tuszowski
2026-07-02 08:48:10 +02:00
committed by Michał Tuszowski
parent 597bc004fc
commit 934e7a6240
3 changed files with 74 additions and 8 deletions
+8 -3
View File
@@ -17,14 +17,19 @@ RUN pip install --no-cache-dir --upgrade pip \
&& pip install --no-cache-dir -r requirements_musician.txt && pip install --no-cache-dir -r requirements_musician.txt
COPY conjurer_musician/ ./ COPY conjurer_musician/ ./
COPY docker/entrypoint.musician.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
ENV PYTHONUNBUFFERED=1 \ ENV PYTHONUNBUFFERED=1 \
CONJURER_MUSICIAN_HOST=0.0.0.0 \ CONJURER_MUSICIAN_HOST=0.0.0.0 \
CONJURER_MUSICIAN_PORT=5000 \ CONJURER_MUSICIAN_PORT=5000 \
CONJURER_MUSIC_FOLDER=/music CONJURER_MUSIC_FOLDER=/music \
CONJURER_MUSICIAN_BASE=/data \
CONJURER_STREAM_TEMPLATE=/app/stream.html
# The mp3 library is mounted here (read-only is fine for serving). # /music = the mp3 library (read-only ok); /data = writable playlists + logs.
VOLUME ["/music"] VOLUME ["/music", "/data"]
EXPOSE 5000 EXPOSE 5000
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["python", "conjurer_musician.py"] CMD ["python", "conjurer_musician.py"]
+23
View File
@@ -0,0 +1,23 @@
#!/bin/sh
# Prepare the musician's writable volume so the web service starts cleanly on a
# fresh container. Existing files are never overwritten (preserves your data).
set -e
DATA="${CONJURER_MUSICIAN_BASE:-/data}"
MUSIC="${CONJURER_MUSIC_FOLDER:-/music}"
mkdir -p "$DATA" "$DATA/logs" "$MUSIC"
# The track-forwarding thread tails the Liquidsoap radio logs. When the radio
# runs separately (or hasn't started yet) these files may not exist; create
# them empty so the tailer waits instead of crashing.
for f in radio_log.log persistence.log; do
[ -e "$DATA/$f" ] || : > "$DATA/$f"
done
# Ensure the managed playlists exist (routes/rescan also create them; this just
# avoids a first-tick race before the initial scan).
for f in all_playlist.playlist hit.playlist request.playlist priority_queue.playlist; do
[ -e "$DATA/$f" ] || : > "$DATA/$f"
done
exec "$@"
+43 -5
View File
@@ -130,22 +130,60 @@ docker logs -f conjurer-librarian
--- ---
## 3. Musician (VM-musician) — you will adapt this ## 3. Musician (VM-musician)
The web service is container-ready (`docker/Dockerfile.musician` + The web service is container-ready (`docker/Dockerfile.musician` +
`compose.musician.yaml`). Note it containerises the **Flask file/playlist `compose.musician.yaml`). It containerises the **Flask file/playlist service
service only**; the Liquidsoap radio (`radio_conjurer.liq`) and any only** the Liquidsoap radio (`radio_conjurer.liq`), `script.params` and any
Samba/NFS share tooling are separate and typically stay on the host or a Samba/NFS share tooling are separate and typically stay on the host or a
dedicated setup. dedicated setup (you'll adapt those yourself).
### 3a. Two volumes: the library and the writable state
| Mount | Container path | Holds |
|-------|---------------|-------|
| `/srv/musician/music` | `/music` (`CONJURER_MUSIC_FOLDER`) | your mp3 library (indexed/served) |
| `/srv/musician/data` | `/data` (`CONJURER_MUSICIAN_BASE`) | playlists, logs, `radio_log.log`/`persistence.log` |
```bash ```bash
sudo mkdir -p /srv/musician/music /srv/musician/data sudo mkdir -p /srv/musician/music /srv/musician/data
# point /srv/musician/music at (or copy in) your mp3s
```
### 3b. Preserve existing playlists/state (optional)
If you already run the musician, copy its working playlists into the data
volume so nothing is regenerated from scratch:
```bash
sudo cp all_playlist.playlist hit.playlist request.playlist \
priority_queue.playlist playlist.json /srv/musician/data/ 2>/dev/null || true
```
The container's entrypoint (`docker/entrypoint.musician.sh`) creates any
missing playlists and touches `radio_log.log` / `persistence.log` empty so the
track-forwarding thread waits instead of crashing when the radio runs
elsewhere. It never overwrites files you copied in.
### 3c. Configure and launch
```bash
cp docker/env/musician.env.example docker/env/musician.env cp docker/env/musician.env.example docker/env/musician.env
# edit: CONJURER_MAIN_BOT=http://BOT_VM_IP:5000, CONJURER_API_KEY, mount your mp3s at /music # edit: CONJURER_MAIN_BOT=http://BOT_VM_IP:5000 and CONJURER_API_KEY (match the bot)
docker compose -f docker/compose.musician.yaml up -d --build docker compose -f docker/compose.musician.yaml up -d --build
docker logs -f conjurer-musician
``` ```
### 3d. Radio coupling (if you keep Liquidsoap separate)
The musician only forwards "now playing" to the bot by tailing the radio's
`radio_log.log` / `persistence.log`. To wire them up, have Liquidsoap write
those two files into the same `/srv/musician/data` directory (or set
`CONJURER_RADIO_LOG` / `CONJURER_PERSISTENCE_LOG` to wherever it writes). The
`/stream` page template is served from the baked-in `/app/stream.html`
(override with `CONJURER_STREAM_TEMPLATE` if you customise it).
--- ---
## 4. Networking & auth ## 4. Networking & auth