Show discovered items on movie & series pages

This commit is contained in:
Damontecres 2026-01-06 17:24:50 -05:00
parent 4d8d389582
commit e7060cedfd
No known key found for this signature in database
7 changed files with 135 additions and 14 deletions

View file

@ -9,6 +9,7 @@ import com.github.damontecres.wholphin.api.seerr.model.TvDetails
import com.github.damontecres.wholphin.api.seerr.model.TvResult import com.github.damontecres.wholphin.api.seerr.model.TvResult
import com.github.damontecres.wholphin.api.seerr.model.TvTvIdRatingsGet200Response import com.github.damontecres.wholphin.api.seerr.model.TvTvIdRatingsGet200Response
import com.github.damontecres.wholphin.services.SeerrSearchResult import com.github.damontecres.wholphin.services.SeerrSearchResult
import com.github.damontecres.wholphin.ui.nav.Destination
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
@ -78,6 +79,25 @@ data class DiscoverItem(
val backDropUrl: String? get() = backdropPath?.let { "https://image.tmdb.org/t/p/w1920_and_h800_multi_faces$it" } val backDropUrl: String? get() = backdropPath?.let { "https://image.tmdb.org/t/p/w1920_and_h800_multi_faces$it" }
val posterUrl: String? get() = posterPath?.let { "https://image.tmdb.org/t/p/w500$it" } val posterUrl: String? get() = posterPath?.let { "https://image.tmdb.org/t/p/w500$it" }
val destination: Destination
get() {
val jfType =
when (type) {
SeerrItemType.MOVIE -> BaseItemKind.MOVIE
SeerrItemType.TV -> BaseItemKind.SERIES
SeerrItemType.PERSON -> BaseItemKind.PERSON
SeerrItemType.UNKNOWN -> null
}
return if (jellyfinItemId != null && jfType != null) {
Destination.MediaItem(
itemId = jellyfinItemId,
type = jfType,
)
} else {
Destination.DiscoveredItem(this)
}
}
constructor(movie: MovieResult) : this( constructor(movie: MovieResult) : this(
id = movie.id, id = movie.id,
type = SeerrItemType.MOVIE, type = SeerrItemType.MOVIE,

View file

@ -2,7 +2,9 @@ package com.github.damontecres.wholphin.services
import com.github.damontecres.wholphin.api.seerr.SeerrApiClient import com.github.damontecres.wholphin.api.seerr.SeerrApiClient
import com.github.damontecres.wholphin.api.seerr.model.SearchGet200ResponseResultsInner import com.github.damontecres.wholphin.api.seerr.model.SearchGet200ResponseResultsInner
import com.github.damontecres.wholphin.data.model.BaseItem
import com.github.damontecres.wholphin.data.model.DiscoverItem import com.github.damontecres.wholphin.data.model.DiscoverItem
import org.jellyfin.sdk.model.api.BaseItemKind
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -40,4 +42,34 @@ class SeerrService
.results .results
?.map(::DiscoverItem) ?.map(::DiscoverItem)
.orEmpty() .orEmpty()
suspend fun similar(item: BaseItem): List<DiscoverItem> =
if (active) {
item.data.providerIds
?.get("Tmdb")
?.toIntOrNull()
?.let {
when (item.type) {
BaseItemKind.MOVIE -> {
api.moviesApi
.movieMovieIdSimilarGet(movieId = it)
.results
?.map(::DiscoverItem)
}
BaseItemKind.SERIES, BaseItemKind.SEASON, BaseItemKind.EPISODE -> {
api.tvApi
.tvTvIdSimilarGet(tvId = it)
.results
?.map(::DiscoverItem)
}
else -> {
null
}
}
}.orEmpty()
} else {
emptyList()
}
} }

View file

@ -11,6 +11,7 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.relocation.BringIntoViewRequester import androidx.compose.foundation.relocation.BringIntoViewRequester
import androidx.compose.foundation.relocation.bringIntoViewRequester import androidx.compose.foundation.relocation.bringIntoViewRequester
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -31,6 +32,7 @@ import com.github.damontecres.wholphin.data.ChosenStreams
import com.github.damontecres.wholphin.data.ExtrasItem import com.github.damontecres.wholphin.data.ExtrasItem
import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.data.model.BaseItem
import com.github.damontecres.wholphin.data.model.Chapter import com.github.damontecres.wholphin.data.model.Chapter
import com.github.damontecres.wholphin.data.model.DiscoverItem
import com.github.damontecres.wholphin.data.model.Person import com.github.damontecres.wholphin.data.model.Person
import com.github.damontecres.wholphin.data.model.Trailer import com.github.damontecres.wholphin.data.model.Trailer
import com.github.damontecres.wholphin.data.model.aspectRatioFloat import com.github.damontecres.wholphin.data.model.aspectRatioFloat
@ -61,8 +63,11 @@ import com.github.damontecres.wholphin.ui.detail.PlaylistLoadingState
import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItems import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItems
import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForHome import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForHome
import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForPerson import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForPerson
import com.github.damontecres.wholphin.ui.discover.DiscoverRow
import com.github.damontecres.wholphin.ui.discover.DiscoverRowData
import com.github.damontecres.wholphin.ui.nav.Destination import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.rememberInt import com.github.damontecres.wholphin.ui.rememberInt
import com.github.damontecres.wholphin.util.DataLoadingState
import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.LoadingState import com.github.damontecres.wholphin.util.LoadingState
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -98,6 +103,7 @@ fun MovieDetails(
val similar by viewModel.similar.observeAsState(listOf()) val similar by viewModel.similar.observeAsState(listOf())
val loading by viewModel.loading.observeAsState(LoadingState.Loading) val loading by viewModel.loading.observeAsState(LoadingState.Loading)
val chosenStreams by viewModel.chosenStreams.observeAsState(null) val chosenStreams by viewModel.chosenStreams.observeAsState(null)
val discovered by viewModel.discovered.collectAsState()
var overviewDialog by remember { mutableStateOf<ItemDetailsDialogInfo?>(null) } var overviewDialog by remember { mutableStateOf<ItemDetailsDialogInfo?>(null) }
var moreDialog by remember { mutableStateOf<DialogParams?>(null) } var moreDialog by remember { mutableStateOf<DialogParams?>(null) }
@ -291,6 +297,10 @@ fun MovieDetails(
onClickExtra = { index, extra -> onClickExtra = { index, extra ->
viewModel.navigateTo(extra.destination) viewModel.navigateTo(extra.destination)
}, },
discovered = discovered,
onClickDiscover = { index, item ->
viewModel.navigateTo(item.destination)
},
modifier = modifier, modifier = modifier,
) )
} }
@ -351,6 +361,7 @@ private const val TRAILER_ROW = PEOPLE_ROW + 1
private const val CHAPTER_ROW = TRAILER_ROW + 1 private const val CHAPTER_ROW = TRAILER_ROW + 1
private const val EXTRAS_ROW = CHAPTER_ROW + 1 private const val EXTRAS_ROW = CHAPTER_ROW + 1
private const val SIMILAR_ROW = EXTRAS_ROW + 1 private const val SIMILAR_ROW = EXTRAS_ROW + 1
private const val DISCOVER_ROW = SIMILAR_ROW + 1
@Composable @Composable
fun MovieDetailsContent( fun MovieDetailsContent(
@ -362,6 +373,7 @@ fun MovieDetailsContent(
trailers: List<Trailer>, trailers: List<Trailer>,
extras: List<ExtrasItem>, extras: List<ExtrasItem>,
similar: List<BaseItem>, similar: List<BaseItem>,
discovered: List<DiscoverItem>,
playOnClick: (Duration) -> Unit, playOnClick: (Duration) -> Unit,
trailerOnClick: (Trailer) -> Unit, trailerOnClick: (Trailer) -> Unit,
overviewOnClick: () -> Unit, overviewOnClick: () -> Unit,
@ -373,12 +385,13 @@ fun MovieDetailsContent(
onLongClickPerson: (Int, Person) -> Unit, onLongClickPerson: (Int, Person) -> Unit,
onLongClickSimilar: (Int, BaseItem) -> Unit, onLongClickSimilar: (Int, BaseItem) -> Unit,
onClickExtra: (Int, ExtrasItem) -> Unit, onClickExtra: (Int, ExtrasItem) -> Unit,
onClickDiscover: (Int, DiscoverItem) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val context = LocalContext.current val context = LocalContext.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
var position by rememberInt(0) var position by rememberInt(0)
val focusRequesters = remember { List(SIMILAR_ROW + 1) { FocusRequester() } } val focusRequesters = remember { List(DISCOVER_ROW + 1) { FocusRequester() } }
val dto = movie.data val dto = movie.data
val resumePosition = dto.userData?.playbackPositionTicks?.ticks ?: Duration.ZERO val resumePosition = dto.userData?.playbackPositionTicks?.ticks ?: Duration.ZERO
@ -526,6 +539,24 @@ fun MovieDetailsContent(
) )
} }
} }
if (discovered.isNotEmpty()) {
item {
DiscoverRow(
row =
DiscoverRowData(
stringResource(R.string.discover),
DataLoadingState.Success(discovered),
),
onClickItem = { index: Int, item: DiscoverItem ->
position = DISCOVER_ROW
onClickDiscover.invoke(index, item)
},
onLongClickItem = { _, _ -> },
onCardFocus = {},
focusRequester = focusRequesters[DISCOVER_ROW],
)
}
}
} }
} }
} }

View file

@ -10,6 +10,7 @@ import com.github.damontecres.wholphin.data.ItemPlaybackRepository
import com.github.damontecres.wholphin.data.ServerRepository import com.github.damontecres.wholphin.data.ServerRepository
import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.data.model.BaseItem
import com.github.damontecres.wholphin.data.model.Chapter import com.github.damontecres.wholphin.data.model.Chapter
import com.github.damontecres.wholphin.data.model.DiscoverItem
import com.github.damontecres.wholphin.data.model.ItemPlayback import com.github.damontecres.wholphin.data.model.ItemPlayback
import com.github.damontecres.wholphin.data.model.Person import com.github.damontecres.wholphin.data.model.Person
import com.github.damontecres.wholphin.data.model.Trailer import com.github.damontecres.wholphin.data.model.Trailer
@ -19,6 +20,7 @@ import com.github.damontecres.wholphin.services.ExtrasService
import com.github.damontecres.wholphin.services.FavoriteWatchManager import com.github.damontecres.wholphin.services.FavoriteWatchManager
import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.services.NavigationManager
import com.github.damontecres.wholphin.services.PeopleFavorites import com.github.damontecres.wholphin.services.PeopleFavorites
import com.github.damontecres.wholphin.services.SeerrService
import com.github.damontecres.wholphin.services.StreamChoiceService import com.github.damontecres.wholphin.services.StreamChoiceService
import com.github.damontecres.wholphin.services.ThemeSongPlayer import com.github.damontecres.wholphin.services.ThemeSongPlayer
import com.github.damontecres.wholphin.services.TrailerService import com.github.damontecres.wholphin.services.TrailerService
@ -40,6 +42,8 @@ import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.jellyfin.sdk.api.client.ApiClient import org.jellyfin.sdk.api.client.ApiClient
@ -54,6 +58,7 @@ class MovieViewModel
@AssistedInject @AssistedInject
constructor( constructor(
private val api: ApiClient, private val api: ApiClient,
private val seerrService: SeerrService,
@param:ApplicationContext private val context: Context, @param:ApplicationContext private val context: Context,
private val navigationManager: NavigationManager, private val navigationManager: NavigationManager,
val serverRepository: ServerRepository, val serverRepository: ServerRepository,
@ -81,6 +86,7 @@ class MovieViewModel
val extras = MutableLiveData<List<ExtrasItem>>(listOf()) val extras = MutableLiveData<List<ExtrasItem>>(listOf())
val similar = MutableLiveData<List<BaseItem>>() val similar = MutableLiveData<List<BaseItem>>()
val chosenStreams = MutableLiveData<ChosenStreams?>(null) val chosenStreams = MutableLiveData<ChosenStreams?>(null)
val discovered = MutableStateFlow<List<DiscoverItem>>(listOf())
init { init {
init() init()
@ -140,6 +146,10 @@ class MovieViewModel
val extras = extrasService.getExtras(item.id) val extras = extrasService.getExtras(item.id)
this@MovieViewModel.extras.setValueOnMain(extras) this@MovieViewModel.extras.setValueOnMain(extras)
} }
viewModelScope.launchIO {
val results = seerrService.similar(item)
discovered.update { results }
}
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
chapters.value = Chapter.fromDto(item.data, api) chapters.value = Chapter.fromDto(item.data, api)

View file

@ -16,6 +16,7 @@ import androidx.compose.foundation.relocation.bringIntoViewRequester
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -40,6 +41,7 @@ import androidx.tv.material3.Text
import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.data.ExtrasItem import com.github.damontecres.wholphin.data.ExtrasItem
import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.data.model.BaseItem
import com.github.damontecres.wholphin.data.model.DiscoverItem
import com.github.damontecres.wholphin.data.model.Person import com.github.damontecres.wholphin.data.model.Person
import com.github.damontecres.wholphin.data.model.Trailer import com.github.damontecres.wholphin.data.model.Trailer
import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.preferences.UserPreferences
@ -71,9 +73,12 @@ import com.github.damontecres.wholphin.ui.detail.PlaylistDialog
import com.github.damontecres.wholphin.ui.detail.PlaylistLoadingState import com.github.damontecres.wholphin.ui.detail.PlaylistLoadingState
import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForHome import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForHome
import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForPerson import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForPerson
import com.github.damontecres.wholphin.ui.discover.DiscoverRow
import com.github.damontecres.wholphin.ui.discover.DiscoverRowData
import com.github.damontecres.wholphin.ui.letNotEmpty import com.github.damontecres.wholphin.ui.letNotEmpty
import com.github.damontecres.wholphin.ui.nav.Destination import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.rememberInt import com.github.damontecres.wholphin.ui.rememberInt
import com.github.damontecres.wholphin.util.DataLoadingState
import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.LoadingState import com.github.damontecres.wholphin.util.LoadingState
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -104,6 +109,7 @@ fun SeriesDetails(
val extras by viewModel.extras.observeAsState(listOf()) val extras by viewModel.extras.observeAsState(listOf())
val people by viewModel.people.observeAsState(listOf()) val people by viewModel.people.observeAsState(listOf())
val similar by viewModel.similar.observeAsState(listOf()) val similar by viewModel.similar.observeAsState(listOf())
val discovered by viewModel.discovered.collectAsState()
var overviewDialog by remember { mutableStateOf<ItemDetailsDialogInfo?>(null) } var overviewDialog by remember { mutableStateOf<ItemDetailsDialogInfo?>(null) }
var showWatchConfirmation by remember { mutableStateOf(false) } var showWatchConfirmation by remember { mutableStateOf(false) }
@ -208,6 +214,10 @@ fun SeriesDetails(
onClickExtra = { _, extra -> onClickExtra = { _, extra ->
viewModel.navigateTo(extra.destination) viewModel.navigateTo(extra.destination)
}, },
discovered = discovered,
onClickDiscover = { index, item ->
viewModel.navigateTo(item.destination)
},
moreActions = moreActions =
MoreDialogActions( MoreDialogActions(
navigateTo = { viewModel.navigateTo(it) }, navigateTo = { viewModel.navigateTo(it) },
@ -281,6 +291,7 @@ private const val PEOPLE_ROW = SEASONS_ROW + 1
private const val TRAILER_ROW = PEOPLE_ROW + 1 private const val TRAILER_ROW = PEOPLE_ROW + 1
private const val EXTRAS_ROW = TRAILER_ROW + 1 private const val EXTRAS_ROW = TRAILER_ROW + 1
private const val SIMILAR_ROW = EXTRAS_ROW + 1 private const val SIMILAR_ROW = EXTRAS_ROW + 1
private const val DISCOVER_ROW = SIMILAR_ROW + 1
@Composable @Composable
fun SeriesDetailsContent( fun SeriesDetailsContent(
@ -291,6 +302,7 @@ fun SeriesDetailsContent(
trailers: List<Trailer>, trailers: List<Trailer>,
extras: List<ExtrasItem>, extras: List<ExtrasItem>,
people: List<Person>, people: List<Person>,
discovered: List<DiscoverItem>,
played: Boolean, played: Boolean,
favorite: Boolean, favorite: Boolean,
onClickItem: (Int, BaseItem) -> Unit, onClickItem: (Int, BaseItem) -> Unit,
@ -303,6 +315,7 @@ fun SeriesDetailsContent(
trailerOnClick: (Trailer) -> Unit, trailerOnClick: (Trailer) -> Unit,
onClickExtra: (Int, ExtrasItem) -> Unit, onClickExtra: (Int, ExtrasItem) -> Unit,
moreActions: MoreDialogActions, moreActions: MoreDialogActions,
onClickDiscover: (Int, DiscoverItem) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val context = LocalContext.current val context = LocalContext.current
@ -310,7 +323,7 @@ fun SeriesDetailsContent(
val bringIntoViewRequester = remember { BringIntoViewRequester() } val bringIntoViewRequester = remember { BringIntoViewRequester() }
var position by rememberInt() var position by rememberInt()
val focusRequesters = remember { List(SIMILAR_ROW + 1) { FocusRequester() } } val focusRequesters = remember { List(DISCOVER_ROW + 1) { FocusRequester() } }
val playFocusRequester = remember { FocusRequester() } val playFocusRequester = remember { FocusRequester() }
RequestOrRestoreFocus(focusRequesters.getOrNull(position)) RequestOrRestoreFocus(focusRequesters.getOrNull(position))
var moreDialog by remember { mutableStateOf<DialogParams?>(null) } var moreDialog by remember { mutableStateOf<DialogParams?>(null) }
@ -546,6 +559,24 @@ fun SeriesDetailsContent(
) )
} }
} }
if (discovered.isNotEmpty()) {
item {
DiscoverRow(
row =
DiscoverRowData(
stringResource(R.string.discover),
DataLoadingState.Success(discovered),
),
onClickItem = { index: Int, item: DiscoverItem ->
position = DISCOVER_ROW
onClickDiscover.invoke(index, item)
},
onLongClickItem = { _, _ -> },
onCardFocus = {},
focusRequester = focusRequesters[DISCOVER_ROW],
)
}
}
} }
} }
} }

View file

@ -9,6 +9,7 @@ import com.github.damontecres.wholphin.data.ExtrasItem
import com.github.damontecres.wholphin.data.ItemPlaybackRepository import com.github.damontecres.wholphin.data.ItemPlaybackRepository
import com.github.damontecres.wholphin.data.ServerRepository import com.github.damontecres.wholphin.data.ServerRepository
import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.data.model.BaseItem
import com.github.damontecres.wholphin.data.model.DiscoverItem
import com.github.damontecres.wholphin.data.model.ItemPlayback import com.github.damontecres.wholphin.data.model.ItemPlayback
import com.github.damontecres.wholphin.data.model.Person import com.github.damontecres.wholphin.data.model.Person
import com.github.damontecres.wholphin.data.model.Trailer import com.github.damontecres.wholphin.data.model.Trailer
@ -18,6 +19,7 @@ import com.github.damontecres.wholphin.services.ExtrasService
import com.github.damontecres.wholphin.services.FavoriteWatchManager import com.github.damontecres.wholphin.services.FavoriteWatchManager
import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.services.NavigationManager
import com.github.damontecres.wholphin.services.PeopleFavorites import com.github.damontecres.wholphin.services.PeopleFavorites
import com.github.damontecres.wholphin.services.SeerrService
import com.github.damontecres.wholphin.services.StreamChoiceService import com.github.damontecres.wholphin.services.StreamChoiceService
import com.github.damontecres.wholphin.services.ThemeSongPlayer import com.github.damontecres.wholphin.services.ThemeSongPlayer
import com.github.damontecres.wholphin.services.TrailerService import com.github.damontecres.wholphin.services.TrailerService
@ -84,6 +86,7 @@ class SeriesViewModel
val streamChoiceService: StreamChoiceService, val streamChoiceService: StreamChoiceService,
private val userPreferencesService: UserPreferencesService, private val userPreferencesService: UserPreferencesService,
private val backdropService: BackdropService, private val backdropService: BackdropService,
private val seerrService: SeerrService,
@Assisted val seriesId: UUID, @Assisted val seriesId: UUID,
@Assisted val seasonEpisodeIds: SeasonEpisodeIds?, @Assisted val seasonEpisodeIds: SeasonEpisodeIds?,
@Assisted val seriesPageType: SeriesPageType, @Assisted val seriesPageType: SeriesPageType,
@ -107,6 +110,7 @@ class SeriesViewModel
val similar = MutableLiveData<List<BaseItem>>() val similar = MutableLiveData<List<BaseItem>>()
val peopleInEpisode = MutableLiveData<PeopleInItem>(PeopleInItem()) val peopleInEpisode = MutableLiveData<PeopleInItem>(PeopleInItem())
val discovered = MutableStateFlow<List<DiscoverItem>>(listOf())
val position = MutableStateFlow(SeriesOverviewPosition(0, 0)) val position = MutableStateFlow(SeriesOverviewPosition(0, 0))
@ -210,6 +214,10 @@ class SeriesViewModel
this@SeriesViewModel.similar.setValueOnMain(similar) this@SeriesViewModel.similar.setValueOnMain(similar)
} }
} }
viewModelScope.launchIO {
val results = seerrService.similar(item)
discovered.update { results }
}
} }
} }
} }

View file

@ -42,7 +42,6 @@ import com.github.damontecres.wholphin.ui.components.ErrorMessage
import com.github.damontecres.wholphin.ui.data.RowColumn import com.github.damontecres.wholphin.ui.data.RowColumn
import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.main.HomePageHeader import com.github.damontecres.wholphin.ui.main.HomePageHeader
import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.rememberPosition import com.github.damontecres.wholphin.ui.rememberPosition
import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.ui.tryRequestFocus
import com.github.damontecres.wholphin.util.DataLoadingState import com.github.damontecres.wholphin.util.DataLoadingState
@ -51,7 +50,6 @@ import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import org.jellyfin.sdk.api.client.ApiClient import org.jellyfin.sdk.api.client.ApiClient
import org.jellyfin.sdk.model.api.BaseItemKind
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
@ -178,16 +176,7 @@ fun SeerrDiscoverPage(
row = row, row = row,
onClickItem = { index, item -> onClickItem = { index, item ->
position = RowColumn(rowIndex, index) position = RowColumn(rowIndex, index)
if (item.jellyfinItemId != null) { viewModel.navigationManager.navigateTo(item.destination)
viewModel.navigationManager.navigateTo(
Destination.MediaItem(
itemId = item.jellyfinItemId,
type = BaseItemKind.MOVIE,
),
)
} else {
viewModel.navigationManager.navigateTo(Destination.DiscoveredItem(item))
}
}, },
onLongClickItem = { index, item -> }, onLongClickItem = { index, item -> },
onCardFocus = { index -> position = RowColumn(rowIndex, index) }, onCardFocus = { index -> position = RowColumn(rowIndex, index) },