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)