Preload next image during slideshow (#1847)

## Description
When viewing images in a slideshow, this PR enqueues the next image so
it will likely be cached and ready to display when the slideshow
advances.

This works for both automatic and manual scrolling as well as advancing
next or previous images.

### Related issues
Closes #1835 

### Testing
Emulator with large photos

## Screenshots
N/A

## AI or LLM usage
None
This commit is contained in:
Damontecres 2026-08-16 10:20:46 -04:00 • committed by GitHub
parent 22a949bc76
commit 4e53cbaa7f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -6,6 +6,9 @@ import androidx.compose.runtime.Stable
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.media3.common.Player
import coil3.imageLoader
import coil3.request.ImageRequest
import coil3.size.Size
import com.github.damontecres.wholphin.data.ChosenStreams
import com.github.damontecres.wholphin.data.PlaybackEffectDao
import com.github.damontecres.wholphin.data.ServerRepository
@ -172,7 +175,7 @@ class SlideshowViewModel
val size = state.value.items.size
val newPosition = state.value.position + 1
return if (newPosition < size) {
updatePosition(newPosition)
updatePosition(newPosition, true)
true
} else {
false
@ -182,14 +185,23 @@ class SlideshowViewModel
fun previousImage(): Boolean {
val newPosition = state.value.position - 1
return if (newPosition >= 0) {
updatePosition(newPosition)
updatePosition(newPosition, false)
true
} else {
false
}
}
fun updatePosition(position: Int): Job? =
/**
* Update image state to the specified position
*
* @param position the index to use
* @param isNext whether moving to the next or previous image for prefetching purposes
*/
fun updatePosition(
position: Int,
isNext: Boolean = true,
): Job? =
(state.value.items as? ApiRequestPager<*>)?.let { pager ->
viewModelScope.launchIO {
try {
@ -286,6 +298,31 @@ class SlideshowViewModel
)
}
}
try {
// If there is a next/previous image, fetch it into the image cache
val nextIndex = position + if (isNext) 1 else -1
if (nextIndex in pager.indices) {
pager.getBlocking(nextIndex)?.let { nextImage ->
if (nextImage.data.mediaType == MediaType.PHOTO) {
val url = api.libraryApi.getDownloadUrl(nextImage.id)
val request =
ImageRequest
.Builder(context)
.data(url)
.size(Size.ORIGINAL)
.build()
context.imageLoader.enqueue(request)
Timber.v(
"Prefetching index=%s, id=%s",
nextIndex,
nextImage.id,
)
}
}
}
} catch (ex: Exception) {
Timber.e(ex, "Error preloading image")
}
}
}
@ -339,7 +376,7 @@ class SlideshowViewModel
fun pulseSlideshow() = pulseSlideshow(slideshowDelay)
fun pulseSlideshow(milliseconds: Long) {
Timber.v("pulseSlideshow $milliseconds")
// Timber.v("pulseSlideshow $milliseconds")
slideshowJob?.cancel()
slideshowJob =
viewModelScope