From 17e20e1596928b7e6c72b0c43f254ad0e766fc7b Mon Sep 17 00:00:00 2001 From: Siiiiinth <60766886+Siiiiinth@users.noreply.github.com> Date: Sun, 1 Feb 2026 12:01:55 +0100 Subject: [PATCH] implement remove track for shuffle --- backend/playbackengine.go | 170 +++++++++++++++++++++----------------- 1 file changed, 96 insertions(+), 74 deletions(-) diff --git a/backend/playbackengine.go b/backend/playbackengine.go index 0b8ae86..fd5a979 100644 --- a/backend/playbackengine.go +++ b/backend/playbackengine.go @@ -6,6 +6,7 @@ import ( "fmt" "log" "math/rand" + "slices" "strings" "time" @@ -238,11 +239,11 @@ func (p *playbackEngine) clearPlayQueue() { } func (p *playbackEngine) setPlayQueue(items []mediaprovider.MediaItem) { - if p.shuffle { - p.shuffledPlayQueue = items - } else { - p.playQueue = items - } + p.playQueue = items +} + +func (p *playbackEngine) setShuffledPlayQueue(items []mediaprovider.MediaItem) { + p.shuffledPlayQueue = items } func (p *playbackEngine) getPlayQueueItemAt(idx int) mediaprovider.MediaItem { @@ -305,63 +306,45 @@ func (p *playbackEngine) GetLoopMode() LoopMode { return p.loopMode } +// TODO_SHUFFLE: rename? +func (p *playbackEngine) GetNewNowPlayingIdx(items []mediaprovider.MediaItem) int { + newNowPlayingIdx := -1 + if p.nowPlayingIdx >= 0 { + nowPlayingID := p.getPlayQueueItemAt(p.nowPlayingIdx).Metadata().ID + for i, tr := range items { + if tr.Metadata().ID == nowPlayingID { + newNowPlayingIdx = i + break + } + } + } + return newNowPlayingIdx +} + func (p *playbackEngine) SetShuffle(shuffle bool) { - //TODO_SHUFFLE: fix very bad code if p.shuffle == shuffle { return } + + newNowPlayingIdx := -1 if shuffle { - fmt.Println("shuffling") shuffledQueue := deepCopyMediaItemSlice(p.playQueue) rand.Shuffle(len(shuffledQueue), func(i, j int) { shuffledQueue[i], shuffledQueue[j] = shuffledQueue[j], shuffledQueue[i] }) - newNowPlayingIdx := -1 - if p.nowPlayingIdx >= 0 { - nowPlayingID := p.getPlayQueueItemAt(p.nowPlayingIdx).Metadata().ID - for i, tr := range shuffledQueue { - if tr.Metadata().ID == nowPlayingID { - newNowPlayingIdx = i - break - } - } - } - p.shuffle = shuffle + newNowPlayingIdx = p.GetNewNowPlayingIdx(shuffledQueue) p.shuffledPlayQueue = shuffledQueue - if p.nowPlayingIdx >= 0 && newNowPlayingIdx == -1 { - return - } - if p.nowPlayingIdx >= 0 { - p.handleNextTrackUpdated() - } - p.nowPlayingIdx = newNowPlayingIdx - - p.invokeNoArgCallbacks(p.onQueueChange) } else { - fmt.Println("deshuffling") - newQueue := deepCopyMediaItemSlice(p.playQueue) - newNowPlayingIdx := -1 - if p.nowPlayingIdx >= 0 { - nowPlayingID := p.getPlayQueueItemAt(p.nowPlayingIdx).Metadata().ID - for i, tr := range newQueue { - if tr.Metadata().ID == nowPlayingID { - newNowPlayingIdx = i - break - } - } - } - p.shuffle = shuffle - p.playQueue = newQueue - if p.nowPlayingIdx >= 0 && newNowPlayingIdx == -1 { - return - } - if p.nowPlayingIdx >= 0 { - p.handleNextTrackUpdated() - } - p.nowPlayingIdx = newNowPlayingIdx - - p.invokeNoArgCallbacks(p.onQueueChange) + newNowPlayingIdx = p.GetNewNowPlayingIdx(p.playQueue) } + + p.shuffle = shuffle + if p.nowPlayingIdx >= 0 && newNowPlayingIdx == -1 { + return + } + p.handleNextTrackUpdated() + p.nowPlayingIdx = newNowPlayingIdx + p.invokeNoArgCallbacks(p.onQueueChange) } func (p *playbackEngine) GetShuffle() bool { @@ -585,36 +568,29 @@ func (p *playbackEngine) UpdatePlayQueue(items []mediaprovider.MediaItem) error } func (p *playbackEngine) RemoveTracksFromQueue(idxs []int) { - newQueue := make([]mediaprovider.MediaItem, 0, p.getPlayQueueLength()-len(idxs)) - idxSet := sharedutil.ToSet(idxs) isPlayingTrackRemoved := false isNextPlayingTrackremoved := false nowPlaying := p.NowPlayingIndex() newNowPlaying := nowPlaying - for i, tr := range p.getPlayQueue() { - if _, ok := idxSet[i]; ok { - if i < nowPlaying { - // if removing a track earlier than the currently playing one (if any), - // decrement new now playing index by one to account for new position in queue - newNowPlaying-- - } else if i == nowPlaying { - isPlayingTrackRemoved = true - // If we are removing the currently playing track, we need to scrobble it - p.checkScrobble() - p.alreadyScrobbled = true - } else if nowPlaying >= 0 && i == nowPlaying+1 { - isNextPlayingTrackremoved = true - } - } else { - // not removing this track - newQueue = append(newQueue, tr) - } + + if p.shuffle { + ids := p.GetTrackIdsFromIdx(idxs) + newPlayQueue := make([]mediaprovider.MediaItem, 0, p.getPlayQueueLength()-len(idxs)) + p.RemoveTracksFromQueueById(ids, &newPlayQueue) + p.setPlayQueue(newPlayQueue) + + newShuffledQueue := make([]mediaprovider.MediaItem, 0, p.getPlayQueueLength()-len(idxs)) + p.RemoveTracksFromQueueByIdx(idxs, &newShuffledQueue, &newNowPlaying, &isPlayingTrackRemoved, &isNextPlayingTrackremoved) + p.setShuffledPlayQueue(newShuffledQueue) + } else { + newPlayQueue := make([]mediaprovider.MediaItem, 0, p.getPlayQueueLength()-len(idxs)) + p.RemoveTracksFromQueueByIdx(idxs, &newPlayQueue, &newNowPlaying, &isPlayingTrackRemoved, &isNextPlayingTrackremoved) + p.setPlayQueue(newPlayQueue) } - //TODO_SHUFFLE: Removing a track should remove from both shuffled and unshuffled queue - p.setPlayQueue(newQueue) + p.nowPlayingIdx = newNowPlaying if isPlayingTrackRemoved { - if newNowPlaying == len(newQueue) { + if newNowPlaying == p.getPlayQueueLength() { // we had been playing the last track, and removed it p.Stop() } else { @@ -624,7 +600,7 @@ func (p *playbackEngine) RemoveTracksFromQueue(idxs []int) { // setNextTrack and onSongChange callbacks will be handled // when we receive new track event from player } else if isNextPlayingTrackremoved { - if newNowPlaying < len(newQueue)-1 { + if newNowPlaying < p.getPlayQueueLength()-1 { p.handleNextTrackUpdated() } else { // no next track to play @@ -635,6 +611,52 @@ func (p *playbackEngine) RemoveTracksFromQueue(idxs []int) { p.invokeNoArgCallbacks(p.onQueueChange) } +func (p *playbackEngine) RemoveTracksFromQueueByIdx(idxs []int, newQueue *[]mediaprovider.MediaItem, newNowPlaying *int, isPlayingTrackRemoved *bool, isNextPlayingTrackRemoved *bool) { + idxSet := sharedutil.ToSet(idxs) + nowPlaying := p.NowPlayingIndex() + + for i, tr := range p.getPlayQueue() { + if _, ok := idxSet[i]; ok { + if i < nowPlaying { + // if removing a track earlier than the currently playing one (if any), + // decrement new now playing index by one to account for new position in queue + *newNowPlaying-- + } else if i == nowPlaying { + *isPlayingTrackRemoved = true + // If we are removing the currently playing track, we need to scrobble it + p.checkScrobble() + p.alreadyScrobbled = true + } else if nowPlaying >= 0 && i == nowPlaying+1 { + *isNextPlayingTrackRemoved = true + } + } else { + // not removing this track + *newQueue = append(*newQueue, tr) + } + } +} + +func (p *playbackEngine) RemoveTracksFromQueueById(ids []string, newQueue *[]mediaprovider.MediaItem) { + for _, tr := range p.playQueue { //TODO_SHUFFLE: Rework this to use a func for 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) + ids = slices.Delete(ids, idx, idx+1) + } else { + // not removing this track + *newQueue = append(*newQueue, tr) + } + } +} + +func (p *playbackEngine) GetTrackIdsFromIdx(idx []int) []string { + ids := make([]string, 0, len(idx)) + for _, v := range idx { + ids = append(ids, p.getPlayQueueItemAt(v).Metadata().ID) + } + return ids +} + func (p *playbackEngine) SetReplayGainOptions(config ReplayGainConfig) { rGainPlayer, ok := p.player.(player.ReplayGainPlayer) if !ok {