diff --git a/backend/player/player.go b/backend/player/player.go index 23d120b..0f9936d 100644 --- a/backend/player/player.go +++ b/backend/player/player.go @@ -106,7 +106,7 @@ func (p *Player) registerEventHandlers() { p.logger.Info("Received RequestPlayEvent") if err := p.Play(); err != nil { - p.logger.Error("failed to play", "err", err) + p.logger.Warn("Play requested but not ready", "err", err) } }) runtime.EventsOn(p.ctx, events.RequestPause, func(_ ...any) { diff --git a/backend/queue/queue.go b/backend/queue/queue.go index 28edfe7..eb28d27 100644 --- a/backend/queue/queue.go +++ b/backend/queue/queue.go @@ -133,6 +133,11 @@ func (q *Queue) registerEventHandlers() { return } + runtime.EventsOn(q.ctx, events.RequestPlay, func(_ ...any) { + q.logger.Info("Received RequestPlay") + q.PlayFromStart() + }) + runtime.EventsOn(q.ctx, events.RequestNext, func(_ ...any) { q.logger.Info("Received RequestNext") q.Next() @@ -703,6 +708,33 @@ func (q *Queue) Previous() { q.emitQueueChanged() } +// PlayFromStart restarts playback from the beginning of the queue. +// If shuffle is enabled, a new shuffle order is generated and playback +// starts from a random track. This is a no-op when a track is already +// active (currentIndex != -1) or the queue is empty. +func (q *Queue) PlayFromStart() { + q.mu.Lock() + defer q.mu.Unlock() + + if q.currentIndex != -1 { + return + } + + if len(q.tracks) == 0 { + return + } + + if q.shuffleMode { + q.generateShuffleOrder() + q.currentIndex = q.shuffleOrder[0] + } else { + q.currentIndex = 0 + } + + q.playCurrentTrack() + q.emitQueueChanged() +} + // PlayIndex jumps to and plays the track at the given index. func (q *Queue) PlayIndex(index int) { q.mu.Lock() diff --git a/frontend/index.css b/frontend/index.css index 55e11e1..479f2ef 100644 --- a/frontend/index.css +++ b/frontend/index.css @@ -53,7 +53,7 @@ body div.sidebar { padding: 0.25em; background-color: #343a40; display: grid; - grid-template-columns: 1fr auto 1fr; + grid-template-columns: auto 1fr auto; align-items: center; #now-playing-info { @@ -87,8 +87,12 @@ body div.sidebar { } } + now-playing { + max-width: 250px; + overflow: hidden; + } + audio-player { - justify-self: center; margin: 0.5em 1em; }