Wire the web player into system media controls (MPRIS, Control Center,
lock screen) and keyboard media keys: push track metadata and handle
play/pause/next, with seek neutralized on the live stream.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
For yt-dlp tracks the locator is the original web page, so pass it as a
url annotation, carry it through the stream metadata and history, and
turn the track title into a link back to that page (both live and in the
history). Subsonic ids are opaque and stay plain text.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Default it to "Nemu FM" and derive the tab title, logo, and dynamic
document title from one STATION_NAME constant for easy renaming.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
If the stream drops (server restart, network loss), the player now retries
goLive() on error/ended/stalled with a delay that doubles each failure, capped
at 30s, and resets once audio flows again.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
/download now accepts ?file=<name> to fetch any cached file, not just the
current track. History entries carry that filename token (via /history), so
the web UI renders each aired track as a download link. A shared
serve_attachment helper validates the request (basename-only, real audio file,
no hidden/.part files) before streaming it; LRU-evicted tracks return 404.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a read-only field with the bare /radio.mp3 URL and a copy button
(clipboard API, with a select+execCommand fallback), so the stream can
easily be opened in VLC or another external player.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Firefox kept resuming the <audio> element from a stale buffer after a
pause, drifting behind the live point. Load the stream with an anti-cache
query parameter, and on resume-from-pause reconnect to live rather than
replaying the buffered audio.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Serve a small web page at http://<host>:8000/ (stream/index.html) from the
Liquidsoap harbor, alongside the /radio.mp3 stream. It shows the track
currently on air and refreshes it from a /nowplaying JSON endpoint, fed by the
broadcast source's live metadata — accurate even though the ingest daemon runs
a track ahead (prefetch).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>