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
|
windowFiltered = false
|
||||||
lastPosition = null
|
lastPosition = null
|
||||||
lastEdit = null
|
lastEdit = null
|
||||||
|
clearDetached()
|
||||||
player?.run {
|
player?.run {
|
||||||
stop()
|
stop()
|
||||||
clear()
|
clear()
|
||||||
@@ -1674,7 +1675,17 @@ class PlaybackController(
|
|||||||
if (snapshot.size == 0L) return@withMutationLock
|
if (snapshot.size == 0L) return@withMutationLock
|
||||||
val current = currentQueuePosition() ?: return@withMutationLock
|
val current = currentQueuePosition() ?: return@withMutationLock
|
||||||
val detached = detachedWindow && currentPosition() == null
|
val detached = detachedWindow && currentPosition() == null
|
||||||
|
val detachedItem = if (detached) player.currentItem else null
|
||||||
windowJob?.cancel()
|
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 =
|
val target =
|
||||||
if (offline) {
|
if (offline) {
|
||||||
downloadedAtOrAfter(snapshot, current) ?: if (detached) {
|
downloadedAtOrAfter(snapshot, current) ?: if (detached) {
|
||||||
@@ -1686,6 +1697,13 @@ class PlaybackController(
|
|||||||
} else {
|
} else {
|
||||||
current
|
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)
|
queueRepository.setCursor(target)
|
||||||
if (preserveDetachedWindow(snapshot, target)) return@withMutationLock
|
if (preserveDetachedWindow(snapshot, target)) return@withMutationLock
|
||||||
if (target == current) {
|
if (target == current) {
|
||||||
|
|||||||
@@ -1777,6 +1777,25 @@ class PlaybackControllerTest {
|
|||||||
assertEquals(null, controller.state.value.item)
|
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
|
@Test
|
||||||
fun restoringAQueueFromAnotherSourceStopsIt() {
|
fun restoringAQueueFromAnotherSourceStopsIt() {
|
||||||
seedAlbum(3, sourceId = 1)
|
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
|
@Test
|
||||||
fun offlineNextCrossesTheLoadedWindowBoundary() {
|
fun offlineNextCrossesTheLoadedWindowBoundary() {
|
||||||
seedAlbum(30, sourceId = 1)
|
seedAlbum(30, sourceId = 1)
|
||||||
|
|||||||
Reference in New Issue
Block a user