mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-15 00:42:54 +01:00
playback: reconcile detached items during offline transitions
This commit is contained in:
@@ -1192,6 +1192,7 @@ class PlaybackController(
|
||||
windowFiltered = false
|
||||
lastPosition = null
|
||||
lastEdit = null
|
||||
clearDetached()
|
||||
player?.run {
|
||||
stop()
|
||||
clear()
|
||||
@@ -1674,7 +1675,17 @@ class PlaybackController(
|
||||
if (snapshot.size == 0L) return@withMutationLock
|
||||
val current = currentQueuePosition() ?: return@withMutationLock
|
||||
val detached = detachedWindow && currentPosition() == null
|
||||
val detachedItem = if (detached) player.currentItem else null
|
||||
windowJob?.cancel()
|
||||
if (offline && detachedItem != null && downloads.localUri(detachedItem.id) == null) {
|
||||
val target = downloadedAtOrAfter(snapshot, current)
|
||||
if (target == null) {
|
||||
stopLocked()
|
||||
} else {
|
||||
loadWindow(target, autoplay = player.playWhenReady, startPositionMs = 0)
|
||||
}
|
||||
return@withMutationLock
|
||||
}
|
||||
val target =
|
||||
if (offline) {
|
||||
downloadedAtOrAfter(snapshot, current) ?: if (detached) {
|
||||
@@ -1686,6 +1697,13 @@ class PlaybackController(
|
||||
} else {
|
||||
current
|
||||
}
|
||||
if (detachedItem != null) {
|
||||
val isLocal = player.itemUris().getOrNull(player.currentIndex)?.startsWith(FILE_SCHEME) == true
|
||||
if (!isLocal) {
|
||||
val replacement = queueItemAt(snapshot, current)?.takeIf { it.id == detachedItem.id } ?: detachedItem
|
||||
player.replaceItem(player.currentIndex, replacement)
|
||||
}
|
||||
}
|
||||
queueRepository.setCursor(target)
|
||||
if (preserveDetachedWindow(snapshot, target)) return@withMutationLock
|
||||
if (target == current) {
|
||||
|
||||
@@ -1777,6 +1777,25 @@ class PlaybackControllerTest {
|
||||
assertEquals(null, controller.state.value.item)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun stoppingWhileDetachedClearsItsIdentityAndLaterPosition() {
|
||||
seedPlaylist(3)
|
||||
controller.playPlaylist(1, "p1", 1)
|
||||
await { handle.currentItem?.id == "s2" }
|
||||
val point = runBlocking { queues.captureResetPoint() }
|
||||
setPlaylist("s1", "s3")
|
||||
runBlocking { queues.resetChangedOrigins(setOf(QueueOrigin(QueueKind.Playlist, 1, "p1")), point) }
|
||||
await { handle.currentItem?.id == "s2" && handle.items.any { it.id == "s3" } }
|
||||
|
||||
controller.playAlbum(1, "missing", 0)
|
||||
await { handle.operations.contains("clear") }
|
||||
controller.cycleRepeat()
|
||||
handle.emitEvents()
|
||||
|
||||
assertNull(controller.state.value.item)
|
||||
assertNull(controller.state.value.position)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun restoringAQueueFromAnotherSourceStopsIt() {
|
||||
seedAlbum(3, sourceId = 1)
|
||||
@@ -3394,6 +3413,57 @@ class PlaybackControllerTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun offlineDetachedCurrentThatIsNotDownloadedMovesToTheSurvivor() {
|
||||
seedPlaylist(3)
|
||||
markDownloaded(1, "s3")
|
||||
controller.playPlaylist(1, "p1", 1)
|
||||
await { handle.currentItem?.id == "s2" }
|
||||
val point = runBlocking { queues.captureResetPoint() }
|
||||
setPlaylist("s1", "s3")
|
||||
runBlocking { queues.resetChangedOrigins(setOf(QueueOrigin(QueueKind.Playlist, 1, "p1")), point) }
|
||||
await { handle.currentItem?.id == "s2" && handle.items.any { it.id == "s3" } }
|
||||
|
||||
setOffline()
|
||||
|
||||
await { handle.currentItem?.id == "s3" }
|
||||
assertEquals(1L, controller.state.value.position)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun offlineDetachedCurrentWithoutADownloadedSurvivorStops() {
|
||||
seedPlaylist(3)
|
||||
controller.playPlaylist(1, "p1", 1)
|
||||
await { handle.currentItem?.id == "s2" }
|
||||
val point = runBlocking { queues.captureResetPoint() }
|
||||
setPlaylist("s1", "s3")
|
||||
runBlocking { queues.resetChangedOrigins(setOf(QueueOrigin(QueueKind.Playlist, 1, "p1")), point) }
|
||||
await { handle.currentItem?.id == "s2" && handle.items.any { it.id == "s3" } }
|
||||
|
||||
setOffline()
|
||||
|
||||
await { controller.state.value.item == null && handle.itemCount == 0 }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun offlineDetachedCurrentThatWasDownloadedReplacesItsStreamUri() {
|
||||
seedPlaylist(3)
|
||||
markDownloaded(1, "s2")
|
||||
markDownloaded(1, "s3")
|
||||
controller.playPlaylist(1, "p1", 1)
|
||||
await { handle.currentItem?.id == "s2" }
|
||||
val point = runBlocking { queues.captureResetPoint() }
|
||||
setPlaylist("s1", "s3")
|
||||
runBlocking { queues.resetChangedOrigins(setOf(QueueOrigin(QueueKind.Playlist, 1, "p1")), point) }
|
||||
await { handle.currentItem?.id == "s2" && handle.items.any { it.id == "s3" } }
|
||||
handle.operations.clear()
|
||||
|
||||
setOffline()
|
||||
|
||||
await { handle.operations.contains("replaceItem(0, s2)") }
|
||||
assertEquals("s2", handle.currentItem?.id)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun offlineNextCrossesTheLoadedWindowBoundary() {
|
||||
seedAlbum(30, sourceId = 1)
|
||||
|
||||
Reference in New Issue
Block a user