diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/FocusedEpisodeHeader.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/FocusedEpisodeHeader.kt index 78775e03..77d7e386 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/FocusedEpisodeHeader.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/FocusedEpisodeHeader.kt @@ -24,18 +24,18 @@ import org.jellyfin.sdk.model.extensions.ticks @Composable fun FocusedEpisodeHeader( - ep: BaseItem, + ep: BaseItem?, overviewOnClick: () -> Unit, modifier: Modifier = Modifier, ) { val context = LocalContext.current - val dto = ep.data + val dto = ep?.data Column( verticalArrangement = Arrangement.spacedBy(4.dp), modifier = modifier, ) { Text( - text = dto.episodeTitle ?: dto.name ?: "", + text = dto?.episodeTitle ?: dto?.name ?: "", style = MaterialTheme.typography.headlineSmall, maxLines = 1, overflow = TextOverflow.Ellipsis, @@ -46,17 +46,17 @@ fun FocusedEpisodeHeader( ) { val details = buildList { - dto.seasonEpisode?.let(::add) - dto.premiereDate?.let { add(formatDateTime(it)) } - val duration = dto.runTimeTicks?.ticks + dto?.seasonEpisode?.let(::add) + dto?.premiereDate?.let { add(formatDateTime(it)) } + val duration = dto?.runTimeTicks?.ticks duration ?.roundMinutes ?.toString() ?.let { add(it) } - dto.officialRating?.let(::add) - dto.timeRemaining?.roundMinutes?.let { add("$it left") } + dto?.officialRating?.let(::add) + dto?.timeRemaining?.roundMinutes?.let { add("$it left") } } DotSeparatedRow( texts = details, @@ -68,12 +68,12 @@ fun FocusedEpisodeHeader( verticalAlignment = Alignment.CenterVertically, ) { SimpleStarRating( - dto.communityRating, + dto?.communityRating, Modifier.height(20.dp), ) } OverviewText( - overview = dto.overview ?: "", + overview = dto?.overview ?: "", maxLines = 3, onClick = overviewOnClick, ) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverviewContent.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverviewContent.kt index f2fde9f3..ce73e872 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverviewContent.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverviewContent.kt @@ -162,13 +162,11 @@ fun SeriesOverviewContent( } item { // Episode header - focusedEpisode?.let { ep -> - FocusedEpisodeHeader( - ep = ep, - overviewOnClick = overviewOnClick, - modifier = Modifier.fillMaxWidth(.66f), - ) - } + FocusedEpisodeHeader( + ep = focusedEpisode, + overviewOnClick = overviewOnClick, + modifier = Modifier.fillMaxWidth(.66f), + ) } item { key(position.seasonTabIndex) { diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt index 8acb295e..36b8b124 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt @@ -293,17 +293,11 @@ class SeriesViewModel itemId: UUID, listIndex: Int, ) = viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) { - val base = api.userLibraryApi.getItem(itemId).content - val item = BaseItem.Companion.from(base, api) val eps = episodes.value if (eps is EpisodeList.Success) { - val newItems = - eps.episodes.toMutableList().apply { - this[listIndex] = item - } - val newValue = eps.copy(episodes = newItems) + eps.episodes.refreshItem(listIndex, itemId) withContext(Dispatchers.Main) { - episodes.value = newValue + episodes.value = eps } } } @@ -409,7 +403,7 @@ sealed interface EpisodeList { } data class Success( - val episodes: List, + val episodes: ApiRequestPager, val initialIndex: Int, ) : EpisodeList } diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/ApiRequestPager.kt b/app/src/main/java/com/github/damontecres/wholphin/util/ApiRequestPager.kt index bde82618..2cc5bb3d 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/ApiRequestPager.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/util/ApiRequestPager.kt @@ -21,6 +21,7 @@ import org.jellyfin.sdk.api.client.extensions.liveTvApi import org.jellyfin.sdk.api.client.extensions.playlistsApi import org.jellyfin.sdk.api.client.extensions.suggestionsApi import org.jellyfin.sdk.api.client.extensions.tvShowsApi +import org.jellyfin.sdk.api.client.extensions.userLibraryApi import org.jellyfin.sdk.model.api.BaseItemDtoQueryResult import org.jellyfin.sdk.model.api.GetProgramsDto import org.jellyfin.sdk.model.api.request.GetEpisodesRequest @@ -31,6 +32,7 @@ import org.jellyfin.sdk.model.api.request.GetPlaylistItemsRequest import org.jellyfin.sdk.model.api.request.GetResumeItemsRequest import org.jellyfin.sdk.model.api.request.GetSuggestionsRequest import timber.log.Timber +import java.util.UUID import java.util.function.Predicate /** @@ -58,7 +60,7 @@ class ApiRequestPager( CacheBuilder .newBuilder() .maximumSize(cacheSize) - .build>() + .build>() suspend fun init(): ApiRequestPager { if (totalCount < 0) { @@ -122,13 +124,38 @@ class ApiRequestPager( false, ) val result = requestHandler.execute(api, newRequest).content - val data = result.items.map { BaseItem.from(it, api, useSeriesForPrimary) } + val data = mutableListOf() + result.items.forEach { data.add(BaseItem.from(it, api, useSeriesForPrimary)) } cachedPages.put(pageNumber, data) items = ItemList(totalCount, pageSize, cachedPages.asMap()) } } } + suspend fun refreshItem( + position: Int, + itemId: UUID, + ) { + val item = + api.userLibraryApi.getItem(itemId).content.let { + BaseItem.from( + it, + api, + useSeriesForPrimary, + ) + } + val pageNumber = position / pageSize + val index = position - pageNumber * pageSize + mutex.withLock { + val page = cachedPages.getIfPresent(pageNumber) + if (page != null && index in page.indices) { + page[index] = item + cachedPages.put(pageNumber, page) + items = ItemList(totalCount, pageSize, cachedPages.asMap()) + } + } + } + companion object { private const val DEBUG = false }