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
|
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 {
|
func (p *PlaybackManager) PlayAlbum(albumID string, firstTrack int, shuffle bool) error {
|
||||||
if err := p.LoadAlbum(albumID, false, shuffle); err != nil {
|
if err := p.LoadAlbum(albumID, false, shuffle); err != nil {
|
||||||
return err
|
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 {
|
func (p *PlaybackManager) setTrack(idx int, next bool) error {
|
||||||
if urlP, ok := p.player.(player.URLPlayer); ok {
|
if urlP, ok := p.player.(player.URLPlayer); ok {
|
||||||
url := ""
|
url := ""
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ func NewNowPlayingPage(
|
|||||||
DisablePlaybackMenu: true,
|
DisablePlaybackMenu: true,
|
||||||
DisableRating: !canRate,
|
DisableRating: !canRate,
|
||||||
AuxiliaryMenuItems: []*fyne.MenuItem{
|
AuxiliaryMenuItems: []*fyne.MenuItem{
|
||||||
|
util.NewReorderTracksSubmenu(a.doSetNewTrackOrder),
|
||||||
fyne.NewMenuItem("Remove from queue", a.onRemoveSelectedFromQueue),
|
fyne.NewMenuItem("Remove from queue", a.onRemoveSelectedFromQueue),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -208,6 +209,26 @@ func (a *NowPlayingPage) onRemoveSelectedFromQueue() {
|
|||||||
a.Reload()
|
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
|
// does not make calls to server - can safely be run in UI callbacks
|
||||||
func (a *NowPlayingPage) load(highlightedTrackID string) {
|
func (a *NowPlayingPage) load(highlightedTrackID string) {
|
||||||
a.queue = a.pm.GetPlayQueue()
|
a.queue = a.pm.GetPlayQueue()
|
||||||
|
|||||||
@@ -86,18 +86,13 @@ func newPlaylistPage(
|
|||||||
a.tracklist.OnVisibleColumnsChanged = func(cols []string) {
|
a.tracklist.OnVisibleColumnsChanged = func(cols []string) {
|
||||||
conf.TracklistColumns = cols
|
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)
|
_, canRate := a.sm.Server.(mediaprovider.SupportsRating)
|
||||||
a.tracklist.Options = widgets.TracklistOptions{
|
a.tracklist.Options = widgets.TracklistOptions{
|
||||||
DisableRating: !canRate,
|
DisableRating: !canRate,
|
||||||
AuxiliaryMenuItems: []*fyne.MenuItem{reorderMenu,
|
AuxiliaryMenuItems: []*fyne.MenuItem{
|
||||||
fyne.NewMenuItem("Remove from playlist", a.onRemoveSelectedFromPlaylist)},
|
util.NewReorderTracksSubmenu(a.doSetNewTrackOrder),
|
||||||
|
fyne.NewMenuItem("Remove from playlist", a.onRemoveSelectedFromPlaylist),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
// connect tracklist actions
|
// connect tracklist actions
|
||||||
a.contr.ConnectTracklistActions(a.tracklist)
|
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) {
|
func (a *PlaylistPage) doSetNewTrackOrder(op sharedutil.TrackReorderOp) {
|
||||||
// Since the tracklist view may be sorted in a different order than the
|
// 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
|
// 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/theme"
|
||||||
"fyne.io/fyne/v2/widget"
|
"fyne.io/fyne/v2/widget"
|
||||||
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
||||||
|
"github.com/dweymouth/supersonic/sharedutil"
|
||||||
"github.com/dweymouth/supersonic/ui/layouts"
|
"github.com/dweymouth/supersonic/ui/layouts"
|
||||||
myTheme "github.com/dweymouth/supersonic/ui/theme"
|
myTheme "github.com/dweymouth/supersonic/ui/theme"
|
||||||
"golang.org/x/net/html"
|
"golang.org/x/net/html"
|
||||||
@@ -180,6 +181,17 @@ func NewRatingSubmenu(onSetRating func(int)) *fyne.MenuItem {
|
|||||||
return ratingMenu
|
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 {
|
func AddHeaderBackground(obj fyne.CanvasObject) *fyne.Container {
|
||||||
bgrnd := myTheme.NewThemedRectangle(myTheme.ColorNamePageHeader)
|
bgrnd := myTheme.NewThemedRectangle(myTheme.ColorNamePageHeader)
|
||||||
bgrnd.CornerRadiusName = theme.SizeNameInputRadius
|
bgrnd.CornerRadiusName = theme.SizeNameInputRadius
|
||||||
|
|||||||
Reference in New Issue
Block a user