From 902f556fe6faf57f9e06272cb11905b9e053964b Mon Sep 17 00:00:00 2001 From: austinried <4966622+austinried@users.noreply.github.com> Date: Sun, 27 Sep 2026 05:15:03 +0000 Subject: [PATCH] advance past a duplicate song when the playing queued track is deleted Removing the currently playing track resolved its id against the updated queue afterwards; if the same song also exists in the context, the lookup matched that copy and jumped the cursor to it instead of advancing. When the removed position was the playing one, use the fallback (next in the block / slot fill) rather than a by-id lookup. Pre-existing on main, exposed while testing the shuffle rework. Regression test fails without the fix. --- .../subtracks/playback/PlaybackController.kt | 20 +++++++++++-- .../playback/PlaybackControllerTest.kt | 29 +++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/app/src/main/kotlin/com/subtracks/playback/PlaybackController.kt b/app/src/main/kotlin/com/subtracks/playback/PlaybackController.kt index 421b564e..157f4ada 100644 --- a/app/src/main/kotlin/com/subtracks/playback/PlaybackController.kt +++ b/app/src/main/kotlin/com/subtracks/playback/PlaybackController.kt @@ -350,9 +350,23 @@ class PlaybackController( } var target = ( - currentId?.let { - if (wasUpNext) queueRepository.combinedIndexOf(updated, it) else queueRepository.combinedContextIndexOf(updated, it) - } ?: fallback + if (position == current) { + // The playing entry was removed; resolving its song id afterwards can match + // an identical song elsewhere (a duplicate in the context or the block), so + // advance from the fallback instead. + fallback + } else { + currentId?.let { + if (wasUpNext) { + queueRepository.combinedIndexOf( + updated, + it, + ) + } else { + queueRepository.combinedContextIndexOf(updated, it) + } + } ?: fallback + } ).coerceIn(0, updated.size - 1) // Removing the playing context track while shuffled leaves the block anchored to some // other track; re-anchor it to the track the cursor landed on so the queued tracks diff --git a/app/src/test/kotlin/com/subtracks/playback/PlaybackControllerTest.kt b/app/src/test/kotlin/com/subtracks/playback/PlaybackControllerTest.kt index fb58d03e..b15d439f 100644 --- a/app/src/test/kotlin/com/subtracks/playback/PlaybackControllerTest.kt +++ b/app/src/test/kotlin/com/subtracks/playback/PlaybackControllerTest.kt @@ -461,6 +461,35 @@ class PlaybackControllerTest { } } + @Test + fun removingThePlayingQueuedTrackAdvancesPastADuplicateInTheContext() { + seedAlbum(4, sourceId = 1) + seedSong("y1", "al2") + + controller.playAlbum(1, "al1", 0) + await { + controller.state.value.item + ?.id == "s1" + } + // "s2" is both queued and in the context; removing the queued copy must advance to the + // next queued track, not jump to the identical song in the context. + controller.addToQueue(1, QueueKind.Song, "s2") + controller.addToQueue(1, QueueKind.Song, "y1") + await { runBlocking { controller.upcomingItem()?.id } == "s2" } + controller.playAt(1) + await { + controller.state.value.item + ?.id == "s2" + } + + runBlocking { controller.removeAt(1) } + + await { + controller.state.value.item + ?.id == "y1" + } + } + @Test fun playNextFromInsideTheBlockGoesAfterTheCurrentTrack() { seedAlbum(3, sourceId = 1)