audio: Spotify-style blob playback + same-origin gate (fix playback loop)
Root cause of the "next-loops-but-never-plays after 4-5 songs" bug: every
track URL was HMAC-signed once at page-render time with a 10-min TTL. A whole
queue shared that single deadline, so tracks further down expired mid-session
-> /audio/stream returned 403 -> audio 'error' -> auto-skip -> next track also
expired -> infinite loop. The 3-strike guard never fired because the eager
'play' event reset the counter before each 403 landed.
Removed the expiring-token system entirely and replaced it with two
non-expiring layers:
- Client fetch()es track bytes and plays from a blob: object URL (no
shareable URL, no "save audio as"); blobs revoked to avoid leaks; loadSeq
guards fast prev/next; pre-seed is metadata-only (no auto-download).
- Server gates /audio/stream to same-origin browser fetches (X-Audio-Player
header or Sec-Fetch-Site): blocks address-bar paste, hotlinks, curl.
Also: reset error counter on real 'playing' event (not eager 'play') so the
3-strike auto-skip-stop actually works; fix admin play-state detection to
compare logical currentTrack().url instead of the now-blob: audio.src; bump
audio-player.js cache-buster v5.
Co-Authored-By: Claude Opus 4.7 <noreply@…>