From 724519a6faa9b56efb1fadc1baf8304b902c516a Mon Sep 17 00:00:00 2001 From: Damontecres Date: Tue, 21 Oct 2025 13:49:46 -0400 Subject: [PATCH] Add locking for refreshes --- .../ui/detail/livetv/LiveTvViewModel.kt | 150 +++++++++--------- 1 file changed, 77 insertions(+), 73 deletions(-) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/LiveTvViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/LiveTvViewModel.kt index 2bf96ef2..df629841 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/LiveTvViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/LiveTvViewModel.kt @@ -17,6 +17,8 @@ import com.github.damontecres.wholphin.util.LoadingState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import org.jellyfin.sdk.api.client.ApiClient import org.jellyfin.sdk.api.client.extensions.imageApi @@ -49,6 +51,7 @@ class LiveTvViewModel val start = LocalDateTime.now().truncatedTo(ChronoUnit.HOURS) private lateinit var channelsIdToIndex: Map + private val mutex = Mutex() val channels = MutableLiveData>() val programs = MutableLiveData>() @@ -87,82 +90,83 @@ class LiveTvViewModel } } - private suspend fun fetchPrograms(channels: List) { - val maxStartDate = start.plusHours(MAX_HOURS).minusMinutes(1) - val minEndDate = start.plusMinutes(1L) - val request = - GetProgramsDto( - maxStartDate = maxStartDate, - minEndDate = minEndDate, - channelIds = channels.map { it.id }, - sortBy = listOf(ItemSortBy.START_DATE), - ) - val programs = - GetProgramsDtoHandler - .execute(api, request) - .content.items - .map { dto -> - TvProgram( - id = dto.id, - channelId = dto.channelId!!, - start = dto.startDate!!, - end = dto.endDate!!, - startHours = hoursBetween(start, dto.startDate!!), - endHours = hoursBetween(start, dto.endDate!!), - duration = dto.runTimeTicks!!.ticks, - name = dto.seriesName ?: dto.name, - subtitle = dto.episodeTitle.takeIf { dto.isSeries ?: false }, - seasonEpisode = - if (dto.indexNumber != null && dto.parentIndexNumber != null) { - SeasonEpisode(dto.parentIndexNumber!!, dto.indexNumber!!) - } else { - null - }, - isRecording = dto.timerId.isNotNullOrBlank(), - isSeriesRecording = dto.seriesTimerId.isNotNullOrBlank(), - ) - }.filter { it.startHours >= 0 && it.endHours >= 0 } // TODO shouldn't need to filter client side + private suspend fun fetchPrograms(channels: List) = + mutex.withLock { + val maxStartDate = start.plusHours(MAX_HOURS).minusMinutes(1) + val minEndDate = start.plusMinutes(1L) + val request = + GetProgramsDto( + maxStartDate = maxStartDate, + minEndDate = minEndDate, + channelIds = channels.map { it.id }, + sortBy = listOf(ItemSortBy.START_DATE), + ) + val programs = + GetProgramsDtoHandler + .execute(api, request) + .content.items + .map { dto -> + TvProgram( + id = dto.id, + channelId = dto.channelId!!, + start = dto.startDate!!, + end = dto.endDate!!, + startHours = hoursBetween(start, dto.startDate!!), + endHours = hoursBetween(start, dto.endDate!!), + duration = dto.runTimeTicks!!.ticks, + name = dto.seriesName ?: dto.name, + subtitle = dto.episodeTitle.takeIf { dto.isSeries ?: false }, + seasonEpisode = + if (dto.indexNumber != null && dto.parentIndexNumber != null) { + SeasonEpisode(dto.parentIndexNumber!!, dto.indexNumber!!) + } else { + null + }, + isRecording = dto.timerId.isNotNullOrBlank(), + isSeriesRecording = dto.seriesTimerId.isNotNullOrBlank(), + ) + }.filter { it.startHours >= 0 && it.endHours >= 0 } // TODO shouldn't need to filter client side - val programsByChannel = programs.groupBy { it.channelId } - val emptyChannels = channels.filter { programsByChannel[it.id].orEmpty().isEmpty() } - val fake = mutableListOf() - val finalProgramsByChannel = - programsByChannel.toMutableMap().apply { - emptyChannels.forEach { channel -> - val fakePrograms = - (0..() + val finalProgramsByChannel = + programsByChannel.toMutableMap().apply { + emptyChannels.forEach { channel -> + val fakePrograms = + (0..