Fix #33: allow reordering of play queue
This commit is contained in:
@@ -221,6 +221,42 @@ func (p *PlaybackManager) LoadTracks(tracks []*mediaprovider.Track, appendToQueu
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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(tracks []*mediaprovider.Track) error {
|
||||
newQueue := make([]*mediaprovider.Track, len(tracks))
|
||||
for i, tr := range tracks {
|
||||
// ensure a deep copy of the track info so that we can maintain our own state
|
||||
// (tracking play count increases, favorite, and rating) without messing up
|
||||
// other views' track models
|
||||
track := *tr
|
||||
newQueue[i] = &track
|
||||
}
|
||||
newNowPlayingIdx := -1
|
||||
if p.nowPlayingIdx >= 0 {
|
||||
nowPlayingID := p.playQueue[p.nowPlayingIdx].ID
|
||||
for i, tr := range newQueue {
|
||||
if tr.ID == nowPlayingID {
|
||||
newNowPlayingIdx = i
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p.playQueue = newQueue
|
||||
if p.nowPlayingIdx >= 0 && newNowPlayingIdx == -1 {
|
||||
return p.Stop()
|
||||
}
|
||||
needToUpdateNext := p.nowPlayingIdx >= 0
|
||||
p.nowPlayingIdx = newNowPlayingIdx
|
||||
if needToUpdateNext {
|
||||
p.setNextTrackAfterQueueUpdate()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PlaybackManager) PlayAlbum(albumID string, firstTrack int, shuffle bool) error {
|
||||
if err := p.LoadAlbum(albumID, false, shuffle); err != nil {
|
||||
return err
|
||||
@@ -563,6 +599,26 @@ func (p *PlaybackManager) setNextTrackBasedOnLoopMode(onLoopModeChange bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PlaybackManager) setNextTrackAfterQueueUpdate() {
|
||||
switch p.loopMode {
|
||||
case LoopNone:
|
||||
if p.nowPlayingIdx < len(p.playQueue)-1 {
|
||||
p.setNextTrack(p.nowPlayingIdx + 1)
|
||||
} else {
|
||||
// need to erase next track
|
||||
p.setNextTrack(-1)
|
||||
}
|
||||
case LoopOne:
|
||||
p.setNextTrack(p.nowPlayingIdx)
|
||||
case LoopAll:
|
||||
if p.nowPlayingIdx >= len(p.playQueue)-1 {
|
||||
p.setNextTrack(0)
|
||||
} else {
|
||||
p.setNextTrack(p.nowPlayingIdx + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PlaybackManager) setTrack(idx int, next bool) error {
|
||||
if urlP, ok := p.player.(player.URLPlayer); ok {
|
||||
url := ""
|
||||
|
||||
@@ -77,6 +77,7 @@ func NewNowPlayingPage(
|
||||
DisablePlaybackMenu: true,
|
||||
DisableRating: !canRate,
|
||||
AuxiliaryMenuItems: []*fyne.MenuItem{
|
||||
util.NewReorderTracksSubmenu(a.doSetNewTrackOrder),
|
||||
fyne.NewMenuItem("Remove from queue", a.onRemoveSelectedFromQueue),
|
||||
},
|
||||
}
|
||||
@@ -208,6 +209,26 @@ func (a *NowPlayingPage) onRemoveSelectedFromQueue() {
|
||||
a.Reload()
|
||||
}
|
||||
|
||||
func (a *NowPlayingPage) doSetNewTrackOrder(op sharedutil.TrackReorderOp) {
|
||||
// Since the tracklist view may be sorted in a different order than the
|
||||
// actual running order, we need to get the IDs of the selected tracks
|
||||
// from the tracklist and convert them to indices in the *original* run order
|
||||
ids := a.tracklist.SelectedTrackIDs()
|
||||
idxs := make([]int, 0, len(ids))
|
||||
for i, tr := range a.queue {
|
||||
if sharedutil.SliceContains(ids, tr.ID) {
|
||||
idxs = append(idxs, i)
|
||||
}
|
||||
}
|
||||
newTracks := sharedutil.ReorderTracks(a.queue, idxs, op)
|
||||
a.pm.UpdatePlayQueue(newTracks)
|
||||
|
||||
// force-switch back to unsorted view to show new track order
|
||||
a.tracklist.SetSorting(widgets.TracklistSort{})
|
||||
a.tracklist.SetTracks(newTracks)
|
||||
a.tracklist.UnselectAll()
|
||||
}
|
||||
|
||||
// does not make calls to server - can safely be run in UI callbacks
|
||||
func (a *NowPlayingPage) load(highlightedTrackID string) {
|
||||
a.queue = a.pm.GetPlayQueue()
|
||||
|
||||
@@ -86,18 +86,13 @@ func newPlaylistPage(
|
||||
a.tracklist.OnVisibleColumnsChanged = func(cols []string) {
|
||||
conf.TracklistColumns = cols
|
||||
}
|
||||
reorderMenu := fyne.NewMenuItem("Reorder tracks", nil)
|
||||
reorderMenu.ChildMenu = fyne.NewMenu("", []*fyne.MenuItem{
|
||||
fyne.NewMenuItem("Move to top", a.onMoveSelectedToTop),
|
||||
fyne.NewMenuItem("Move up", a.onMoveSelectedUp),
|
||||
fyne.NewMenuItem("Move down", a.onMoveSelectedDown),
|
||||
fyne.NewMenuItem("Move to bottom", a.onMoveSelectedToBottom),
|
||||
}...)
|
||||
_, canRate := a.sm.Server.(mediaprovider.SupportsRating)
|
||||
a.tracklist.Options = widgets.TracklistOptions{
|
||||
DisableRating: !canRate,
|
||||
AuxiliaryMenuItems: []*fyne.MenuItem{reorderMenu,
|
||||
fyne.NewMenuItem("Remove from playlist", a.onRemoveSelectedFromPlaylist)},
|
||||
AuxiliaryMenuItems: []*fyne.MenuItem{
|
||||
util.NewReorderTracksSubmenu(a.doSetNewTrackOrder),
|
||||
fyne.NewMenuItem("Remove from playlist", a.onRemoveSelectedFromPlaylist),
|
||||
},
|
||||
}
|
||||
// connect tracklist actions
|
||||
a.contr.ConnectTracklistActions(a.tracklist)
|
||||
@@ -171,22 +166,6 @@ func renumberTracks(tracks []*mediaprovider.Track) {
|
||||
}
|
||||
}
|
||||
|
||||
func (a *PlaylistPage) onMoveSelectedToTop() {
|
||||
a.doSetNewTrackOrder(sharedutil.MoveToTop)
|
||||
}
|
||||
|
||||
func (a *PlaylistPage) onMoveSelectedUp() {
|
||||
a.doSetNewTrackOrder(sharedutil.MoveUp)
|
||||
}
|
||||
|
||||
func (a *PlaylistPage) onMoveSelectedDown() {
|
||||
a.doSetNewTrackOrder(sharedutil.MoveDown)
|
||||
}
|
||||
|
||||
func (a *PlaylistPage) onMoveSelectedToBottom() {
|
||||
a.doSetNewTrackOrder(sharedutil.MoveToBottom)
|
||||
}
|
||||
|
||||
func (a *PlaylistPage) doSetNewTrackOrder(op sharedutil.TrackReorderOp) {
|
||||
// Since the tracklist view may be sorted in a different order than the
|
||||
// actual running order, we need to get the IDs of the selected tracks
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"fyne.io/fyne/v2/theme"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
||||
"github.com/dweymouth/supersonic/sharedutil"
|
||||
"github.com/dweymouth/supersonic/ui/layouts"
|
||||
myTheme "github.com/dweymouth/supersonic/ui/theme"
|
||||
"golang.org/x/net/html"
|
||||
@@ -180,6 +181,17 @@ func NewRatingSubmenu(onSetRating func(int)) *fyne.MenuItem {
|
||||
return ratingMenu
|
||||
}
|
||||
|
||||
func NewReorderTracksSubmenu(onReorderTracks func(sharedutil.TrackReorderOp)) *fyne.MenuItem {
|
||||
reorderMenu := fyne.NewMenuItem("Reorder tracks", nil)
|
||||
reorderMenu.ChildMenu = fyne.NewMenu("", []*fyne.MenuItem{
|
||||
fyne.NewMenuItem("Move to top", func() { onReorderTracks(sharedutil.MoveToTop) }),
|
||||
fyne.NewMenuItem("Move up", func() { onReorderTracks(sharedutil.MoveUp) }),
|
||||
fyne.NewMenuItem("Move down", func() { onReorderTracks(sharedutil.MoveDown) }),
|
||||
fyne.NewMenuItem("Move to bottom", func() { onReorderTracks(sharedutil.MoveToBottom) }),
|
||||
}...)
|
||||
return reorderMenu
|
||||
}
|
||||
|
||||
func AddHeaderBackground(obj fyne.CanvasObject) *fyne.Container {
|
||||
bgrnd := myTheme.NewThemedRectangle(myTheme.ColorNamePageHeader)
|
||||
bgrnd.CornerRadiusName = theme.SizeNameInputRadius
|
||||
|
||||
Reference in New Issue
Block a user