address fourth review: reset the saved position per track and serialize cursor writes

This commit is contained in:
austinried
2026-10-04 00:29:55 +00:00
parent 20ab30920f
commit e7f8032250
4 changed files with 45 additions and 11 deletions
@@ -10,6 +10,8 @@ import com.subtracks.data.model.QueueEntry
import com.subtracks.data.model.QueueKind
import com.subtracks.data.model.ShuffleOrder
import com.subtracks.data.model.SongListItem
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import java.util.concurrent.atomic.AtomicLong
const val QUEUE_CHUNK = 60
@@ -71,6 +73,8 @@ class QueueRepository(
@Volatile
private var cachedFlatVersion = -1L
private val cursorMutex = Mutex()
fun playlistEntry(
sourceId: Long,
playlistId: String,
@@ -94,7 +98,7 @@ class QueueRepository(
) {
write(entries)
setShuffle(shuffleOrder != null, shuffleOrder)
dao.setCursor(cursorRow().copy(queuePosition = 0))
cursorMutex.withLock { dao.setCursor(cursorRow().copy(queuePosition = 0, positionMs = 0)) }
}
suspend fun snapshot(): QueueSnapshot {
@@ -124,13 +128,15 @@ class QueueRepository(
order: LongArray?,
) {
val seed = if (enabled && order != null) System.nanoTime() else 0L
db.useWriterConnection { transactor ->
transactor.immediateTransaction {
dao.clearShuffleOrder()
if (enabled && order != null) {
dao.insertShuffleOrder(order.mapIndexed { index, flat -> ShuffleOrder(index.toLong(), flat) })
cursorMutex.withLock {
db.useWriterConnection { transactor ->
transactor.immediateTransaction {
dao.clearShuffleOrder()
if (enabled && order != null) {
dao.insertShuffleOrder(order.mapIndexed { index, flat -> ShuffleOrder(index.toLong(), flat) })
}
dao.setCursor(cursorRow().copy(shuffleEnabled = enabled, shuffleSeed = seed))
}
dao.setCursor(cursorRow().copy(shuffleEnabled = enabled, shuffleSeed = seed))
}
}
cachedShuffleSeed = 0
@@ -142,7 +148,7 @@ class QueueRepository(
queueVersion.incrementAndGet()
}
suspend fun setRepeat(mode: Int) = dao.setCursor(cursorRow().copy(repeatMode = mode))
suspend fun setRepeat(mode: Int) = cursorMutex.withLock { dao.setCursor(cursorRow().copy(repeatMode = mode)) }
private suspend fun loadShuffleOrder(
seed: Long,
@@ -272,9 +278,16 @@ class QueueRepository(
suspend fun cursorPositionMs(): Long = dao.cursor()?.positionMs ?: 0
suspend fun setPosition(positionMs: Long) = dao.setCursor(cursorRow().copy(positionMs = positionMs.coerceAtLeast(0)))
suspend fun setPosition(positionMs: Long) =
cursorMutex.withLock {
dao.setCursor(cursorRow().copy(positionMs = positionMs.coerceAtLeast(0)))
}
suspend fun setCursor(position: Long) = dao.setCursor(cursorRow().copy(queuePosition = position))
suspend fun setCursor(position: Long) =
cursorMutex.withLock {
val row = cursorRow()
dao.setCursor(row.copy(queuePosition = position, positionMs = if (row.queuePosition == position) row.positionMs else 0))
}
private suspend fun cursorRow(): PlaybackCursor = dao.cursor() ?: PlaybackCursor(queuePosition = 0)
@@ -234,6 +234,7 @@ class PlaybackController(
val order = withContext(Dispatchers.Default) { shuffledOrder(snap.size, start) }
queueRepository.setShuffle(true, order)
shuffleEnabled = true
queueSourceId = entry.sourceId
queueRepository.setCursor(0)
snap = queueRepository.snapshot()
this@PlaybackController.snapshot = snap
@@ -116,7 +116,7 @@ class QueueViewModel(
rows.addAll(queueRepository.range(snapshot, start, end).map(::newRow))
first = rows.firstOrNull()?.position ?: 0
last = rows.lastOrNull()?.position ?: -1
initialIndex = (cursor - first).toInt().coerceAtLeast(0)
initialIndex = rows.indexOfFirst { it.position == cursor }.coerceAtLeast(0)
}
ready = true
generation++
@@ -932,6 +932,26 @@ class PlaybackControllerTest {
restored.close()
}
@Test
fun changingTrackResetsTheSavedPosition() {
seedAlbum(3, sourceId = 1)
controller.playAlbum(1, "al1", 0)
await {
controller.state.value.item
?.id == "s1"
}
runBlocking { queues.setPosition(60_000L) }
controller.next()
await {
controller.state.value.item
?.id == "s2"
}
assertEquals(0L, runBlocking { queues.cursorPositionMs() })
}
private fun seedAlbum(
count: Int,
sourceId: Long,