diff --git a/backend/app.go b/backend/app.go index 71856b8..a7936b3 100644 --- a/backend/app.go +++ b/backend/app.go @@ -46,19 +46,19 @@ var ( ) type App struct { - Config *Config - ServerManager *ServerManager - LyricsManager *LyricsManager - ImageManager *ImageManager - AudioCache *AudioCache - AutoEQManager *AutoEQManager - EQPresetManager *EQPresetManager - PlaybackManager *PlaybackManager - LocalPlayer *mpv.Player - UpdateChecker UpdateChecker - MPRISHandler *MPRISHandler - WinSMTC *windows.SMTC - ipcServer ipc.IPCServer + Config *Config + ServerManager *ServerManager + LyricsManager *LyricsManager + ImageManager *ImageManager + AudioCache *AudioCache + AutoEQManager *AutoEQManager + EQPresetManager *EQPresetManager + PlaybackManager *PlaybackManager + LocalPlayer *mpv.Player + UpdateChecker UpdateChecker + MPRISHandler *MPRISHandler + WinSMTC *windows.SMTC + ipcServer ipc.IPCServer // UI callbacks to be set in main OnReactivate func() @@ -653,7 +653,7 @@ func (a *App) LoadSavedPlayQueue() error { return nil } - a.PlaybackManager.LoadTracks(playQueue.Tracks, Replace, Both, false) + a.PlaybackManager.LoadTracks(playQueue.Tracks, Replace, false) if playQueue.TrackIndex >= 0 && playQueue.TrackIndex < len(playQueue.Tracks) { // TODO: This isn't ideal but doesn't seem to cause an audible play-for-a-split-second artifact a.PlaybackManager.PlayTrackAt(playQueue.TrackIndex) @@ -673,8 +673,7 @@ func (a *App) LoadSavedPlayQueue() error { if slices.Equal(serverStatePlayQueue, clientStatePlayQueue) { fmt.Println("Loading unshuffled items into playQueue") - a.PlaybackManager.SetShuffle(true) - a.PlaybackManager.LoadTracks(unshuffledPlayQueue.Tracks, Replace, PlayQueue, false) + a.PlaybackManager.LoadTracks(unshuffledPlayQueue.Tracks, Replace, false) } else { a.PlaybackManager.SetShuffle(false) } diff --git a/backend/playbackcommands.go b/backend/playbackcommands.go index 3a79b70..2632fef 100644 --- a/backend/playbackcommands.go +++ b/backend/playbackcommands.go @@ -35,7 +35,6 @@ type playbackCommand struct { Arg any Arg2 any Arg3 any - Arg4 any OnDone func() } @@ -149,14 +148,13 @@ func (c *playbackCommandQueue) LoadRadioStation(radio *mediaprovider.RadioStatio c.cmdAvailable.Signal() } -func (c *playbackCommandQueue) LoadItems(items []mediaprovider.MediaItem, insertQueueMode InsertQueueMode, queueType QueueType, shuffle bool) { +func (c *playbackCommandQueue) LoadItems(items []mediaprovider.MediaItem, insertQueueMode InsertQueueMode, shuffle bool) { c.mutex.Lock() c.queue = append(c.queue, playbackCommand{ Type: cmdLoadItems, Arg: items, Arg2: insertQueueMode, - Arg3: queueType, - Arg4: shuffle, + Arg3: shuffle, }) c.mutex.Unlock() c.cmdAvailable.Signal() diff --git a/backend/playbackengine.go b/backend/playbackengine.go index 63a94cb..22430a4 100644 --- a/backend/playbackengine.go +++ b/backend/playbackengine.go @@ -7,7 +7,6 @@ import ( "log" "math/rand" "slices" - "strconv" "strings" "time" @@ -272,7 +271,6 @@ func (p *playbackEngine) insertItemsIntoPlayQueueAt(items []mediaprovider.MediaI p.playQueue = append(p.playQueue[:idx], append(items, p.playQueue[idx:]...)...) p.shuffledPlayQueue = append(p.shuffledPlayQueue[:idx], append(items, p.shuffledPlayQueue[idx:]...)...) case PlayQueue: - fmt.Println("Inserting into playQueue at: " + strconv.Itoa(idx)) p.playQueue = append(p.playQueue[:idx], append(items, p.playQueue[idx:]...)...) case ShuffledPlayQueue: p.shuffledPlayQueue = append(p.shuffledPlayQueue[:idx], append(items, p.shuffledPlayQueue[idx:]...)...) @@ -475,19 +473,21 @@ func (p *playbackEngine) Continue() error { // Load items into the play queue. // If replacing the current queue (!appendToQueue), playback will be stopped. -func (p *playbackEngine) LoadItems(items []mediaprovider.MediaItem, insertQueueMode InsertQueueMode, queueType QueueType, shuffle bool) error { +func (p *playbackEngine) LoadItems(items []mediaprovider.MediaItem, insertQueueMode InsertQueueMode, shuffle bool) error { newItems := deepCopyMediaItemSlice(items) - return p.doLoaditems(newItems, insertQueueMode, queueType, shuffle) + return p.doLoaditems(newItems, insertQueueMode, shuffle) } // Load tracks into the play queue. // If replacing the current queue (!appendToQueue), playback will be stopped. -func (p *playbackEngine) LoadTracks(tracks []*mediaprovider.Track, insertQueueMode InsertQueueMode, queueType QueueType, shuffle bool) error { +func (p *playbackEngine) LoadTracks(tracks []*mediaprovider.Track, insertQueueMode InsertQueueMode, shuffle bool) error { newTracks := sharedutil.CopyTrackSliceToMediaItemSlice(tracks) - return p.doLoaditems(newTracks, insertQueueMode, queueType, shuffle) + return p.doLoaditems(newTracks, insertQueueMode, shuffle) } -func (p *playbackEngine) doLoaditems(items []mediaprovider.MediaItem, insertQueueMode InsertQueueMode, queueType QueueType, shuffle bool) error { +func (p *playbackEngine) doLoaditems(items []mediaprovider.MediaItem, insertQueueMode InsertQueueMode, shuffle bool) error { + queueType := PlayQueue + if insertQueueMode == Replace { p.player.Stop(false) p.nowPlayingIdx = -1 @@ -497,16 +497,31 @@ func (p *playbackEngine) doLoaditems(items []mediaprovider.MediaItem, insertQueu defer p.handleNextTrackUpdated() } - if shuffle || p.shuffle { - rand.Shuffle(len(items), func(i, j int) { items[i], items[j] = items[j], items[i] }) - } - insertIdx := p.getPlayQueueLength() - if insertQueueMode == InsertNext { - insertIdx = p.nowPlayingIdx + 1 + + if insertQueueMode == Replace { + if shuffle || p.shuffle { + shuffledItems := deepCopyMediaItemSlice(items) + rand.Shuffle(len(shuffledItems), func(i, j int) { shuffledItems[i], shuffledItems[j] = shuffledItems[j], shuffledItems[i] }) + + p.insertItemsIntoPlayQueueAt(items, insertIdx, PlayQueue) + p.insertItemsIntoPlayQueueAt(shuffledItems, insertIdx, ShuffledPlayQueue) + } else { + p.insertItemsIntoPlayQueueAt(items, insertIdx, queueType) + } + } else { + if shuffle { + rand.Shuffle(len(items), func(i, j int) { items[i], items[j] = items[j], items[i] }) + } + if insertQueueMode == InsertNext { + insertIdx = p.nowPlayingIdx + 1 + } + if p.shuffle { + queueType = Both + } + p.insertItemsIntoPlayQueueAt(items, insertIdx, queueType) } - p.insertItemsIntoPlayQueueAt(items, insertIdx, queueType) p.invokeNoArgCallbacks(p.onQueueChange) return nil } diff --git a/backend/playbackmanager.go b/backend/playbackmanager.go index 7acebe1..d594cf5 100644 --- a/backend/playbackmanager.go +++ b/backend/playbackmanager.go @@ -369,7 +369,7 @@ func (p *PlaybackManager) LoadAlbum(albumID string, insertQueueMode InsertQueueM if err != nil { return err } - p.LoadTracks(album.Tracks, insertQueueMode, Both, shuffle) + p.LoadTracks(album.Tracks, insertQueueMode, shuffle) return nil } @@ -383,21 +383,21 @@ func (p *PlaybackManager) LoadPlaylist(playlistID string, insertQueueMode Insert if err != nil { return err } - p.LoadTracks(playlist.Tracks, insertQueueMode, Both, 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, queueType QueueType, shuffle bool) { +func (p *PlaybackManager) LoadTracks(tracks []*mediaprovider.Track, insertQueueMode InsertQueueMode, shuffle bool) { items := sharedutil.CopyTrackSliceToMediaItemSlice(tracks) - p.cmdQueue.LoadItems(items, insertQueueMode, queueType, shuffle) + p.cmdQueue.LoadItems(items, insertQueueMode, shuffle) } // Load items into the currently active queue. (shuffledPlayQueue/playQueue) // If replacing the current queue (!appendToQueue), playback will be stopped. -func (p *PlaybackManager) LoadItems(items []mediaprovider.MediaItem, insertQueueMode InsertQueueMode, queueType QueueType, shuffle bool) { - p.cmdQueue.LoadItems(items, insertQueueMode, queueType, 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. @@ -434,7 +434,7 @@ func (p *PlaybackManager) PlayTrack(trackID string) error { if err != nil { return err } - p.LoadTracks([]*mediaprovider.Track{tr}, Replace, Both, false) + p.LoadTracks([]*mediaprovider.Track{tr}, Replace, false) if p.engine.replayGainCfg.Mode == ReplayGainAuto { p.SetReplayGainMode(player.ReplayGainTrack) } @@ -472,7 +472,7 @@ func (p *PlaybackManager) PlayArtistDiscography(artistID string, shuffleTracks b log.Printf("failed to get artist tracks: %v\n", err) return } - p.LoadTracks(tr, Replace, Both, shuffleTracks) + p.LoadTracks(tr, Replace, shuffleTracks) if p.engine.replayGainCfg.Mode == ReplayGainAuto { if shuffleTracks { p.SetReplayGainMode(player.ReplayGainTrack) @@ -537,7 +537,7 @@ func (p *PlaybackManager) PlayRandomAlbums(genreName string) error { break } if al, err := mp.GetAlbum(al.ID); err == nil { - p.LoadTracks(al.Tracks, insertMode, Both, false) + p.LoadTracks(al.Tracks, insertMode, false) if i == 0 { p.PlayFromBeginning() insertMode = Append @@ -561,7 +561,7 @@ func (p *PlaybackManager) fetchAndPlayTracks(fetchFn func() ([]*mediaprovider.Tr if songs, err := fetchFn(); err != nil { return err } else { - p.LoadTracks(songs, Replace, Both, false) + p.LoadTracks(songs, Replace, false) if p.engine.replayGainCfg.Mode == ReplayGainAuto { p.SetReplayGainMode(player.ReplayGainTrack) } @@ -811,7 +811,7 @@ func (p *PlaybackManager) enqueueAutoplayTracks() { } if len(tracks) > 0 { - p.LoadTracks(tracks, Append, Both, false /*no need to shuffle, already random*/) + p.LoadTracks(tracks, Append, false /*no need to shuffle, already random*/) } }() } @@ -855,8 +855,7 @@ func (p *PlaybackManager) runCmdQueue(ctx context.Context) { err := p.engine.LoadItems( c.Arg.([]mediaprovider.MediaItem), c.Arg2.(InsertQueueMode), - c.Arg3.(QueueType), - c.Arg4.(bool), + c.Arg3.(bool), ) logIfErr("LoadItems", err) case cmdLoadRadioStation: diff --git a/ui/browsing/albumpage.go b/ui/browsing/albumpage.go index 0221df2..830a8a2 100644 --- a/ui/browsing/albumpage.go +++ b/ui/browsing/albumpage.go @@ -260,7 +260,7 @@ func NewAlbumPageHeader(page *AlbumPage) *AlbumPageHeader { go a.page.pm.PlayAlbum(a.page.albumID, 0, false) }) shuffleBtn := widget.NewButtonWithIcon(lang.L("Shuffle"), myTheme.ShuffleIcon, func() { - a.page.pm.LoadTracks(a.page.tracklist.GetTracks(), backend.Replace, backend.Both, true) + a.page.pm.LoadTracks(a.page.tracklist.GetTracks(), backend.Replace, true) a.page.pm.PlayFromBeginning() }) var pop *widget.PopUpMenu diff --git a/ui/browsing/favoritespage.go b/ui/browsing/favoritespage.go index 1708113..4ef3000 100644 --- a/ui/browsing/favoritespage.go +++ b/ui/browsing/favoritespage.go @@ -98,7 +98,7 @@ func (a *FavoritesPage) createHeader(activeBtnIdx int) { widget.NewButtonWithIcon("", myTheme.TracksIcon, a.onShowFavoriteSongs)) a.shuffleBtn = widget.NewButtonWithIcon(lang.L("Shuffle"), myTheme.ShuffleIcon, func() { if tr := a.tracklistOrNil(); tr != nil { - a.pm.LoadTracks(tr.GetTracks(), backend.Replace, backend.Both, true /*shuffle*/) + a.pm.LoadTracks(tr.GetTracks(), backend.Replace, true /*shuffle*/) a.pm.PlayFromBeginning() } }) diff --git a/ui/browsing/nowplayingpage.go b/ui/browsing/nowplayingpage.go index 241958c..97716b9 100644 --- a/ui/browsing/nowplayingpage.go +++ b/ui/browsing/nowplayingpage.go @@ -159,18 +159,18 @@ func NewNowPlayingPage( a.relatedList.OnSetRating = a.queueList.OnSetRating a.relatedList.OnSetFavorite = a.queueList.OnSetFavorite a.relatedList.OnPlayItemAt = func(idx int) { - a.pm.LoadTracks(a.related, backend.Replace, backend.Both, false) + a.pm.LoadTracks(a.related, backend.Replace, false) a.pm.PlayTrackAt(idx) } a.relatedList.OnAddToQueue = func(items []mediaprovider.MediaItem) { - a.pm.LoadItems(items, backend.Append, backend.Both, false) + a.pm.LoadItems(items, backend.Append, false) } a.relatedList.OnPlaySelection = func(items []mediaprovider.MediaItem, shuffle bool) { - a.pm.LoadItems(items, backend.Replace, backend.Both, shuffle) + a.pm.LoadItems(items, backend.Replace, shuffle) a.pm.PlayFromBeginning() } a.relatedList.OnPlaySelectionNext = func(items []mediaprovider.MediaItem) { - a.pm.LoadItems(items, backend.InsertNext, backend.Both, false) + a.pm.LoadItems(items, backend.InsertNext, false) } a.relatedList.OnPlaySongRadio = func(track *mediaprovider.Track) { go func() { diff --git a/ui/browsing/playlistpage.go b/ui/browsing/playlistpage.go index e4aff18..31374e1 100644 --- a/ui/browsing/playlistpage.go +++ b/ui/browsing/playlistpage.go @@ -348,11 +348,11 @@ func NewPlaylistPageHeader(page *PlaylistPage) *PlaylistPageHeader { }) a.editButton.Hidden = true playButton := widget.NewButtonWithIcon(lang.L("Play"), theme.MediaPlayIcon(), func() { - a.page.pm.LoadTracks(a.page.tracks, backend.Replace, backend.Both, false) + a.page.pm.LoadTracks(a.page.tracks, backend.Replace, false) a.page.pm.PlayFromBeginning() }) shuffleBtn := widget.NewButtonWithIcon(lang.L("Shuffle"), myTheme.ShuffleIcon, func() { - a.page.pm.LoadTracks(a.page.tracks, backend.Replace, backend.Both, true) + a.page.pm.LoadTracks(a.page.tracks, backend.Replace, true) a.page.pm.PlayFromBeginning() }) diff --git a/ui/controller/connectactions.go b/ui/controller/connectactions.go index cc617c4..1f051e5 100644 --- a/ui/controller/connectactions.go +++ b/ui/controller/connectactions.go @@ -25,20 +25,20 @@ func (m *Controller) ConnectTracklistActions(tracklist *widgets.Tracklist) { func (m *Controller) connectTracklistActionsWithReplayGainMode(tracklist *widgets.Tracklist, mode player.ReplayGainMode) { tracklist.OnAddToPlaylist = m.DoAddTracksToPlaylistWorkflow tracklist.OnPlaySelectionNext = func(tracks []*mediaprovider.Track) { - m.App.PlaybackManager.LoadTracks(tracks, backend.InsertNext, backend.Both, false) + m.App.PlaybackManager.LoadTracks(tracks, backend.InsertNext, false) } tracklist.OnAddToQueue = func(tracks []*mediaprovider.Track) { - m.App.PlaybackManager.LoadTracks(tracks, backend.Append, backend.Both, false) + m.App.PlaybackManager.LoadTracks(tracks, backend.Append, false) } tracklist.OnPlayTrackAt = func(idx int) { - m.App.PlaybackManager.LoadTracks(tracklist.GetTracks(), backend.Replace, backend.Both, false) + m.App.PlaybackManager.LoadTracks(tracklist.GetTracks(), backend.Replace, false) if m.App.Config.ReplayGain.Mode == backend.ReplayGainAuto { m.App.PlaybackManager.SetReplayGainMode(mode) } m.App.PlaybackManager.PlayTrackAt(idx) } tracklist.OnPlaySelection = func(tracks []*mediaprovider.Track, shuffle bool) { - m.App.PlaybackManager.LoadTracks(tracks, backend.Replace, backend.Both, shuffle) + m.App.PlaybackManager.LoadTracks(tracks, backend.Replace, shuffle) if m.App.Config.ReplayGain.Mode == backend.ReplayGainAuto { m.App.PlaybackManager.SetReplayGainMode(mode) } @@ -70,7 +70,7 @@ func (m *Controller) connectTracklistActionsWithReplayGainMode(tracklist *widget log.Println("Error getting song radio: ", err) return } - m.App.PlaybackManager.LoadTracks(tracks, backend.Replace, backend.Both, false) + m.App.PlaybackManager.LoadTracks(tracks, backend.Replace, false) if m.App.Config.ReplayGain.Mode == backend.ReplayGainAuto { m.App.PlaybackManager.SetReplayGainMode(mode) } @@ -164,11 +164,11 @@ func (m *Controller) onDownloadAlbum(albumID string) { func (m *Controller) ConnectArtistGridActions(grid *widgets.GridView) { grid.OnShowItemPage = func(id string) { m.NavigateTo(ArtistRoute(id)) } grid.OnPlayNext = func(artistID string) { - go m.App.PlaybackManager.LoadTracks(m.GetArtistTracks(artistID), backend.InsertNext, backend.Both, false) + go m.App.PlaybackManager.LoadTracks(m.GetArtistTracks(artistID), backend.InsertNext, false) } grid.OnPlay = func(artistID string, shuffle bool) { go m.App.PlaybackManager.PlayArtistDiscography(artistID, shuffle) } grid.OnAddToQueue = func(artistID string) { - go m.App.PlaybackManager.LoadTracks(m.GetArtistTracks(artistID), backend.Append, backend.Both, false) + go m.App.PlaybackManager.LoadTracks(m.GetArtistTracks(artistID), backend.Append, false) } grid.OnAddToPlaylist = func(artistID string) { m.DoAddTracksToPlaylistWorkflow( diff --git a/ui/controller/searchdialog.go b/ui/controller/searchdialog.go index 3c4ab65..b7c44d3 100644 --- a/ui/controller/searchdialog.go +++ b/ui/controller/searchdialog.go @@ -46,7 +46,7 @@ func (c *Controller) ShowQuickSearch() { case mediaprovider.ContentTypeTrack: c.App.PlaybackManager.LoadTracks( []*mediaprovider.Track{item.(*mediaprovider.Track)}, - backend.Replace, backend.Both, false /*shuffle*/) + backend.Replace, false /*shuffle*/) c.App.PlaybackManager.PlayFromBeginning() case mediaprovider.ContentTypeAlbum: go c.App.PlaybackManager.PlayAlbum(id, 0, shuffle) @@ -69,7 +69,7 @@ func (c *Controller) ShowQuickSearch() { go func() { tracks, err := c.GetSongRadioTracks(track) if err != nil { - c.App.PlaybackManager.LoadTracks(tracks, backend.Replace, backend.Both, false) + c.App.PlaybackManager.LoadTracks(tracks, backend.Replace, false) c.App.PlaybackManager.PlayFromBeginning() } }() @@ -99,7 +99,6 @@ func (c *Controller) ShowQuickSearch() { func (c *Controller) handleSearchDialogOnAddToQueue(t mediaprovider.ContentType, id string, item any, next bool) { insertMode := backend.Append - queueType := backend.Both if next { insertMode = backend.InsertNext } @@ -107,13 +106,13 @@ func (c *Controller) handleSearchDialogOnAddToQueue(t mediaprovider.ContentType, case mediaprovider.ContentTypeTrack: c.App.PlaybackManager.LoadTracks( []*mediaprovider.Track{item.(*mediaprovider.Track)}, - insertMode, queueType, false /*shuffle*/) + insertMode, false /*shuffle*/) case mediaprovider.ContentTypeAlbum: go c.App.PlaybackManager.LoadAlbum(id, insertMode, false) case mediaprovider.ContentTypeArtist: go func() { tracks := c.GetArtistTracks(id) - c.App.PlaybackManager.LoadTracks(tracks, insertMode, queueType, false) + c.App.PlaybackManager.LoadTracks(tracks, insertMode, false) }() case mediaprovider.ContentTypePlaylist: go c.App.PlaybackManager.LoadPlaylist(id, insertMode, false) @@ -121,7 +120,7 @@ func (c *Controller) handleSearchDialogOnAddToQueue(t mediaprovider.ContentType, go func() { tr, err := c.App.ServerManager.Server.GetRandomTracks(id /*genre name*/, c.App.Config.Application.EnqueueBatchSize) if err != nil { - c.App.PlaybackManager.LoadTracks(tr, insertMode, queueType, false /*shuffle*/) + c.App.PlaybackManager.LoadTracks(tr, insertMode, false /*shuffle*/) } }() case mediaprovider.ContentTypeRadioStation: