Skip to content

Commit

Permalink
Merge pull request #481 from dweymouth/playback-cmd-queue
Browse files Browse the repository at this point in the history
Prep for Jukebox mode - add queue to accumulate playback commands
  • Loading branch information
dweymouth authored Sep 12, 2024
2 parents 72de615 + fd2d831 commit fe39239
Show file tree
Hide file tree
Showing 14 changed files with 588 additions and 232 deletions.
4 changes: 1 addition & 3 deletions backend/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,9 +377,7 @@ func (a *App) LoadSavedPlayQueue() error {
return nil
}

if err := a.PlaybackManager.LoadTracks(queue.Tracks, Replace, false); err != nil {
return err
}
a.PlaybackManager.LoadTracks(queue.Tracks, Replace, false)
if queue.TrackIndex >= 0 && queue.TrackIndex < len(queue.Tracks) {
// TODO: This isn't ideal but doesn't seem to cause an audible play-for-a-split-second artifact
a.PlaybackManager.PlayTrackAt(queue.TrackIndex)
Expand Down
47 changes: 20 additions & 27 deletions backend/ipc/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ import (
)

type PlaybackHandler interface {
PlayPause() error
Stop() error
Pause() error
Continue() error
SeekBackOrPrevious() error
SeekNext() error
SeekSeconds(float64) error
SeekBySeconds(float64) error
PlayPause()
Stop()
Pause()
Continue()
SeekBackOrPrevious()
SeekNext()
SeekSeconds(float64)
SeekBySeconds(float64)
Volume() int
SetVolume(int) error
SetVolume(int)
}

type IPCServer interface {
Expand Down Expand Up @@ -57,14 +57,12 @@ func (s *serverImpl) createHandler() http.Handler {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("The given path is not valid"))
})
m.HandleFunc(PingPath, s.makeSimpleEndpointHandler(func() error { return nil }))
m.HandleFunc(ShowPath, s.makeSimpleEndpointHandler(func() error {
m.HandleFunc(PingPath, s.makeSimpleEndpointHandler(func() {}))
m.HandleFunc(ShowPath, s.makeSimpleEndpointHandler(func() {
s.showFn()
return nil
}))
m.HandleFunc(QuitPath, s.makeSimpleEndpointHandler(func() error {
m.HandleFunc(QuitPath, s.makeSimpleEndpointHandler(func() {
s.quitFn()
return nil
}))
m.HandleFunc(PlayPath, s.makeSimpleEndpointHandler(s.pbHandler.Continue))
m.HandleFunc(PausePath, s.makeSimpleEndpointHandler(s.pbHandler.Pause))
Expand All @@ -77,39 +75,34 @@ func (s *serverImpl) createHandler() http.Handler {
m.HandleFunc(VolumePath, func(w http.ResponseWriter, r *http.Request) {
v := r.URL.Query().Get("v")
if vol, err := strconv.Atoi(v); err == nil {
s.writeSimpleResponse(w, s.pbHandler.SetVolume(vol))
s.pbHandler.SetVolume(vol)
s.writeOK(w)
} else {
s.writeErr(w, err)
}
})
return m
}

func (s *serverImpl) makeSimpleEndpointHandler(f func() error) func(http.ResponseWriter, *http.Request) {
func (s *serverImpl) makeSimpleEndpointHandler(f func()) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
s.writeSimpleResponse(w, f())
f()
s.writeOK(w)
}
}

func (s *serverImpl) makeFloatEndpointHandler(f func(float64) error, queryParam string) func(http.ResponseWriter, *http.Request) {
func (s *serverImpl) makeFloatEndpointHandler(f func(float64), queryParam string) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
v := r.URL.Query().Get(queryParam)
if val, err := strconv.ParseFloat(v, 64); err == nil {
s.writeSimpleResponse(w, f(val))
f(val)
s.writeOK(w)
} else {
s.writeErr(w, err)
}
}
}

func (s *serverImpl) writeSimpleResponse(w http.ResponseWriter, err error) {
if err == nil {
s.writeOK(w)
} else {
s.writeErr(w, err)
}
}

func (s *serverImpl) writeOK(w http.ResponseWriter) (int, error) {
var r Response
b, err := json.Marshal(&r)
Expand Down
9 changes: 7 additions & 2 deletions backend/mediaprovider/mediaprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 3 additions & 1 deletion backend/mediaprovider/subsonic/jukebox.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package subsonic

import (
"fmt"
"strconv"

"github.com/dweymouth/supersonic/backend/mediaprovider"
Expand All @@ -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
}

Expand Down
26 changes: 16 additions & 10 deletions backend/mpris.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,46 +150,51 @@ func (m *MPRISHandler) SupportedMimeTypes() ([]string, error) {
// OrgMprisMediaPlayer2PlayerAdapter implementation

func (m *MPRISHandler) Next() error {
return m.pm.SeekNext()
m.pm.SeekNext()
return nil
}

func (m *MPRISHandler) Previous() error {
return m.pm.SeekBackOrPrevious()
m.pm.SeekBackOrPrevious()
return nil
}

func (m *MPRISHandler) Pause() error {
if m.pm.PlayerStatus().State == player.Playing {
return m.pm.PlayPause()
m.pm.PlayPause()
}
return nil
}

func (m *MPRISHandler) PlayPause() error {
return m.pm.PlayPause()
m.pm.PlayPause()
return nil
}

func (m *MPRISHandler) Stop() error {
return m.pm.Stop()
m.pm.Stop()
return nil
}

func (m *MPRISHandler) Play() error {
switch m.pm.PlayerStatus().State {
case player.Paused:
return m.pm.PlayPause()
m.pm.PlayPause()
case player.Stopped:
return m.pm.PlayFromBeginning()
m.pm.PlayFromBeginning()
}
return nil
}

func (m *MPRISHandler) Seek(offset types.Microseconds) error {
// MPRIS seek command is relative to current position
return m.pm.SeekBySeconds(microsecondsToSeconds(offset))
m.pm.SeekBySeconds(microsecondsToSeconds(offset))
return nil
}

func (m *MPRISHandler) SetPosition(trackId string, position types.Microseconds) error {
if m.curTrackPath == trackId {
return m.pm.SeekSeconds(microsecondsToSeconds(position))
m.pm.SeekSeconds(microsecondsToSeconds(position))
}
return nil
}
Expand Down Expand Up @@ -297,7 +302,8 @@ func (m *MPRISHandler) Volume() (float64, error) {
}

func (m *MPRISHandler) SetVolume(v float64) error {
return m.pm.SetVolume(int(v * 100))
m.pm.SetVolume(int(v * 100))
return nil
}

func (m *MPRISHandler) Position() (int64, error) {
Expand Down
Loading

0 comments on commit fe39239

Please sign in to comment.