From c2efb048e2035c4363bf6d6acdc37b86f4e3545e Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Sat, 31 Aug 2024 09:09:12 -0700 Subject: [PATCH] more jukebox work --- backend/mediaprovider/mediaprovider.go | 9 ++- backend/mediaprovider/subsonic/jukebox.go | 4 +- backend/player/jukebox/jukeboxplayer.go | 85 ++++++++++++++++------- 3 files changed, 68 insertions(+), 30 deletions(-) diff --git a/backend/mediaprovider/mediaprovider.go b/backend/mediaprovider/mediaprovider.go index 256405e..9fb1951 100644 --- a/backend/mediaprovider/mediaprovider.go +++ b/backend/mediaprovider/mediaprovider.go @@ -298,11 +298,16 @@ type JukeboxProvider interface { JukeboxStop() error JukeboxSeek(idx, seconds int) error JukeboxClear() error - JukeboxSet(trackID string) error JukeboxAdd(trackID string) error JukeboxRemove(idx int) error - JukeboxSetVolume(vol int) error JukeboxGetStatus() (*JukeboxStatus, error) + + // Performs a Clear followed by an Add to set the queue + // to contain a single track + JukeboxSet(trackID string) error + + // Sets the volume of the jukebox player (0-100) + JukeboxSetVolume(vol int) error } type JukeboxStatus struct { diff --git a/backend/mediaprovider/subsonic/jukebox.go b/backend/mediaprovider/subsonic/jukebox.go index de95d93..d705ed4 100644 --- a/backend/mediaprovider/subsonic/jukebox.go +++ b/backend/mediaprovider/subsonic/jukebox.go @@ -1,6 +1,7 @@ package subsonic import ( + "fmt" "strconv" "github.com/dweymouth/supersonic/backend/mediaprovider" @@ -24,8 +25,9 @@ func (s *subsonicMediaProvider) JukeboxClear() error { } func (s *subsonicMediaProvider) JukeboxSetVolume(vol int) error { + v := float64(vol) / 100 _, err := s.client.JukeboxControl("setGain", - map[string]string{"gain": strconv.Itoa(vol)}) + map[string]string{"gain": fmt.Sprintf("%0.2f", v)}) return err } diff --git a/backend/player/jukebox/jukeboxplayer.go b/backend/player/jukebox/jukeboxplayer.go index a73d5f6..52e7cb6 100644 --- a/backend/player/jukebox/jukeboxplayer.go +++ b/backend/player/jukebox/jukeboxplayer.go @@ -1,6 +1,8 @@ package jukebox import ( + "time" + "github.com/dweymouth/supersonic/backend/mediaprovider" "github.com/dweymouth/supersonic/backend/player" ) @@ -14,15 +16,16 @@ const ( type JukeboxPlayer struct { provider mediaprovider.JukeboxProvider - state int // stopped, playing, paused - volume int - seeking bool - numTracks int + state int // stopped, playing, paused + volume int + seeking bool - curTrack int - curTrackDuration float64 - startTrackTime float64 - startedAtUnixSecs float64 + curTrack int + queueLength int + curTrackDuration float64 + startTrackTime float64 + startedAtUnixMilli int64 + nextTrackTimer *time.Timer } func (j *JukeboxPlayer) SetVolume(vol int) error { @@ -37,21 +40,14 @@ func (j *JukeboxPlayer) GetVolume() int { return j.volume } -func (j *JukeboxPlayer) PlayTrackAt(idx int) error { - if err := j.provider.JukeboxSeek(idx, 0); err != nil { - return err - } - j.curTrack = idx - return j.Continue() -} - func (j *JukeboxPlayer) Continue() error { if j.state == playing { return nil } - if err := j.provider.JukeboxStart(); err != nil { + if err := j.startAndUpdateTime(); err != nil { return err } + j.state = playing return nil } @@ -63,6 +59,7 @@ func (j *JukeboxPlayer) Pause() error { if err := j.provider.JukeboxStop(); err != nil { return err } + // TODO: calculate paused at time j.state = paused return nil } @@ -78,20 +75,37 @@ func (j *JukeboxPlayer) Stop() error { return nil } -func (j *JukeboxPlayer) SeekPrevious() error { - track := j.curTrack - if track > 0 { - track = j.curTrack - 1 +func (j *JukeboxPlayer) PlayTrack(track *mediaprovider.Track) error { + if err := j.provider.JukeboxSet(track.ID); err != nil { + return err } - return j.PlayTrackAt(track) + j.startTrackTime = 0 + if err := j.startAndUpdateTime(); err != nil { + return err + } + + j.curTrack = 0 + j.queueLength = 1 + j.curTrackDuration = float64(track.Duration) + + return nil } -func (j *JukeboxPlayer) SeekNext() error { - track := j.curTrack - if track >= j.numTracks { - return nil +func (j *JukeboxPlayer) SetNextTrack(track *mediaprovider.Track) error { + // we need to replace the last track in the queue, remove it first + if j.curTrack < j.queueLength-1 { + if err := j.provider.JukeboxRemove(j.curTrack + 1); err != nil { + return err + } + j.queueLength -= 1 } - return j.PlayTrackAt(track + 1) + // append the new track to the queue + if err := j.provider.JukeboxAdd(track.ID); err != nil { + return err + } + j.queueLength += 1 + return nil + } func (j *JukeboxPlayer) SeekSeconds(secs float64) error { @@ -119,3 +133,20 @@ func (j *JukeboxPlayer) GetStatus() player.Status { State: state, } } + +func (j *JukeboxPlayer) startAndUpdateTime() error { + beforeStart := time.Now() + if err := j.provider.JukeboxStart(); err != nil { + return err + } + afterStart := time.Now() + + // assume track started playing at (ie has been playing for) + // half the round-trip latency + j.startedAtUnixMilli = time.Now().Add(-afterStart.Sub(beforeStart)).UnixMilli() + return nil +} + +func (j *JukeboxPlayer) handleNextTrack() { + +}