stream: compute now-playing duration/position off the HTTP thread
All checks were successful
continuous-integration/drone/push Build is passing

The /nowplaying handler called source.duration/source.elapsed on the live
crossfaded source. Since it is polled every second by the web player, those
cross-thread source queries contended with the crossfade's own clock during a
transition and wedged the streaming clock — a few seconds of audio then a
frozen buffer looping on air, only ever with a listener connected.

Capture duration and the track start time in a synchronous on_metadata
callback (which runs in the clock thread, where touching the source is safe)
and have /nowplaying read those refs instead — position is just the wall-clock
time since the track started. No handler touches the source outside its clock
anymore, so the 3 s crossfade is safe again.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
nemunaire 2026-07-05 17:06:55 +08:00
commit b5f83ef6c8
2 changed files with 22 additions and 2 deletions

View file

@ -217,6 +217,23 @@ radio.on_metadata(
end
)
# Durée et instant de départ du morceau courant, pour la règle de scrobble du
# player (« démarré au début et écouté à ~90 % »). CAPTURÉS ICI, DANS LE THREAD
# D'HORLOGE via un on_metadata SYNCHRONE : interroger source.duration/
# source.elapsed depuis un handler HTTP (thread harbor) entrait en contention
# avec l'horloge du crossfade pendant une transition et figeait l'antenne. On ne
# touche donc plus la source hors de son horloge : /nowplaying (web.liq) se
# contente de lire ces refs — position = temps écoulé depuis cur_started.
cur_duration = ref(0.)
cur_started = ref(time())
radio.on_metadata(
synchronous=true,
fun(_) -> begin
cur_duration := source.duration(radio)
cur_started := time()
end
)
# --- Sortie : flux MP3 sur http://<hote>:8000/radio.mp3 ---
output.harbor(
%mp3(bitrate=192),