mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Show discovered items on movie & series pages
This commit is contained in:
parent
4d8d389582
commit
e7060cedfd
7 changed files with 135 additions and 14 deletions
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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) },
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue