From 0e0ea5a16551cd0b2c656e9dc4ceda8a0ddde352 Mon Sep 17 00:00:00 2001 From: Siiiiinth <60766886+Siiiiinth@users.noreply.github.com> Date: Sun, 1 Feb 2026 18:46:49 +0100 Subject: [PATCH] fixed TODOs, first checkpoint saving queue on server --- backend/app.go | 58 +++++++++++++++++++++++++++++--------- backend/playbackengine.go | 39 ++++++++++++------------- backend/playbackmanager.go | 11 ++++++-- backend/savedplayqueue.go | 3 +- sharedutil/sharedutil.go | 8 ++++++ 5 files changed, 81 insertions(+), 38 deletions(-) diff --git a/backend/app.go b/backend/app.go index e731f20..f584f33 100644 --- a/backend/app.go +++ b/backend/app.go @@ -28,11 +28,12 @@ import ( ) const ( - configFile = "config.toml" - portableDir = "supersonic_portable" - savedQueueFile = "saved_queue.json" - themesDir = "themes" - audioCacheSubdir = "audio" + configFile = "config.toml" + portableDir = "supersonic_portable" + savedQueueFile = "saved_queue.json" + savedUnshuffledQueueFile = "saved_unshuffled_queue.json" + themesDir = "themes" + audioCacheSubdir = "audio" ) var ( @@ -604,30 +605,61 @@ func (a *App) SavePlayQueueIfEnabled() { queueServer = qs } } - SavePlayQueue(a.ServerManager.ServerID.String(), a.PlaybackManager, path.Join(a.configDir, savedQueueFile), queueServer) + SavePlayQueue(a.ServerManager.ServerID.String(), a.PlaybackManager.GetActivePlayQueue(), a.PlaybackManager, path.Join(a.configDir, savedQueueFile), queueServer) + if a.Config.Playback.Shuffle { + // if shuffle, also save the unshuffeled queue to enable unshuffling on restarting supersonic + SavePlayQueue(a.ServerManager.ServerID.String(), a.PlaybackManager.GetPlayQueue(), a.PlaybackManager, path.Join(a.configDir, savedUnshuffledQueueFile), queueServer) + } } func (a *App) LoadSavedPlayQueue() error { queueFilePath := path.Join(a.configDir, savedQueueFile) - queue, err := LoadPlayQueue(queueFilePath, a.ServerManager, a.Config.Application.SaveQueueToServer) + playQueue, err := LoadPlayQueue(queueFilePath, a.ServerManager, a.Config.Application.SaveQueueToServer) + var unshuffledPlayQueue *SavedPlayQueue + + if a.Config.Playback.Shuffle { + unshuffledQueueFilePath := path.Join(a.configDir, savedUnshuffledQueueFile) + unshuffledPlayQueue, err = LoadPlayQueue(unshuffledQueueFilePath, a.ServerManager, a.Config.Application.SaveQueueToServer) //unshuffledPlayQueue is only stored locally + + if err != nil { + return err + } + } if err != nil { return err } - if len(queue.Tracks) == 0 { + if len(playQueue.Tracks) == 0 { return nil } - if len(a.PlaybackManager.GetPlayQueue()) > 0 { + if len(a.PlaybackManager.GetActivePlayQueue()) > 0 { // don't restore play queue if the user has already queued new tracks return nil } - a.PlaybackManager.LoadTracks(queue.Tracks, Replace, false) - if queue.TrackIndex >= 0 && queue.TrackIndex < len(queue.Tracks) { + 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(queue.TrackIndex) + a.PlaybackManager.PlayTrackAt(playQueue.TrackIndex) a.PlaybackManager.Pause() time.Sleep(100 * time.Millisecond) // MPV seek fails if run quickly after - a.PlaybackManager.SeekSeconds(queue.TimePos) + a.PlaybackManager.SeekSeconds(playQueue.TimePos) + } + + if a.Config.Playback.Shuffle { + // check if queue was changed server side + // if playQueue.items same as unshuffledPlayQueue.items + // set shuffle to false, only load normal playqueue + if len(playQueue.Tracks) != len(unshuffledPlayQueue.Tracks) { + // queue was changed server side, no need to compare elements + a.PlaybackManager.SetShuffle(false) + } else { + //TODO_SHUFFLE: implement + + //shuffled := sharedutil.CopyTrackSliceToMediaItemSlice(playQueue.Tracks) + //unshuffled := sharedutil.CopyTrackSliceToMediaItemSlice(unshuffledPlayQueue.Tracks) + + //a.PlaybackManager.engine.setPlayQueue(unshuffled) + } } return nil } diff --git a/backend/playbackengine.go b/backend/playbackengine.go index 1000cf1..72931f4 100644 --- a/backend/playbackengine.go +++ b/backend/playbackengine.go @@ -253,6 +253,14 @@ func (p *playbackEngine) setShuffledPlayQueue(items []mediaprovider.MediaItem) { p.shuffledPlayQueue = items } +func (p *playbackEngine) setActivePlayQueue(items []mediaprovider.MediaItem) { + if p.shuffle { + p.setShuffledPlayQueue(items) + } else { + p.setPlayQueue(items) + } +} + func (p *playbackEngine) getPlayQueueItemAt(idx int) mediaprovider.MediaItem { return p.getPlayQueue()[idx] } @@ -266,6 +274,10 @@ func (p *playbackEngine) GetPlayQueueDeepCopy() []mediaprovider.MediaItem { return deepCopyMediaItemSlice(p.getPlayQueue()) } +func (p *playbackEngine) GetShuffledPlayQueueDeepCopy() []mediaprovider.MediaItem { + return deepCopyMediaItemSlice(p.getPlayQueue()) +} + func (p *playbackEngine) GetActivePlayQueueDeepCopy() []mediaprovider.MediaItem { return deepCopyMediaItemSlice(p.getActivePlayQueue()) } @@ -317,8 +329,7 @@ func (p *playbackEngine) GetLoopMode() LoopMode { return p.loopMode } -// TODO_SHUFFLE: rename? -func (p *playbackEngine) GetNewNowPlayingIdx(items []mediaprovider.MediaItem) int { +func (p *playbackEngine) GetNowPlayingIdxFrom(items []mediaprovider.MediaItem) int { newNowPlayingIdx := -1 if p.nowPlayingIdx >= 0 { nowPlayingID := p.getPlayQueueItemAt(p.nowPlayingIdx).Metadata().ID @@ -343,10 +354,10 @@ func (p *playbackEngine) SetShuffle(shuffle bool) { rand.Shuffle(len(shuffledQueue), func(i, j int) { shuffledQueue[i], shuffledQueue[j] = shuffledQueue[j], shuffledQueue[i] }) - p.shuffledPlayQueue = sharedutil.ReorderItems(shuffledQueue, []int{p.GetNewNowPlayingIdx(shuffledQueue)}, 0) + p.shuffledPlayQueue = sharedutil.ReorderItems(shuffledQueue, []int{p.GetNowPlayingIdxFrom(shuffledQueue)}, 0) newNowPlayingIdx = 0 } else { - newNowPlayingIdx = p.GetNewNowPlayingIdx(p.playQueue) + newNowPlayingIdx = p.GetNowPlayingIdxFrom(p.playQueue) } p.shuffle = shuffle @@ -463,7 +474,7 @@ func (p *playbackEngine) LoadItems(items []mediaprovider.MediaItem, insertQueueM // 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, shuffle bool) error { - newTracks := copyTrackSliceToMediaItemSlice(tracks) + newTracks := sharedutil.CopyTrackSliceToMediaItemSlice(tracks) return p.doLoaditems(newTracks, insertQueueMode, shuffle) } @@ -504,13 +515,7 @@ func (p *playbackEngine) LoadRadioStation(radio *mediaprovider.RadioStation, ins if insertMode == InsertNext { insertIdx = p.nowPlayingIdx + 1 } - new := make([]mediaprovider.MediaItem, p.getPlayQueueLength()+1) - firstHalf := p.playQueue[:insertIdx] - copy(new, firstHalf) - new[len(firstHalf)] = radio - copy(new[len(firstHalf)+1:], p.playQueue[insertIdx:]) //TODO_SHUFFLE: Test shuffle with radio stations - p.setPlayQueue(new) - + p.insertItemsIntoPlayQueueAt([]mediaprovider.MediaItem{radio}, insertIdx) p.invokeNoArgCallbacks(p.onQueueChange) } @@ -587,7 +592,7 @@ func (p *playbackEngine) RemoveTracksFromQueue(idxs []int) { // remove tracks by ID from playQueue ids := p.GetTrackIdsFromIdx(idxs) newPlayQueue := make([]mediaprovider.MediaItem, 0, p.getPlayQueueLength()-len(idxs)) - for _, tr := range p.playQueue { //TODO_SHUFFLE: Rework this to use a func for getPlayQueue + for _, tr := range p.getPlayQueue() { if slices.Contains(ids, tr.Metadata().ID) { //remove id from id list, handles having the same track present multiple times in playQueue idx := slices.Index(ids, tr.Metadata().ID) @@ -954,14 +959,6 @@ func deepCopyMediaItemSlice(tracks []mediaprovider.MediaItem) []mediaprovider.Me return newTracks } -func copyTrackSliceToMediaItemSlice(tracks []*mediaprovider.Track) []mediaprovider.MediaItem { - newTracks := make([]mediaprovider.MediaItem, len(tracks)) - for i, tr := range tracks { - newTracks[i] = tr.Copy() - } - return newTracks -} - func (p *playbackEngine) invokeOnSongChangeCallbacks() { if p.callbacksDisabled { return diff --git a/backend/playbackmanager.go b/backend/playbackmanager.go index 63bef50..345eb55 100644 --- a/backend/playbackmanager.go +++ b/backend/playbackmanager.go @@ -390,7 +390,7 @@ func (p *PlaybackManager) LoadPlaylist(playlistID string, insertQueueMode Insert // 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) { - items := copyTrackSliceToMediaItemSlice(tracks) + items := sharedutil.CopyTrackSliceToMediaItemSlice(tracks) p.cmdQueue.LoadItems(items, insertQueueMode, shuffle) } @@ -570,10 +570,18 @@ func (p *PlaybackManager) fetchAndPlayTracks(fetchFn func() ([]*mediaprovider.Tr } } +func (p *PlaybackManager) GetActivePlayQueue() []mediaprovider.MediaItem { + return p.engine.GetActivePlayQueueDeepCopy() +} + func (p *PlaybackManager) GetPlayQueue() []mediaprovider.MediaItem { return p.engine.GetPlayQueueDeepCopy() } +func (p *PlaybackManager) GetShuffledPlayQueue() []mediaprovider.MediaItem { + return p.engine.GetShuffledPlayQueueDeepCopy() +} + // Any time the user changes the favorite status of a track elsewhere in the app, // this should be called to ensure the in-memory track model is updated. func (p *PlaybackManager) OnTrackFavoriteStatusChanged(id string, fav bool) { @@ -645,7 +653,6 @@ func (p *PlaybackManager) SetAutoplay(autoplay bool) { func (p *PlaybackManager) SetShuffle(shuffle bool) { p.cfg.Shuffle = shuffle - fmt.Println("Setting shuffle in config") p.engine.SetShuffle(shuffle) } diff --git a/backend/savedplayqueue.go b/backend/savedplayqueue.go index b7ec5ed..6234665 100644 --- a/backend/savedplayqueue.go +++ b/backend/savedplayqueue.go @@ -24,8 +24,7 @@ type serializedSavedPlayQueue struct { // SavePlayQueue saves the current play queue and playback position to a JSON file. // If the provided CanSavePlayQueue server is non-nil, it will also save to the server. -func SavePlayQueue(serverID string, pm *PlaybackManager, filepath string, server mediaprovider.CanSavePlayQueue) error { - queue := pm.GetPlayQueue() +func SavePlayQueue(serverID string, queue []mediaprovider.MediaItem, pm *PlaybackManager, filepath string, server mediaprovider.CanSavePlayQueue) error { stats := pm.PlaybackStatus() trackIdx := pm.NowPlayingIndex() diff --git a/sharedutil/sharedutil.go b/sharedutil/sharedutil.go index e094364..7907d78 100644 --- a/sharedutil/sharedutil.go +++ b/sharedutil/sharedutil.go @@ -186,3 +186,11 @@ func DownloadFileWithContext(ctx context.Context, url string, destPath string) ( return true, nil } + +func CopyTrackSliceToMediaItemSlice(tracks []*mediaprovider.Track) []mediaprovider.MediaItem { + newTracks := make([]mediaprovider.MediaItem, len(tracks)) + for i, tr := range tracks { + newTracks[i] = tr.Copy() + } + return newTracks +}