From 8c56994056ebbd5fa74b20443e2a4714e399eb67 Mon Sep 17 00:00:00 2001 From: Toby Bridle Date: Sat, 11 Nov 2023 01:04:11 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=89=20Handle=20Seasons=20given?= =?UTF-8?q?=20by=20Eplist=20Handler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/screens/info/InfoView.kt | 31 +++++-- .../ui/screens/info/InfoViewModel.kt | 81 ++++++++++++++++--- 2 files changed, 94 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/chouten/app/presentation/ui/screens/info/InfoView.kt b/app/src/main/java/com/chouten/app/presentation/ui/screens/info/InfoView.kt index 7be453d..e00cc06 100644 --- a/app/src/main/java/com/chouten/app/presentation/ui/screens/info/InfoView.kt +++ b/app/src/main/java/com/chouten/app/presentation/ui/screens/info/InfoView.kt @@ -104,17 +104,30 @@ fun InfoView( val infoResults by infoViewModel.infoResults.collectAsState() val episodeList by infoViewModel.episodeList.collectAsState() - LaunchedEffect(infoResults) { + val selectedSeason by infoViewModel.selectedSeason.collectAsState() + var lastUrl by rememberSaveable { mutableStateOf(url) } + + LaunchedEffect(infoResults, selectedSeason) { when (infoResults) { is Resource.Success -> { - if (infoResults.data?.epListURLs?.isNotEmpty() == true) { - val urls = infoResults.data?.epListURLs ?: listOf() - infoViewModel.getEpisodes(urls, 0) - } + val urls = infoResults.data?.epListURLs ?: listOf() + selectedSeason?.let { + if ((lastUrl != it.url) && (infoViewModel.getMediaList().find { media -> + media.title == it.name + } == null) + ) { + infoViewModel.getInfo(title, it.url) + lastUrl = it.url + } else { + // Make sure that we don't reload the episodes if we already have them + if (episodeList is Resource.Uninitialized) + infoViewModel.getEpisodes(urls, 0) + } + } ?: infoViewModel.getEpisodes(urls, 0) } is Resource.Uninitialized -> { - infoViewModel.getInfo(title, infoViewModel.selectedSeason?.url ?: url) + infoViewModel.getInfo(title, selectedSeason?.url ?: url) } is Resource.Error -> { @@ -332,7 +345,7 @@ fun InfoView( isSeasonDropdown = it }, modifier = Modifier.fillMaxWidth()) { TextField( - value = infoViewModel.selectedSeason?.name ?: "Season 1", + value = selectedSeason?.name ?: "Season 1", onValueChange = {}, readOnly = true, trailingIcon = { @@ -390,7 +403,9 @@ fun InfoView( verticalArrangement = Arrangement.spacedBy(20.dp) ) { itemsIndexed( - items = episodeList.data?.getOrNull(0)?.list ?: listOf() + items = infoViewModel.getMediaList() + .find { it.title == selectedSeason?.name }?.list + ?: episodeList.data?.getOrNull(0)?.list ?: listOf() ) { index, item -> EpisodeItem(item, infoResults.data?.poster ?: "", diff --git a/app/src/main/java/com/chouten/app/presentation/ui/screens/info/InfoViewModel.kt b/app/src/main/java/com/chouten/app/presentation/ui/screens/info/InfoViewModel.kt index 4dafe77..9d6e03a 100644 --- a/app/src/main/java/com/chouten/app/presentation/ui/screens/info/InfoViewModel.kt +++ b/app/src/main/java/com/chouten/app/presentation/ui/screens/info/InfoViewModel.kt @@ -14,9 +14,11 @@ import com.chouten.app.domain.use_case.log_use_cases.LogUseCases import com.chouten.app.domain.use_case.module_use_cases.ModuleUseCases import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import kotlinx.parcelize.Parcelize import kotlinx.serialization.Serializable @@ -96,6 +98,17 @@ class InfoViewModel @Inject constructor( val episodeList: StateFlow>> = savedStateHandle.getStateFlow("epListResults", Resource.Uninitialized()) + /** + * The list of media items. Made from concatenating the [infoResults] and [episodeList] data + */ + fun getMediaList(): List { + return runBlocking { + infoResults.firstOrNull()?.data?.mediaList?.plus( + episodeList.firstOrNull()?.data ?: listOf() + ) ?: listOf() + } + } + /** * Whether or not the episode list has been paginated to the end * NOTE: This does NOT mean that the webview itself has finished returning @@ -110,8 +123,8 @@ class InfoViewModel @Inject constructor( var paginatedAll = false private set - var selectedSeason: InfoResult.Season? = null - private set + private var _selectedSeason: MutableStateFlow = MutableStateFlow(null) + val selectedSeason: StateFlow = _selectedSeason var seasonCount = 0 private set @@ -149,10 +162,19 @@ class InfoViewModel @Inject constructor( return@initialize } viewModelScope.launch { - if (selectedSeason == null) selectedSeason = + if (_selectedSeason.firstOrNull() == null) _selectedSeason.emit( res.result.result.seasons?.firstOrNull() + ) seasonCount = res.result.result.seasons?.size ?: 1 - savedStateHandle["infoResults"] = Resource.Success(res.result.result) + if (infoResults.firstOrNull() is Resource.Success) { + savedStateHandle["infoResults"] = Resource.Success( + infoResults.firstOrNull()?.data?.copy( + epListURLs = res.result.result.epListURLs + ) + ) + } else { + savedStateHandle["infoResults"] = Resource.Success(res.result.result) + } } } @@ -198,6 +220,28 @@ class InfoViewModel @Inject constructor( savedStateHandle.get>>("epListResults")?.data?.toMutableSet() ?: mutableSetOf() episodes.addAll(res.result.result) + if (episodes.size > 1) { + val collectedInfoResults = infoResults.firstOrNull()?.data + collectedInfoResults?.let { + savedStateHandle["infoResults"] = Resource.Success( + it.copy( + seasons = (it.seasons?.plus(episodes.mapIndexed { index, season -> + val resultSeason = InfoResult.Season( + name = season.title, + url = season.list.firstOrNull()?.url ?: "" + ) + if (it.seasons.size.plus( + it.mediaList?.size ?: 0 + ) == 0 && index == 0 + ) { + _selectedSeason.emit(resultSeason) + } + resultSeason + })?.toSet()?.toList()) + ) + ) + } + } savedStateHandle["epListResults"] = Resource.Success(episodes.toList()) if (_paginatedAll) { paginatedAll = true @@ -218,9 +262,20 @@ class InfoViewModel @Inject constructor( } fun changeSeason(season: InfoResult.Season) { - selectedSeason = infoResults.value.data?.seasons?.find { it == season } - savedStateHandle["infoResults"] = Resource.Uninitialized() - savedStateHandle["epListResults"] = Resource.Uninitialized>() + viewModelScope.launch { + if (season == selectedSeason.firstOrNull()) return@launch + _selectedSeason.emit(infoResults.value.data?.seasons?.find { it == season }) + // If the media doesn't appear to have been loaded, request it using the season url + if (getMediaList().find { it.title == season.name } == null) { + savedStateHandle["infoResults"] = Resource.Success( + infoResults.firstOrNull()?.data?.copy( + epListURLs = listOf(season.url) + ) + ) + savedStateHandle["epListResults"] = + Resource.Uninitialized>() + } + } } fun log(title: String = "Webview Handler", content: String) { @@ -234,9 +289,15 @@ class InfoViewModel @Inject constructor( } suspend fun saveMediaBundle() { - val media = infoResults.firstOrNull()?.data?.mediaList?.plus( - episodeList.firstOrNull()?.data ?: emptyList() - ) ?: emptyList() + val season = selectedSeason.firstOrNull() + val media = getMediaList().sortedBy { + // We want the selected season to be the first index + if (it.title == season?.name) { + 0 + } else { + 1 + } + } withContext(Dispatchers.IO) { application.applicationContext.cacheDir.resolve("${FILE_PREFIX}_media.json") .bufferedWriter().use {