«Send denne kl. 18:00.» Engangs, ikke gjentakende. Dette er den første delen av appen som må ligge på disk. Kø og historikk har alltid bare levd i minnet, og det har gått bra fordi en melding spilles med en gang. En melding planlagt til 18:00 må overleve at containeren startes om 17:45, så schedule.py lagrer i data/scheduled.json – skrevet til side og flyttet på plass, så fila aldri er halvskrevet. Ingen database, med vilje: dette er en håndfull meldinger på et arrangement, og en fil man kan lese med cat er verdt mer her enn indekser. En ødelagt fil hindrer ikke oppstart; da er det bedre å miste planene og si det i loggen. Modulen vet ingenting om Flask eller avspilling. Den lagrer, og den svarer på «hva skal sendes nå?». _scheduler() i announcer.py eier tråden som spør hvert 20. sekund og legger forfalte meldinger i den vanlige køen – derfra oppfører de seg nøyaktig som en melding sendt for hånd. Gikk den glipp av, spilles den ikke. due() skiller på grace_seconds (120): innenfor den sendes meldingen, utenfor markeres den missed og vises som det. Nådefristen er slingringsmonn for at tråden våkner med mellomrom og for en kjapp omstart, ikke en policy om å ta igjen etterslep – «turneringen starter om ti minutter» skal ikke ut på anlegget en time etter at maskina kom tilbake. due() endrer ingenting selv; markeringen skjer først etter at meldingen faktisk er i kø, så ingenting kan stå som «sendt» uten å ha blitt det. Klienten sender absolutt tidspunkt (epoch), så tidssoner spiller ingen rolle for selve planleggingen. TZ settes likevel fra .env, ellers står loggen i UTC mens man selv står i CEST. Planlegging er låst til samme forhåndslytt-nøkkel som «Send ut»: man planlegger bare det man har hørt. Det gir en bonus – forhåndslytt varmer cache/, så WAV-en ligger ferdig på disk, og en planlagt edge-tts-melding spiller kl. 18:00 selv om nettet er nede da. Sine egne kan man alltid avbryte. Andres krever clear, samme rett som å tømme køen, siden det er samme slags inngrep i noe andre har satt i gang. 23 nye tester. Verifisert i container: sending fyrer og spiller, overlever en omstart av containeren, og en melding som forfalt mens maskina var av blir markert missed uten å bli spilt. Merk ved oppgradering: schedule er en ny handling, og et oppsett med eksplisitt [roles] i config.toml får den ikke automatisk. Den må legges inn selv. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
42 lines
1.6 KiB
Docker
42 lines
1.6 KiB
Docker
# Announcer kjøres i Docker på klienten som er koblet til høyttaleranlegget.
|
||
#
|
||
# Lyden er det som gjør bildet litt uvanlig: containeren må nå vertens
|
||
# lydserver. Det løses i compose.yaml ved å montere inn vertens
|
||
# XDG_RUNTIME_DIR og kjøre som samme bruker-ID som eier socketen – ikke her.
|
||
FROM python:3.13-slim
|
||
|
||
# ffmpeg: edge_pcm() dekoder MP3 med den, og egne jinglefiler normaliseres
|
||
# med den. pipewire-bin gir pw-play, pulseaudio-utils gir paplay; koden
|
||
# velger selv hvilken som finnes (player_cmd()), så begge lydservere virker.
|
||
RUN apt-get update \
|
||
&& apt-get install -y --no-install-recommends \
|
||
ffmpeg ca-certificates tzdata pipewire-bin pulseaudio-utils \
|
||
&& rm -rf /var/lib/apt/lists/*
|
||
|
||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
|
||
|
||
ENV UV_PROJECT_ENVIRONMENT=/opt/venv \
|
||
UV_COMPILE_BYTECODE=1 \
|
||
PATH=/opt/venv/bin:$PATH \
|
||
PYTHONUNBUFFERED=1
|
||
|
||
WORKDIR /app
|
||
COPY pyproject.toml uv.lock ./
|
||
RUN uv sync --frozen --no-dev
|
||
|
||
# Koden sist, så avhengighetslaget gjenbrukes når bare appen endres.
|
||
COPY announcer.py auth.py config.py schedule.py index.html ./
|
||
|
||
# Alt som skal overleve en ny container ligger under /data, som monteres
|
||
# inn fra verten. Stemmene er ~670 MB og lastes ned ved første oppstart.
|
||
ENV ANNOUNCER_VOICES=/data/voices \
|
||
ANNOUNCER_CACHE=/data/cache \
|
||
ANNOUNCER_CHIMES=/data/chimes \
|
||
ANNOUNCER_LOG=/data/logs/announcements.jsonl \
|
||
ANNOUNCER_STATE=/data/instance \
|
||
ANNOUNCER_SCHEDULE=/data/scheduled.json \
|
||
ANNOUNCER_CONFIG=/data/config.toml \
|
||
ANNOUNCER_PORT=8090
|
||
|
||
EXPOSE 8090
|
||
CMD ["python", "announcer.py", "--host", "0.0.0.0"]
|