From 5738d4732da5491b6491b852d3aa76ee28a9de4f Mon Sep 17 00:00:00 2001 From: Damontecres Date: Sun, 7 Dec 2025 22:36:22 -0500 Subject: [PATCH] Movie details improvements --- .../detail/discover/DiscoverMovieDetails.kt | 92 ++++++++----------- .../discover/DiscoverMovieDetailsHeader.kt | 16 +++- .../detail/discover/DiscoverMovieViewModel.kt | 55 ++++++----- 3 files changed, 86 insertions(+), 77 deletions(-) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverMovieDetails.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverMovieDetails.kt index 15dc236a..654228ff 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverMovieDetails.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverMovieDetails.kt @@ -36,8 +36,7 @@ import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.api.seerr.model.MovieDetails -import com.github.damontecres.wholphin.data.ExtrasItem -import com.github.damontecres.wholphin.data.model.BaseItem +import com.github.damontecres.wholphin.data.model.DiscoverItem import com.github.damontecres.wholphin.data.model.DiscoverRating import com.github.damontecres.wholphin.data.model.LocalTrailer import com.github.damontecres.wholphin.data.model.Person @@ -46,7 +45,7 @@ import com.github.damontecres.wholphin.data.model.Trailer import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.services.TrailerService import com.github.damontecres.wholphin.ui.Cards -import com.github.damontecres.wholphin.ui.cards.ExtrasRow +import com.github.damontecres.wholphin.ui.cards.DiscoverItemCard import com.github.damontecres.wholphin.ui.cards.ItemRow import com.github.damontecres.wholphin.ui.cards.PersonRow import com.github.damontecres.wholphin.ui.cards.SeasonCard @@ -59,7 +58,6 @@ import com.github.damontecres.wholphin.ui.data.AddPlaylistViewModel import com.github.damontecres.wholphin.ui.data.ItemDetailsDialog import com.github.damontecres.wholphin.ui.data.ItemDetailsDialogInfo import com.github.damontecres.wholphin.ui.detail.MoreDialogActions -import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForHome import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForPerson import com.github.damontecres.wholphin.ui.nav.Destination import com.github.damontecres.wholphin.ui.rememberInt @@ -88,8 +86,8 @@ fun DiscoverMovieDetails( val rating by viewModel.rating.observeAsState(null) val people by viewModel.people.observeAsState(listOf()) val trailers by viewModel.trailers.observeAsState(listOf()) - val extras by viewModel.extras.observeAsState(listOf()) val similar by viewModel.similar.observeAsState(listOf()) + val recommended by viewModel.similar.observeAsState(listOf()) val loading by viewModel.loading.observeAsState(LoadingState.Loading) var overviewDialog by remember { mutableStateOf(null) } @@ -121,14 +119,14 @@ fun DiscoverMovieDetails( movie = movie, rating = rating, people = people, - extras = extras, trailers = trailers, similar = similar, + recommended = recommended, requestOnClick = { // TODO seerr }, onClickItem = { index, item -> - viewModel.navigateTo(item.destination()) + viewModel.navigateTo(Destination.DiscoveredItem(item)) }, onClickPerson = { }, @@ -164,29 +162,10 @@ fun DiscoverMovieDetails( ) }, onLongClickSimilar = { index, similar -> - val items = - buildMoreDialogItemsForHome( - context = context, - item = similar, - seriesId = null, - playbackPosition = similar.playbackPosition, - watched = similar.played, - favorite = similar.favorite, - actions = moreActions, - ) - moreDialog = - DialogParams( - fromLongClick = true, - title = similar.title ?: "", - items = items, - ) }, trailerOnClick = { TrailerService.onClick(context, it, viewModel::navigateTo) }, - onClickExtra = { index, extra -> - viewModel.navigateTo(extra.destination) - }, modifier = modifier, ) } @@ -217,6 +196,7 @@ private const val TRAILER_ROW = PEOPLE_ROW + 1 private const val CHAPTER_ROW = TRAILER_ROW + 1 private const val EXTRAS_ROW = CHAPTER_ROW + 1 private const val SIMILAR_ROW = EXTRAS_ROW + 1 +private const val RECOMMENDED_ROW = SIMILAR_ROW + 1 @Composable fun DiscoverMovieDetailsContent( @@ -225,23 +205,22 @@ fun DiscoverMovieDetailsContent( rating: DiscoverRating?, people: List, trailers: List, - extras: List, - similar: List, + similar: List, + recommended: List, requestOnClick: () -> Unit, trailerOnClick: (Trailer) -> Unit, overviewOnClick: () -> Unit, moreOnClick: () -> Unit, - onClickItem: (Int, BaseItem) -> Unit, + onClickItem: (Int, DiscoverItem) -> Unit, onClickPerson: (Person) -> Unit, onLongClickPerson: (Int, Person) -> Unit, - onLongClickSimilar: (Int, BaseItem) -> Unit, - onClickExtra: (Int, ExtrasItem) -> Unit, + onLongClickSimilar: (Int, DiscoverItem) -> Unit, modifier: Modifier = Modifier, ) { val context = LocalContext.current val scope = rememberCoroutineScope() var position by rememberInt(0) - val focusRequesters = remember { List(SIMILAR_ROW + 1) { FocusRequester() } } + val focusRequesters = remember { List(RECOMMENDED_ROW + 1) { FocusRequester() } } val bringIntoViewRequester = remember { BringIntoViewRequester() } LaunchedEffect(Unit) { @@ -329,22 +308,6 @@ fun DiscoverMovieDetailsContent( } } - if (extras.isNotEmpty()) { - item { - ExtrasRow( - extras = extras, - onClickItem = { index, item -> - position = EXTRAS_ROW - onClickExtra.invoke(index, item) - }, - onLongClickItem = { _, _ -> }, - modifier = - Modifier - .fillMaxWidth() - .focusRequester(focusRequesters[EXTRAS_ROW]), - ) - } - } if (similar.isNotEmpty()) { item { ItemRow( @@ -359,14 +322,11 @@ fun DiscoverMovieDetailsContent( onLongClickSimilar.invoke(index, similar) }, cardContent = { index, item, mod, onClick, onLongClick -> - SeasonCard( + DiscoverItemCard( item = item, onClick = onClick, onLongClick = onLongClick, modifier = mod, - showImageOverlay = true, - imageHeight = Cards.height2x3, - imageWidth = Dp.Unspecified, ) }, modifier = @@ -376,6 +336,34 @@ fun DiscoverMovieDetailsContent( ) } } + if (recommended.isNotEmpty()) { + item { + ItemRow( + title = stringResource(R.string.recommended), + items = similar, + onClickItem = { index, item -> + position = RECOMMENDED_ROW + onClickItem.invoke(index, item) + }, + onLongClickItem = { index, similar -> + position = RECOMMENDED_ROW + onLongClickSimilar.invoke(index, similar) + }, + cardContent = { index, item, mod, onClick, onLongClick -> + DiscoverItemCard( + item = item, + onClick = onClick, + onLongClick = onLongClick, + modifier = mod, + ) + }, + modifier = + Modifier + .fillMaxWidth() + .focusRequester(focusRequesters[RECOMMENDED_ROW]), + ) + } + } } } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverMovieDetailsHeader.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverMovieDetailsHeader.kt index 6d24b7f7..c25f3ff0 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverMovieDetailsHeader.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverMovieDetailsHeader.kt @@ -29,6 +29,7 @@ import com.github.damontecres.wholphin.ui.letNotEmpty import com.github.damontecres.wholphin.ui.roundMinutes import com.github.damontecres.wholphin.util.ExceptionHandler import kotlinx.coroutines.launch +import java.util.Locale import kotlin.time.Duration.Companion.minutes @Composable @@ -71,9 +72,18 @@ fun DiscoverMovieDetailsHeader( ?.roundMinutes ?.toString() ?.let(::add) - movie.releases - ?.results - ?.firstOrNull() + val release = + movie.releases + ?.results + ?.firstOrNull { it.iso31661 == Locale.getDefault().country } + ?: movie.releases + ?.results + ?.firstOrNull { it.iso31661 == Locale.US.country } + ?: movie.releases + ?.results + ?.firstOrNull() + + release ?.releaseDates ?.firstOrNull() ?.certification diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverMovieViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverMovieViewModel.kt index 38742909..ad8193c2 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverMovieViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverMovieViewModel.kt @@ -5,17 +5,16 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.github.damontecres.wholphin.api.seerr.model.MovieDetails -import com.github.damontecres.wholphin.data.ChosenStreams -import com.github.damontecres.wholphin.data.ExtrasItem +import com.github.damontecres.wholphin.api.seerr.model.RelatedVideo import com.github.damontecres.wholphin.data.ServerRepository -import com.github.damontecres.wholphin.data.model.BaseItem -import com.github.damontecres.wholphin.data.model.Chapter import com.github.damontecres.wholphin.data.model.DiscoverItem import com.github.damontecres.wholphin.data.model.DiscoverRating import com.github.damontecres.wholphin.data.model.Person +import com.github.damontecres.wholphin.data.model.RemoteTrailer import com.github.damontecres.wholphin.data.model.Trailer import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.services.SeerrService +import com.github.damontecres.wholphin.ui.isNotNullOrBlank import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.nav.Destination import com.github.damontecres.wholphin.ui.setValueOnMain @@ -58,10 +57,8 @@ class DiscoverMovieViewModel val trailers = MutableLiveData>(listOf()) val people = MutableLiveData>(listOf()) - val chapters = MutableLiveData>(listOf()) - val extras = MutableLiveData>(listOf()) - val similar = MutableLiveData>() - val chosenStreams = MutableLiveData(null) + val similar = MutableLiveData>(listOf()) + val recommended = MutableLiveData>(listOf()) init { init() @@ -96,6 +93,26 @@ class DiscoverMovieViewModel val result = seerrService.api.moviesApi.movieMovieIdRatingsGet(movieId = item.id) rating.setValueOnMain(DiscoverRating(result)) } + if (!similar.isInitialized) { + viewModelScope.launchIO { + val result = + seerrService.api.moviesApi + .movieMovieIdSimilarGet(movieId = item.id, page = 2) + .results + ?.map(::DiscoverItem) + .orEmpty() + similar.setValueOnMain(result) + } + viewModelScope.launchIO { + val result = + seerrService.api.moviesApi + .movieMovieIdRecommendationsGet(movieId = item.id, page = 2) + .results + ?.map(::DiscoverItem) + .orEmpty() + similar.setValueOnMain(result) + } + } val people = movie.credits ?.cast @@ -122,20 +139,14 @@ class DiscoverMovieViewModel ) }.orEmpty() this@DiscoverMovieViewModel.people.setValueOnMain(people) - if (!similar.isInitialized) { -// val similar = -// api.libraryApi -// .getSimilarItems( -// GetSimilarItemsRequest( -// userId = serverRepository.currentUser.value?.id, -// itemId = itemId, -// fields = SlimItemFields, -// limit = 25, -// ), -// ).content.items -// .map { BaseItem.Companion.from(it, api) } -// this@DiscoverMovieViewModel.similar.setValueOnMain(similar) - } + val trailers = + movie.relatedVideos + ?.filter { it.type == RelatedVideo.Type.TRAILER } + ?.filter { it.name.isNotNullOrBlank() && it.url.isNotNullOrBlank() } + ?.map { + RemoteTrailer(it.name!!, it.url!!) + }.orEmpty() + this@DiscoverMovieViewModel.trailers.setValueOnMain(trailers) } fun navigateTo(destination: Destination) {