From b667e2cd07b2773e06b056167aef71a172cae8cf Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Sat, 11 Feb 2023 13:07:02 -0800 Subject: [PATCH] now playing page can remove tracks from play queue --- backend/playbackmanager.go | 34 ++++++++++++++++++++++++++++++++-- player/player.go | 8 ++++++++ ui/browsing/nowplayingpage.go | 9 +++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/backend/playbackmanager.go b/backend/playbackmanager.go index 334f869..5ee3168 100644 --- a/backend/playbackmanager.go +++ b/backend/playbackmanager.go @@ -64,6 +64,7 @@ func NewPlaybackManager(ctx context.Context, s *ServerManager, p *player.Player) pm.checkScrobble(pm.playTimeStopwatch.Elapsed()) pm.playTimeStopwatch.Reset() pm.stopPollTimePos() + pm.doUpdateTimePos() for _, cb := range pm.onSongChange { cb(nil) } @@ -171,14 +172,43 @@ func (p *PlaybackManager) GetPlayQueue() []*subsonic.Child { return pq } +// trackIdxs must be sorted func (p *PlaybackManager) RemoveTracksFromQueue(trackIdxs []int) { - // TODO + newQueue := make([]*subsonic.Child, 0, len(p.playQueue)-len(trackIdxs)) + rmCount := 0 + rmIdx := 0 + for i, tr := range p.playQueue { + if rmIdx < len(trackIdxs) && trackIdxs[rmIdx] == i { + // removing this track + rmIdx++ + if err := p.player.RemoveTrackAt(i - rmCount); err == nil { + rmCount++ + } else { + log.Printf("error removing track: %v", err.Error()) + // did not remove this track + newQueue = append(newQueue, tr) + } + } else { + // not removing this track + newQueue = append(newQueue, tr) + } + } + p.playQueue = newQueue + p.nowPlayingIdx = p.player.GetStatus().PlaylistPos + // fire on song change callbacks in case the playing track was removed + // TODO: only call this if the playing track actually was removed + for _, cb := range p.onSongChange { + cb(p.NowPlaying()) + } } func (p *PlaybackManager) checkScrobble(playDur time.Duration) { - if playDur.Seconds() < 0.1 || p.curTrackTime < 0.1 { + if len(p.playQueue) == 0 || p.nowPlayingIdx < 0 { return } + if playDur.Seconds() < 0.1 || p.curTrackTime < 0.1 { + return // ignore spurious onTrackChange callbacks + } song := p.playQueue[p.nowPlayingIdx] if playDur.Seconds()/p.curTrackTime > ScrobbleThreshold { log.Printf("Scrobbling %q", song.Title) diff --git a/player/player.go b/player/player.go index 26e5108..9574458 100644 --- a/player/player.go +++ b/player/player.go @@ -131,6 +131,14 @@ func (p *Player) PlayFile(url string) error { return err } +// Removes the item at the given index from the internal playqueue. +func (p *Player) RemoveTrackAt(idx int) error { + if p.mpv == nil { + return ErrUnitialized + } + return p.mpv.Command([]string{"playlist-remove", strconv.Itoa(idx)}) +} + // Stops playback and clears the play queue. func (p *Player) Stop() error { if p.mpv == nil { diff --git a/ui/browsing/nowplayingpage.go b/ui/browsing/nowplayingpage.go index 70a0fab..55f9df1 100644 --- a/ui/browsing/nowplayingpage.go +++ b/ui/browsing/nowplayingpage.go @@ -43,6 +43,9 @@ func NewNowPlayingPage( a.tracklist.DisablePlaybackMenu = true a.tracklist.OnPlayTrackAt = a.onPlayTrackAt a.tracklist.OnAddToPlaylist = a.contr.DoAddTracksToPlaylistWorkflow + a.tracklist.AuxiliaryMenuItems = []*fyne.MenuItem{ + fyne.NewMenuItem("Remove from queue", a.onRemoveSelectedFromQueue), + } a.title = widget.NewRichTextWithText("Now Playing") a.title.Segments[0].(*widget.TextSegment).Style.SizeName = widget.RichTextStyleHeading.SizeName a.container = container.New(&layouts.MaxPadLayout{PadLeft: 15, PadRight: 15, PadTop: 5, PadBottom: 15}, @@ -89,6 +92,12 @@ func (a *NowPlayingPage) onPlayTrackAt(tracknum int) { _ = a.pm.PlayTrackAt(tracknum) } +func (a *NowPlayingPage) onRemoveSelectedFromQueue() { + a.pm.RemoveTracksFromQueue(a.tracklist.SelectedTrackIndexes()) + a.tracklist.UnselectAll() + go a.Reload() +} + func (a *NowPlayingPage) loadAsync() { go func() { queue := a.pm.GetPlayQueue()