Commit Graph

6 Commits

Author SHA1 Message Date
bvandeusen d8b955ff0c feat(web): radio queue auto-refreshes at 80% via ?exclude= param
Adds _radioSeedId state and a module-level $effect.root that triggers a
/api/radio?seed_track=…&exclude=… fetch when queue consumption reaches
80%, appending new tracks without resetting the radio seed. Clears seed
on any non-radio enqueue (playQueue/enqueueTrack/enqueueTracks). Five
new vitest cases cover the trigger threshold, append logic, in-flight
guard, and manual-enqueue reset path.
2026-04-29 08:50:24 -04:00
bvandeusen b6f8a0c390 feat(web): events dispatcher posts to /api/events on player transitions
useEventsDispatcher subscribes to player.current/state via \$effect:
on first transition to playing for a new track, POSTs play_started
and caches the id; on track-id change while a play is open, closes
the prior row as play_skipped; on natural completion (state
playing→paused with position >= duration > 0), POSTs play_ended;
on pagehide, navigator.sendBeacon fires a final play_skipped.
Stable per-tab client_id from sessionStorage.
2026-04-26 09:42:11 -04:00
bvandeusen 7dd77a4723 feat(web): add enqueueTrack, enqueueTracks, playRadio to player store
enqueueTrack/enqueueTracks append to the queue without disturbing
playback state (no auto-play on append, no state mutation when queue
was non-empty). playRadio fetches /api/radio?seed_track=<id> and
feeds the response into playQueue. Empty response leaves queue alone.
2026-04-25 15:22:03 -04:00
bvandeusen 4189ef9899 feat(web): add MediaSession glue for OS media controls
useMediaSession() wires navigator.mediaSession metadata + action
handlers (play/pause/previous/next/seekto) + positionState to the
player store via $effect. Exports nothing stateful — callers just
invoke it once at root-layout mount.
2026-04-24 21:03:01 -04:00
bvandeusen 1476d5081b feat(web): add shuffle, repeat, and audio-event reporting to player store
toggleShuffle (Fisher-Yates over queue[index+1..end] with injectable
RNG for test determinism), cycleRepeat (off → all → one → off),
reportStateFromAudio with full repeat-mode semantics on 'ended',
plus simple state mirroring for playing/paused/waiting/error. Extends
skipNext to wrap when repeat='all'.
2026-04-24 17:56:00 -04:00
bvandeusen 431f333e41 feat(web): add player rune store with basic actions
Core state (queue, index, state, position, duration, volume, shuffle,
repeat, error) plus actions: playQueue, togglePlay, linear skipNext,
skipPrev 3-second rule, seekTo, setVolume (localStorage-persisted),
registerAudioEl, reportTimeUpdate, reportDuration. Shuffle/repeat
logic and audio-event reporting land in follow-up commits.

Also install an in-memory Storage polyfill in vitest.setup.ts: Node 25
exposes a partial localStorage global that lacks getItem/setItem/clear
(requires --localstorage-file with a path), and jsdom defers to the
Node global when present, leaving tests unable to exercise storage.
2026-04-24 17:54:07 -04:00