«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>
57 lines
2.4 KiB
YAML
57 lines
2.4 KiB
YAML
# Announcer i drift på klienten ved anlegget.
|
|
#
|
|
# ./install.sh setter opp alt, inkludert .env
|
|
# docker compose up -d senere, for hånd
|
|
#
|
|
# Testoppsettet med dummy-IdP ligger i compose.test.yaml, ikke her.
|
|
name: announcer
|
|
|
|
services:
|
|
announcer:
|
|
build: .
|
|
image: announcer:local
|
|
restart: unless-stopped
|
|
|
|
# BIND_IP er sikkerhetsmekanismen her, ikke ufw. Dockers publiserte
|
|
# porter går forbi ufw, så en ufw-regel ville ikke stengt noen ute.
|
|
# install.sh setter den til NetBird-adressen når wt0 finnes.
|
|
ports:
|
|
- "${BIND_IP:-0.0.0.0}:${ANNOUNCER_PORT:-8090}:8090"
|
|
|
|
# Må være brukeren som eier lydsocketen på verten, ellers kommer det
|
|
# ingen lyd. install.sh fyller inn id -u / id -g.
|
|
user: "${PUID:-1000}:${PGID:-1000}"
|
|
|
|
environment:
|
|
ANNOUNCER_SINK: ${ANNOUNCER_SINK:-}
|
|
ANNOUNCER_TITLE: ${ANNOUNCER_TITLE:-}
|
|
ANNOUNCER_PUBLIC_URL: ${ANNOUNCER_PUBLIC_URL:-}
|
|
# Hemmeligheter: hører i .env, ikke i config.toml.
|
|
ANNOUNCER_PIN: ${ANNOUNCER_PIN:-}
|
|
ANNOUNCER_JWT_SECRET: ${ANNOUNCER_JWT_SECRET:-}
|
|
ANNOUNCER_OIDC_CLIENT_SECRET: ${ANNOUNCER_OIDC_CLIENT_SECRET:-}
|
|
ANNOUNCER_SECRET_KEY: ${ANNOUNCER_SECRET_KEY:-}
|
|
XDG_RUNTIME_DIR: ${XDG_RUNTIME_DIR:-/run/user/1000}
|
|
# Planlagte sendinger lagres som absolutt tid, så TZ påvirker dem ikke.
|
|
# Men uten den står loggen i UTC mens du står i CEST, og da blir det
|
|
# vanskelig å se om noe gikk som det skulle.
|
|
TZ: ${TZ:-Europe/Oslo}
|
|
|
|
volumes:
|
|
# Stemmer, buffer, logg, sesjonsnøkkel, egne jingler og config.toml.
|
|
- ./data:/data
|
|
# Lyden. Hele XDG_RUNTIME_DIR, ikke bare én socket, så både
|
|
# PipeWire (pipewire-0) og PulseAudio (pulse/native + cookie) virker.
|
|
- ${XDG_RUNTIME_DIR:-/run/user/1000}:${XDG_RUNTIME_DIR:-/run/user/1000}
|
|
|
|
# Containeren starter ved boot, altså før noen er logget inn grafisk.
|
|
# Da finnes ikke lydsocketen ennå, og avspilling feiler. Det retter seg
|
|
# selv: player_cmd() slås opp per melding, så første utrop etter
|
|
# innlogging virker uten at noe må startes om.
|
|
healthcheck:
|
|
test: ["CMD", "python", "-c",
|
|
"import urllib.request;urllib.request.urlopen('http://127.0.0.1:8090/api/config',timeout=5)"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
start_period: 180s # første oppstart laster ned ~670 MB stemmer
|
|
retries: 3
|