diff --git a/app/src/main/java/com/github/damontecres/wholphin/services/SeerrService.kt b/app/src/main/java/com/github/damontecres/wholphin/services/SeerrService.kt index 18511a58..e38ba41b 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/services/SeerrService.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/services/SeerrService.kt @@ -50,6 +50,27 @@ class SeerrService ?.map(::DiscoverItem) .orEmpty() + suspend fun trending(page: Int = 1): List = + api.searchApi + .discoverTrendingGet(page = page) + .results + ?.map(::DiscoverItem) + .orEmpty() + + suspend fun upcomingMovies(page: Int = 1): List = + api.searchApi + .discoverMoviesUpcomingGet(page = page) + .results + ?.map(::DiscoverItem) + .orEmpty() + + suspend fun upcomingTv(page: Int = 1): List = + api.searchApi + .discoverTvUpcomingGet(page = page) + .results + ?.map(::DiscoverItem) + .orEmpty() + /** * Get [DiscoverItem]s similar to the JF items such as movies, series, or people * diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverQuickDetails.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverQuickDetails.kt new file mode 100644 index 00000000..458a1f43 --- /dev/null +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverQuickDetails.kt @@ -0,0 +1,38 @@ +package com.github.damontecres.wholphin.ui.detail.discover + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.tv.material3.MaterialTheme +import com.github.damontecres.wholphin.data.model.DiscoverItem +import com.github.damontecres.wholphin.data.model.DiscoverRating +import com.github.damontecres.wholphin.ui.components.DotSeparatedRow +import timber.log.Timber + +@Composable +fun DiscoverQuickDetails( + item: DiscoverItem?, + rating: DiscoverRating?, + modifier: Modifier = Modifier, +) { + Timber.v("id=${item?.id}, rating=$rating") + val context = LocalContext.current + val details = + remember(item) { + buildList { + item + ?.releaseDate + ?.year + ?.toString() + ?.let(::add) + } + } + DotSeparatedRow( + texts = details, + communityRating = rating?.audienceRating, + criticRating = rating?.criticRating?.toFloat(), + textStyle = MaterialTheme.typography.titleSmall, + modifier = modifier, + ) +} diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/discover/SeerrDiscoverPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/discover/SeerrDiscoverPage.kt index 38acd988..ac307f21 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/discover/SeerrDiscoverPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/discover/SeerrDiscoverPage.kt @@ -32,6 +32,8 @@ import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.DiscoverItem +import com.github.damontecres.wholphin.data.model.DiscoverRating +import com.github.damontecres.wholphin.data.model.SeerrItemType import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.services.BackdropService import com.github.damontecres.wholphin.services.NavigationManager @@ -40,11 +42,13 @@ import com.github.damontecres.wholphin.ui.cards.DiscoverItemCard import com.github.damontecres.wholphin.ui.cards.ItemRow import com.github.damontecres.wholphin.ui.components.ErrorMessage import com.github.damontecres.wholphin.ui.data.RowColumn +import com.github.damontecres.wholphin.ui.detail.discover.DiscoverQuickDetails import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.main.HomePageHeader import com.github.damontecres.wholphin.ui.rememberPosition import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.util.DataLoadingState +import com.google.common.cache.CacheBuilder import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow @@ -63,6 +67,7 @@ class SeerrDiscoverViewModel private val backdropService: BackdropService, ) : ViewModel() { val state = MutableStateFlow(DiscoverState()) + val rating = MutableStateFlow>(mapOf()) init { viewModelScope.launchIO { @@ -74,6 +79,21 @@ class SeerrDiscoverViewModel fetchAndUpdateState(seerrService::discoverTv) { this.copy(tv = DiscoverRowData(context.getString(R.string.tv_shows), it)) } + fetchAndUpdateState(seerrService::trending) { + this.copy(trending = DiscoverRowData(context.getString(R.string.trending), it)) + } + fetchAndUpdateState(seerrService::upcomingMovies) { + this.copy( + upcomingMovies = + DiscoverRowData(context.getString(R.string.upcoming_movies), it), + ) + } + fetchAndUpdateState(seerrService::upcomingTv) { + this.copy( + upcomingTv = + DiscoverRowData(context.getString(R.string.upcoming_tv), it), + ) + } } private fun fetchAndUpdateState( @@ -101,6 +121,49 @@ class SeerrDiscoverViewModel viewModelScope.launchIO { if (item != null) { backdropService.submit("discover_${item.id}", item.backDropUrl) + fetchRating(item) + } + } + } + + private val ratingCache = + CacheBuilder + .newBuilder() + .maximumSize(100) + .build() + + // TODO this is not very efficient + fun fetchRating(item: DiscoverItem) { + viewModelScope.launchIO { + val cachedResult = ratingCache.getIfPresent(item.id) + if (cachedResult != null) { + return@launchIO + } + val result = + when (item.type) { + SeerrItemType.MOVIE -> { + DiscoverRating( + seerrService.api.moviesApi.movieMovieIdRatingsGet( + movieId = item.id, + ), + ) + } + + SeerrItemType.TV -> { + DiscoverRating(seerrService.api.tvApi.tvTvIdRatingsGet(tvId = item.id)) + } + + SeerrItemType.PERSON -> { + DiscoverRating(null, null) + } + + SeerrItemType.UNKNOWN -> { + DiscoverRating(null, null) + } + } + ratingCache.put(item.id, result) + rating.update { + ratingCache.asMap().toMap() } } } @@ -118,6 +181,9 @@ data class DiscoverRowData( data class DiscoverState( val movies: DiscoverRowData = DiscoverRowData.EMPTY, val tv: DiscoverRowData = DiscoverRowData.EMPTY, + val trending: DiscoverRowData = DiscoverRowData.EMPTY, + val upcomingMovies: DiscoverRowData = DiscoverRowData.EMPTY, + val upcomingTv: DiscoverRowData = DiscoverRowData.EMPTY, ) @Composable @@ -127,7 +193,9 @@ fun SeerrDiscoverPage( viewModel: SeerrDiscoverViewModel = hiltViewModel(), ) { val state by viewModel.state.collectAsState() - val rows = listOf(state.movies, state.tv) + val rows = + listOf(state.trending, state.movies, state.tv, state.upcomingMovies, state.upcomingTv) + val ratingMap by viewModel.rating.collectAsState() val focusRequesters = remember(2) { List(rows.size) { FocusRequester() } } var position by rememberPosition(0, -1) @@ -141,8 +209,8 @@ fun SeerrDiscoverPage( viewModel.updateBackdrop(focusedItem) } var firstFocused by rememberSaveable { mutableStateOf(false) } - LaunchedEffect(state.movies) { - if (!firstFocused && state.movies.items is DataLoadingState.Success<*>) { + LaunchedEffect(state.trending) { + if (!firstFocused && state.trending.items is DataLoadingState.Success<*>) { firstFocused = focusRequesters.getOrNull(0)?.tryRequestFocus("discover") == true } } @@ -156,12 +224,15 @@ fun SeerrDiscoverPage( overview = focusedItem?.overview, overviewTwoLines = true, quickDetails = { - // TODO + DiscoverQuickDetails( + item = focusedItem, + rating = focusedItem?.id?.let { ratingMap[it] }, + ) }, modifier = Modifier - .fillMaxHeight(.33f) - .padding(top = 48.dp, bottom = 32.dp, start = 32.dp), + .padding(top = 24.dp, bottom = 16.dp, start = 32.dp) + .fillMaxHeight(.25f), ) LazyColumn( verticalArrangement = Arrangement.spacedBy(8.dp), diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/nav/NavDrawer.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/nav/NavDrawer.kt index 62ebba0d..10c48451 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/nav/NavDrawer.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/nav/NavDrawer.kt @@ -640,8 +640,7 @@ fun NavigationDrawerScope.NavItem( } NavDrawerItem.Discover -> { - // TODO seerr - R.string.fa_list_ul + R.string.fa_magnifying_glass_plus } is ServerNavDrawerItem -> { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c5fef869..73d5777e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -428,6 +428,9 @@ Password Username URL + Trending + Upcoming Movies + Upcoming TV Shows Disabled