diff --git a/backend/app.go b/backend/app.go index 53fa84e..3dd2475 100644 --- a/backend/app.go +++ b/backend/app.go @@ -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) diff --git a/backend/ipc/server.go b/backend/ipc/server.go index fa32ba8..26c50ac 100644 --- a/backend/ipc/server.go +++ b/backend/ipc/server.go @@ -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 { @@ -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)) @@ -77,7 +75,8 @@ 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) } @@ -85,31 +84,25 @@ func (s *serverImpl) createHandler() http.Handler { 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) diff --git a/backend/mpris.go b/backend/mpris.go index d0716c3..9d90da4 100644 --- a/backend/mpris.go +++ b/backend/mpris.go @@ -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 } @@ -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) { diff --git a/backend/playbackcommands.go b/backend/playbackcommands.go index d0396da..33cac58 100644 --- a/backend/playbackcommands.go +++ b/backend/playbackcommands.go @@ -28,113 +28,120 @@ const ( cmdLoadRadioStation // arg: *mediaprovider.RadioStation, arg2: InsertQueueMode ) -type PlaybackCommand struct { +type playbackCommand struct { Type playbackCommandType Arg any Arg2 any Arg3 any } -type CommandQueue struct { +type playbackCommandQueue struct { mutex sync.Mutex - queue []PlaybackCommand + queue []playbackCommand cmdAvailable *sync.Cond - nextChan chan (PlaybackCommand) + nextChan chan playbackCommand } -func NewCommandQueue() *CommandQueue { - c := &CommandQueue{} +func NewCommandQueue() *playbackCommandQueue { + c := &playbackCommandQueue{} + c.nextChan = make(chan playbackCommand) c.cmdAvailable = sync.NewCond(&c.mutex) go c.chanWriter() return c } -func (c *CommandQueue) C() <-chan PlaybackCommand { +func (c *playbackCommandQueue) C() <-chan playbackCommand { return c.nextChan } -func (c *CommandQueue) Stop() { +func (c *playbackCommandQueue) Stop() { c.filterCommandsAndAdd([]playbackCommandType{cmdContinue, cmdPause, cmdStop}, - PlaybackCommand{Type: cmdStop}) + playbackCommand{Type: cmdStop}) } -func (c *CommandQueue) Continue() { +func (c *playbackCommandQueue) Continue() { c.filterCommandsAndAdd([]playbackCommandType{cmdContinue, cmdPause, cmdStop}, - PlaybackCommand{Type: cmdContinue}) + playbackCommand{Type: cmdContinue}) } -func (c *CommandQueue) Pause() { +func (c *playbackCommandQueue) Pause() { c.filterCommandsAndAdd([]playbackCommandType{cmdContinue, cmdPause, cmdStop}, - PlaybackCommand{Type: cmdPause}) + playbackCommand{Type: cmdPause}) } -func (c *CommandQueue) StopAndClearPlayQueue() { +func (c *playbackCommandQueue) PlayTrackAt(idx int) { + c.filterCommandsAndAdd([]playbackCommandType{cmdContinue, cmdPause, cmdStop, cmdPlayTrackAt}, + playbackCommand{Type: cmdPlayTrackAt, Arg: idx}) +} + +func (c *playbackCommandQueue) StopAndClearPlayQueue() { c.filterCommandsAndAdd([]playbackCommandType{cmdContinue, cmdPause, cmdStop, cmdStopAndClearPlayQueue}, - PlaybackCommand{Type: cmdStopAndClearPlayQueue}) + playbackCommand{Type: cmdStopAndClearPlayQueue}) } -func (c *CommandQueue) SetVolume(vol int) { +func (c *playbackCommandQueue) SetVolume(vol int) { c.filterCommandsAndAdd([]playbackCommandType{cmdVolume}, - PlaybackCommand{Type: cmdVolume, Arg: vol}) + playbackCommand{Type: cmdVolume, Arg: vol}) } -func (c *CommandQueue) SetLoopMode(mode LoopMode) { +func (c *playbackCommandQueue) SetLoopMode(mode LoopMode) { c.filterCommandsAndAdd([]playbackCommandType{cmdLoopMode}, - PlaybackCommand{Type: cmdLoopMode, Arg: mode}) + playbackCommand{Type: cmdLoopMode, Arg: mode}) } -func (c *CommandQueue) SeekSeconds(s float64) { +func (c *playbackCommandQueue) SeekSeconds(s float64) { c.filterCommandsAndAdd([]playbackCommandType{cmdSeekSeconds}, - PlaybackCommand{Type: cmdSeekSeconds, Arg: s}) + playbackCommand{Type: cmdSeekSeconds, Arg: s}) } -func (c *CommandQueue) SeekNext() { +func (c *playbackCommandQueue) SeekNext() { c.seekBackOrFwd(1) } -func (c *CommandQueue) SeekBackOrPrevious() { +func (c *playbackCommandQueue) SeekBackOrPrevious() { c.seekBackOrFwd(-1) } -func (c *CommandQueue) UpdatePlayQueue(items []mediaprovider.MediaItem) { +func (c *playbackCommandQueue) UpdatePlayQueue(items []mediaprovider.MediaItem) { c.filterCommandsAndAdd([]playbackCommandType{cmdUpdatePlayQueue}, - PlaybackCommand{Type: cmdUpdatePlayQueue, Arg: items}) + playbackCommand{Type: cmdUpdatePlayQueue, Arg: items}) } -func (c *CommandQueue) RemoveItemsFromQueue(idxs []int) { +func (c *playbackCommandQueue) RemoveItemsFromQueue(idxs []int) { c.mutex.Lock() - defer c.mutex.Unlock() - c.queue = append(c.queue, PlaybackCommand{ + c.queue = append(c.queue, playbackCommand{ Type: cmdRemoveTracksFromQueue, Arg: idxs, }) + c.mutex.Unlock() + c.cmdAvailable.Signal() } -func (c *CommandQueue) LoadRadioStation(radio *mediaprovider.RadioStation, insertMode InsertQueueMode) { +func (c *playbackCommandQueue) LoadRadioStation(radio *mediaprovider.RadioStation, insertMode InsertQueueMode) { c.mutex.Lock() - defer c.mutex.Unlock() - c.queue = append(c.queue, PlaybackCommand{ + c.queue = append(c.queue, playbackCommand{ Type: cmdLoadRadioStation, Arg: radio, Arg2: insertMode, }) + c.mutex.Unlock() + c.cmdAvailable.Signal() } -func (c *CommandQueue) LoadItems(items []mediaprovider.MediaItem, insertQueueMode InsertQueueMode, shuffle bool) { +func (c *playbackCommandQueue) LoadItems(items []mediaprovider.MediaItem, insertQueueMode InsertQueueMode, shuffle bool) { c.mutex.Lock() - defer c.mutex.Unlock() - c.queue = append(c.queue, PlaybackCommand{ + c.queue = append(c.queue, playbackCommand{ Type: cmdLoadItems, Arg: items, Arg2: insertQueueMode, Arg3: shuffle, }) + c.mutex.Unlock() + c.cmdAvailable.Signal() } -func (c *CommandQueue) filterCommandsAndAdd(excludeTypes []playbackCommandType, command PlaybackCommand) { +func (c *playbackCommandQueue) filterCommandsAndAdd(excludeTypes []playbackCommandType, command playbackCommand) { c.mutex.Lock() - defer c.mutex.Unlock() - j := 0 for _, cmd := range c.queue { if slices.Contains(excludeTypes, cmd.Type) { @@ -145,12 +152,12 @@ func (c *CommandQueue) filterCommandsAndAdd(excludeTypes []playbackCommandType, } c.queue = c.queue[:j] c.queue = append(c.queue, command) + c.mutex.Unlock() + c.cmdAvailable.Signal() } -func (c *CommandQueue) seekBackOrFwd(direction int) { +func (c *playbackCommandQueue) seekBackOrFwd(direction int) { c.mutex.Lock() - defer c.mutex.Unlock() - j := 0 n := 0 for _, cmd := range c.queue { @@ -162,12 +169,14 @@ func (c *CommandQueue) seekBackOrFwd(direction int) { } } c.queue = c.queue[:j] - c.queue = append(c.queue, PlaybackCommand{ + c.queue = append(c.queue, playbackCommand{ Type: cmdSeekFwdBackN, Arg: n + direction}) + c.mutex.Unlock() + c.cmdAvailable.Signal() } -func (c *CommandQueue) chanWriter() { +func (c *playbackCommandQueue) chanWriter() { for { c.mutex.Lock() for len(c.queue) == 0 { diff --git a/backend/playbackmanager.go b/backend/playbackmanager.go index e6edd7b..b95e357 100644 --- a/backend/playbackmanager.go +++ b/backend/playbackmanager.go @@ -2,7 +2,6 @@ package backend import ( "context" - "errors" "log" "math/rand" @@ -13,7 +12,8 @@ import ( // A high-level MediaProvider-aware playback engine, serves as an // intermediary between the frontend and various Player backends. type PlaybackManager struct { - engine *playbackEngine + engine *playbackEngine + cmdQueue *playbackCommandQueue } func NewPlaybackManager( @@ -23,9 +23,14 @@ func NewPlaybackManager( scrobbleCfg *ScrobbleConfig, transcodeCfg *TranscodingConfig, ) *PlaybackManager { - return &PlaybackManager{ - engine: NewPlaybackEngine(ctx, s, p, scrobbleCfg, transcodeCfg), + e := NewPlaybackEngine(ctx, s, p, scrobbleCfg, transcodeCfg) + q := NewCommandQueue() + pm := &PlaybackManager{ + engine: e, + cmdQueue: q, } + go pm.runCmdQueue(ctx) + return pm } func (p *PlaybackManager) CurrentPlayer() player.BasePlayer { @@ -106,7 +111,8 @@ func (p *PlaybackManager) LoadAlbum(albumID string, insertQueueMode InsertQueueM if err != nil { return err } - return p.LoadTracks(album.Tracks, insertQueueMode, shuffle) + p.LoadTracks(album.Tracks, insertQueueMode, shuffle) + return nil } // Loads the specified playlist into the play queue. @@ -115,26 +121,28 @@ func (p *PlaybackManager) LoadPlaylist(playlistID string, insertQueueMode Insert if err != nil { return err } - return p.LoadTracks(playlist.Tracks, insertQueueMode, shuffle) + p.LoadTracks(playlist.Tracks, insertQueueMode, shuffle) + return nil } // Load tracks into the play queue. // If replacing the current queue (!appendToQueue), playback will be stopped. -func (p *PlaybackManager) LoadTracks(tracks []*mediaprovider.Track, insertQueueMode InsertQueueMode, shuffle bool) error { - return p.engine.LoadTracks(tracks, insertQueueMode, shuffle) +func (p *PlaybackManager) LoadTracks(tracks []*mediaprovider.Track, insertQueueMode InsertQueueMode, shuffle bool) { + items := copyTrackSliceToMediaItemSlice(tracks) + p.cmdQueue.LoadItems(items, insertQueueMode, shuffle) } // Load items into the play queue. // If replacing the current queue (!appendToQueue), playback will be stopped. -func (p *PlaybackManager) LoadItems(items []mediaprovider.MediaItem, insertQueueMode InsertQueueMode, shuffle bool) error { - return p.engine.LoadItems(items, insertQueueMode, shuffle) +func (p *PlaybackManager) LoadItems(items []mediaprovider.MediaItem, insertQueueMode InsertQueueMode, shuffle bool) { + p.cmdQueue.LoadItems(items, insertQueueMode, shuffle) } // Replaces the play queue with the given set of tracks. // Does not stop playback if the currently playing track is in the new queue, // but updates the now playing index to point to the first instance of the track in the new queue. -func (p *PlaybackManager) UpdatePlayQueue(items []mediaprovider.MediaItem) error { - return p.engine.UpdatePlayQueue(items) +func (p *PlaybackManager) UpdatePlayQueue(items []mediaprovider.MediaItem) { + p.cmdQueue.UpdatePlayQueue(items) } func (p *PlaybackManager) PlayAlbum(albumID string, firstTrack int, shuffle bool) error { @@ -144,7 +152,8 @@ func (p *PlaybackManager) PlayAlbum(albumID string, firstTrack int, shuffle bool if p.engine.replayGainCfg.Mode == ReplayGainAuto { p.SetReplayGainMode(player.ReplayGainAlbum) } - return p.PlayTrackAt(firstTrack) + p.PlayTrackAt(firstTrack) + return nil } func (p *PlaybackManager) PlayPlaylist(playlistID string, firstTrack int, shuffle bool) error { @@ -154,7 +163,8 @@ func (p *PlaybackManager) PlayPlaylist(playlistID string, firstTrack int, shuffl if p.engine.replayGainCfg.Mode == ReplayGainAuto { p.SetReplayGainMode(player.ReplayGainTrack) } - return p.PlayTrackAt(firstTrack) + p.PlayTrackAt(firstTrack) + return nil } func (p *PlaybackManager) PlayTrack(trackID string) error { @@ -166,13 +176,14 @@ func (p *PlaybackManager) PlayTrack(trackID string) error { if p.engine.replayGainCfg.Mode == ReplayGainAuto { p.SetReplayGainMode(player.ReplayGainTrack) } - return p.PlayFromBeginning() + p.PlayFromBeginning() + return nil } func (p *PlaybackManager) ShuffleArtistAlbums(artistID string) { artist, err := p.engine.sm.Server.GetArtist(artistID) if err != nil { - log.Printf(err.Error()) + log.Printf("failed to get artist: %v\n", err) return } if len(artist.Albums) == 0 { @@ -196,7 +207,7 @@ func (p *PlaybackManager) ShuffleArtistAlbums(artistID string) { func (p *PlaybackManager) PlayArtistDiscography(artistID string, shuffleTracks bool) { tr, err := p.engine.sm.Server.GetArtistTracks(artistID) if err != nil { - log.Printf(err.Error()) + log.Printf("failed to get artist tracks: %v\n", err) return } p.LoadTracks(tr, Replace, shuffleTracks) @@ -210,12 +221,12 @@ func (p *PlaybackManager) PlayArtistDiscography(artistID string, shuffleTracks b p.PlayFromBeginning() } -func (p *PlaybackManager) PlayFromBeginning() error { - return p.engine.PlayTrackAt(0) +func (p *PlaybackManager) PlayFromBeginning() { + p.cmdQueue.PlayTrackAt(0) } -func (p *PlaybackManager) PlayTrackAt(idx int) error { - return p.engine.PlayTrackAt(idx) +func (p *PlaybackManager) PlayTrackAt(idx int) { + p.cmdQueue.PlayTrackAt(idx) } func (p *PlaybackManager) PlayRandomSongs(genreName string) { @@ -231,12 +242,12 @@ func (p *PlaybackManager) PlaySimilarSongs(id string) { } func (p *PlaybackManager) LoadRadioStation(station *mediaprovider.RadioStation, queueMode InsertQueueMode) { - p.engine.LoadRadioStation(station, queueMode) + p.cmdQueue.LoadRadioStation(station, queueMode) } -func (p *PlaybackManager) PlayRadioStation(station *mediaprovider.RadioStation) error { +func (p *PlaybackManager) PlayRadioStation(station *mediaprovider.RadioStation) { p.LoadRadioStation(station, Replace) - return p.PlayFromBeginning() + p.PlayFromBeginning() } func (p *PlaybackManager) fetchAndPlayTracks(fetchFn func() ([]*mediaprovider.Track, error)) { @@ -268,12 +279,12 @@ func (p *PlaybackManager) OnTrackRatingChanged(id string, rating int) { } func (p *PlaybackManager) RemoveTracksFromQueue(idxs []int) { - p.engine.RemoveTracksFromQueue(idxs) + p.cmdQueue.RemoveItemsFromQueue(idxs) } // Stop playback and clear the play queue. func (p *PlaybackManager) StopAndClearPlayQueue() { - p.engine.StopAndClearPlayQueue() + p.cmdQueue.StopAndClearPlayQueue() } func (p *PlaybackManager) SetReplayGainOptions(config ReplayGainConfig) { @@ -289,17 +300,16 @@ func (p *PlaybackManager) SetReplayGainMode(mode player.ReplayGainMode) { func (p *PlaybackManager) SetNextLoopMode() { switch p.engine.loopMode { case LoopNone: - p.engine.SetLoopMode(LoopAll) + p.cmdQueue.SetLoopMode(LoopAll) case LoopAll: - p.engine.SetLoopMode(LoopOne) + p.cmdQueue.SetLoopMode(LoopOne) case LoopOne: - p.engine.SetLoopMode(LoopNone) - + p.cmdQueue.SetLoopMode(LoopNone) } } func (p *PlaybackManager) SetLoopMode(loopMode LoopMode) { - p.engine.SetLoopMode(loopMode) + p.cmdQueue.SetLoopMode(loopMode) } func (p *PlaybackManager) GetLoopMode() LoopMode { @@ -310,29 +320,29 @@ func (p *PlaybackManager) PlayerStatus() player.Status { return p.engine.PlayerStatus() } -func (p *PlaybackManager) SetVolume(vol int) error { - return p.engine.SetVolume(vol) +func (p *PlaybackManager) SetVolume(vol int) { + p.cmdQueue.SetVolume(vol) } func (p *PlaybackManager) Volume() int { return p.engine.CurrentPlayer().GetVolume() } -func (p *PlaybackManager) SeekNext() error { - return p.engine.SeekNext() +func (p *PlaybackManager) SeekNext() { + p.cmdQueue.SeekNext() } -func (p *PlaybackManager) SeekBackOrPrevious() error { - return p.engine.SeekBackOrPrevious() +func (p *PlaybackManager) SeekBackOrPrevious() { + p.cmdQueue.SeekBackOrPrevious() } // Seek to given absolute position in the current track by seconds. -func (p *PlaybackManager) SeekSeconds(sec float64) error { - return p.engine.SeekSeconds(sec) +func (p *PlaybackManager) SeekSeconds(sec float64) { + p.cmdQueue.SeekSeconds(sec) } // Seek by given relative position in the current track by seconds. -func (p *PlaybackManager) SeekBySeconds(sec float64) error { +func (p *PlaybackManager) SeekBySeconds(sec float64) { status := p.engine.PlayerStatus() target := status.TimePos + sec if target < 0 { @@ -340,40 +350,79 @@ func (p *PlaybackManager) SeekBySeconds(sec float64) error { } else if target > status.Duration { target = status.Duration } - return p.engine.SeekSeconds(target) + p.cmdQueue.SeekSeconds(target) } // Seek to a fractional position in the current track [0..1] -func (p *PlaybackManager) SeekFraction(fraction float64) error { +func (p *PlaybackManager) SeekFraction(fraction float64) { if fraction < 0 { fraction = 0 } else if fraction > 1 { fraction = 1 } target := p.engine.curTrackDuration * fraction - return p.engine.SeekSeconds(target) + p.cmdQueue.SeekSeconds(target) } -func (p *PlaybackManager) Stop() error { - return p.engine.Stop() +func (p *PlaybackManager) Stop() { + p.cmdQueue.Stop() } -func (p *PlaybackManager) Pause() error { - return p.engine.Pause() +func (p *PlaybackManager) Pause() { + p.cmdQueue.Pause() } -func (p *PlaybackManager) Continue() error { - return p.engine.Continue() +func (p *PlaybackManager) Continue() { + p.cmdQueue.Continue() } -func (p *PlaybackManager) PlayPause() error { +func (p *PlaybackManager) PlayPause() { switch p.engine.PlayerStatus().State { case player.Playing: - return p.engine.Pause() + p.Pause() case player.Paused: - return p.engine.Continue() + p.Continue() case player.Stopped: - return p.engine.PlayTrackAt(0) + p.PlayTrackAt(0) + } +} + +func (p *PlaybackManager) runCmdQueue(ctx context.Context) { + for { + select { + case <-ctx.Done(): + return + case c := <-p.cmdQueue.C(): + switch c.Type { + case cmdStop: + p.engine.Stop() + case cmdContinue: + p.engine.Continue() + case cmdPause: + p.engine.Pause() + case cmdPlayTrackAt: + p.engine.PlayTrackAt(c.Arg.(int)) + case cmdSeekSeconds: + p.engine.SeekSeconds(c.Arg.(float64)) + case cmdSeekFwdBackN: + log.Println("TODO") + case cmdVolume: + p.engine.SetVolume(c.Arg.(int)) + case cmdLoopMode: + p.engine.SetLoopMode(c.Arg.(LoopMode)) + case cmdStopAndClearPlayQueue: + p.engine.StopAndClearPlayQueue() + case cmdUpdatePlayQueue: + p.engine.UpdatePlayQueue(c.Arg.([]mediaprovider.MediaItem)) + case cmdRemoveTracksFromQueue: + p.engine.RemoveTracksFromQueue(c.Arg.([]int)) + case cmdLoadItems: + p.engine.LoadItems( + c.Arg.([]mediaprovider.MediaItem), + c.Arg2.(InsertQueueMode), + c.Arg3.(bool), + ) + } + } } - return errors.New("unreached - invalid player state") } diff --git a/ui/bottompanel.go b/ui/bottompanel.go index 60957ba..29d32e8 100644 --- a/ui/bottompanel.go +++ b/ui/bottompanel.go @@ -106,7 +106,7 @@ func NewBottomPanel(pm *backend.PlaybackManager, im *backend.ImageManager, contr pm.OnLoopModeChange(bp.AuxControls.SetLoopMode) pm.OnVolumeChange(bp.AuxControls.VolumeControl.SetVolume) bp.AuxControls.VolumeControl.OnSetVolume = func(v int) { - _ = pm.SetVolume(v) + pm.SetVolume(v) } bp.AuxControls.OnChangeLoopMode(func() { pm.SetNextLoopMode() diff --git a/ui/controller/connectactions.go b/ui/controller/connectactions.go index 8692447..d9006c8 100644 --- a/ui/controller/connectactions.go +++ b/ui/controller/connectactions.go @@ -158,7 +158,7 @@ func (c *Controller) ConnectPlayQueuelistActions(list *widgets.PlayQueueList) { } list.OnAddToPlaylist = c.DoAddTracksToPlaylistWorkflow list.OnPlayItemAt = func(tracknum int) { - _ = c.App.PlaybackManager.PlayTrackAt(tracknum) + c.App.PlaybackManager.PlayTrackAt(tracknum) } list.OnShowArtistPage = func(artistID string) { c.NavigateTo(ArtistRoute(artistID)) diff --git a/ui/mainwindow.go b/ui/mainwindow.go index bb33ada..cf00f95 100644 --- a/ui/mainwindow.go +++ b/ui/mainwindow.go @@ -241,13 +241,13 @@ func (m *MainWindow) SetupSystemTrayMenu(appName string, fyneApp fyne.App) { if desk, ok := fyneApp.(desktop.App); ok { menu := fyne.NewMenu(appName, fyne.NewMenuItem(fmt.Sprintf("%s/%s", lang.L("Play"), lang.L("Pause")), func() { - _ = m.App.PlaybackManager.PlayPause() + m.App.PlaybackManager.PlayPause() }), fyne.NewMenuItem(lang.L("Previous"), func() { - _ = m.App.PlaybackManager.SeekBackOrPrevious() + m.App.PlaybackManager.SeekBackOrPrevious() }), fyne.NewMenuItem(lang.L("Next"), func() { - _ = m.App.PlaybackManager.SeekNext() + m.App.PlaybackManager.SeekNext() }), fyne.NewMenuItemSeparator(), fyne.NewMenuItem(lang.L("Volume")+" +10%", func() {