The player now owns the effective mode (config or override) and passes it
down: spotify_apply(enable) and hls_set_url(url) instead of both reading
source.mode from the config. /api/player/source forces spotify|hls|off
(config returns to the saved mode), /api/player/url plays an m3u8 now;
neither is saved. GET reports it in `forced`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
play/pause/toggle/stop/next/prev. Spotify commands run on the session task
(same thread as cspot's frame handling) and the POST waits until they ran,
so it answers the new state. HLS pause stops fetching and resume rejoins at
the live edge; next/prev on HLS and anything on tone/off return 409.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No Spotify session (or paused, with failover_on_pause) for failover_delay_s
switches to HLS; Spotify playing switches back within ~1 s. Switches fade
out/in over 30 ms and flush the ring. HLS is suspended while Spotify plays.
cspot is held back instead of drained while it isn't Spotify's turn: it keeps
decoding on pause, so dropping its frames let it race through the queue.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An upload during an active Spotify session crawled (~10 kB/s, 197 s)
and ended in a reset (seen twice). Workaround:
- aes67_ota_on_update(cb): cb(true) right before an accepted upload
writes flash, cb(false) if it then fails.
- The player pauses Spotify (spotify_pause -> SpircHandler::setPause, the
app follows) and suspends HLS fetching (hls_suspend); resumed if the
upload fails. AES67 TX keeps running (silence).
- Verified: upload during Spotify playback paused the session and
installed in 25.8 s (normal for 2 MB), clean reboot and self-test.
Root cause still open (noted in CLAUDE.md with the other cspot items).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- main/hls: task active while source.mode is hls/auto and hls_url is
set. esp_http_client over HTTPS (IDF certificate bundle), redirects
followed (max 5), streamed reads in 4 KB chunks to a sink callback.
- Master playlist: highest BANDWIDTH variant; URL resolution for
absolute, host-relative and path-relative references; CRLF tolerant.
- Media playlist: TARGETDURATION, MEDIA-SEQUENCE, segments; start 3
segments behind the live edge, fetch each new one in order, skip ahead
if the window moved past us, reload after target/2 when nothing is new.
- Verified with Triple J Hottest (ABC, Akamai): 252 kbit/s AAC-LC
variant chosen, 3 back-fill segments then one new ~10 s segment at a
time, ~303 KB in ~1.25 s each; heap 439 KB, PSRAM 31.9 MB free.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>