refactor: refine TV detail cast rows and player queue behavior

Temporarily hide cast rows on TV movie, episode, and series detail surfaces while preserving the surrounding section layout.

Simplify TV player queue rows by removing the secondary subtitle line so queued items scan more cleanly.

Increase the next-up queue fetch from 5 to 10 items and skip missing playlist episodes with an error log instead of throwing during queue mapping.
This commit is contained in:
2026-04-25 18:40:46 +02:00
parent f7f9fc1058
commit 78a5f716c0
7 changed files with 43 additions and 51 deletions

View File

@@ -13,21 +13,20 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import hu.bbara.purefin.ui.common.media.MediaCastRow import hu.bbara.purefin.feature.content.episode.EpisodeScreenViewModel
import hu.bbara.purefin.ui.screen.waiting.PurefinWaitingScreen import hu.bbara.purefin.image.ArtworkKind
import hu.bbara.purefin.image.ImageUrlBuilder
import hu.bbara.purefin.model.Episode
import hu.bbara.purefin.navigation.EpisodeDto
import hu.bbara.purefin.navigation.LocalNavigationManager
import hu.bbara.purefin.navigation.Route
import hu.bbara.purefin.ui.common.media.MediaDetailOverviewSection import hu.bbara.purefin.ui.common.media.MediaDetailOverviewSection
import hu.bbara.purefin.ui.common.media.MediaDetailPlaybackSection import hu.bbara.purefin.ui.common.media.MediaDetailPlaybackSection
import hu.bbara.purefin.ui.common.media.MediaDetailSectionTitle import hu.bbara.purefin.ui.common.media.MediaDetailSectionTitle
import hu.bbara.purefin.ui.common.media.TvMediaDetailBodyBox import hu.bbara.purefin.ui.common.media.TvMediaDetailBodyBox
import hu.bbara.purefin.ui.common.media.TvMediaDetailScaffold import hu.bbara.purefin.ui.common.media.TvMediaDetailScaffold
import hu.bbara.purefin.ui.screen.episode.components.TvEpisodeHeroSection import hu.bbara.purefin.ui.screen.episode.components.TvEpisodeHeroSection
import hu.bbara.purefin.image.ImageUrlBuilder import hu.bbara.purefin.ui.screen.waiting.PurefinWaitingScreen
import hu.bbara.purefin.navigation.EpisodeDto
import hu.bbara.purefin.navigation.LocalNavigationManager
import hu.bbara.purefin.navigation.Route
import hu.bbara.purefin.model.Episode
import hu.bbara.purefin.feature.content.episode.EpisodeScreenViewModel
import hu.bbara.purefin.image.ArtworkKind
@Composable @Composable
fun TvEpisodeScreen( fun TvEpisodeScreen(
@@ -126,7 +125,7 @@ internal fun TvEpisodeScreenContent(
Column(modifier = it.fillMaxWidth()) { Column(modifier = it.fillMaxWidth()) {
MediaDetailSectionTitle(text = "Cast") MediaDetailSectionTitle(text = "Cast")
Spacer(modifier = Modifier.height(14.dp)) Spacer(modifier = Modifier.height(14.dp))
MediaCastRow(cast = episode.cast) // MediaCastRow(cast = episode.cast)
Spacer(modifier = Modifier.height(24.dp)) Spacer(modifier = Modifier.height(24.dp))
} }
} }

View File

@@ -13,8 +13,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import hu.bbara.purefin.ui.common.media.MediaCastRow import hu.bbara.purefin.feature.content.movie.MovieScreenViewModel
import hu.bbara.purefin.ui.screen.waiting.PurefinWaitingScreen import hu.bbara.purefin.model.Movie
import hu.bbara.purefin.navigation.MovieDto
import hu.bbara.purefin.ui.common.media.MediaDetailOverviewSection import hu.bbara.purefin.ui.common.media.MediaDetailOverviewSection
import hu.bbara.purefin.ui.common.media.MediaDetailPlaybackSection import hu.bbara.purefin.ui.common.media.MediaDetailPlaybackSection
import hu.bbara.purefin.ui.common.media.MediaDetailSectionTitle import hu.bbara.purefin.ui.common.media.MediaDetailSectionTitle
@@ -22,9 +23,7 @@ import hu.bbara.purefin.ui.common.media.TvMediaDetailBodyBox
import hu.bbara.purefin.ui.common.media.TvMediaDetailScaffold import hu.bbara.purefin.ui.common.media.TvMediaDetailScaffold
import hu.bbara.purefin.ui.common.media.tvMediaDetailBackgroundImageUrl import hu.bbara.purefin.ui.common.media.tvMediaDetailBackgroundImageUrl
import hu.bbara.purefin.ui.screen.movie.components.TvMovieHeroSection import hu.bbara.purefin.ui.screen.movie.components.TvMovieHeroSection
import hu.bbara.purefin.navigation.MovieDto import hu.bbara.purefin.ui.screen.waiting.PurefinWaitingScreen
import hu.bbara.purefin.model.Movie
import hu.bbara.purefin.feature.content.movie.MovieScreenViewModel
@Composable @Composable
fun TvMovieScreen( fun TvMovieScreen(
@@ -91,8 +90,8 @@ internal fun TvMovieScreenContent(
item(key = "movie-playback") { item(key = "movie-playback") {
Column(modifier = it.fillMaxWidth()) { Column(modifier = it.fillMaxWidth()) {
MediaDetailPlaybackSection( MediaDetailPlaybackSection(
audioTrack = movie.audioTrack, audioTrack = "ENG",
subtitles = movie.subtitles, subtitles = "ENG",
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
Spacer(modifier = Modifier.height(20.dp)) Spacer(modifier = Modifier.height(20.dp))
@@ -103,7 +102,7 @@ internal fun TvMovieScreenContent(
Column(modifier = it.fillMaxWidth()) { Column(modifier = it.fillMaxWidth()) {
MediaDetailSectionTitle(text = "Cast") MediaDetailSectionTitle(text = "Cast")
Spacer(modifier = Modifier.height(14.dp)) Spacer(modifier = Modifier.height(14.dp))
MediaCastRow(cast = movie.cast) // MediaCastRow(cast = movie.cast)
Spacer(modifier = Modifier.height(24.dp)) Spacer(modifier = Modifier.height(24.dp))
} }
} }

View File

@@ -231,15 +231,6 @@ private fun TvQueueRowCard(
maxLines = 2, maxLines = 2,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
item.subtitle?.takeIf { it.isNotBlank() }?.let { subtitle ->
Text(
text = subtitle,
color = scheme.onSurfaceVariant,
style = MaterialTheme.typography.bodySmall,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
} }
} }
} }

View File

@@ -10,12 +10,16 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.withFrameNanos import androidx.compose.runtime.withFrameNanos
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import hu.bbara.purefin.ui.screen.waiting.PurefinWaitingScreen import hu.bbara.purefin.feature.content.series.SeriesViewModel
import hu.bbara.purefin.model.Season
import hu.bbara.purefin.model.Series
import hu.bbara.purefin.navigation.SeriesDto
import hu.bbara.purefin.ui.common.media.MediaDetailOverviewSection import hu.bbara.purefin.ui.common.media.MediaDetailOverviewSection
import hu.bbara.purefin.ui.common.media.MediaDetailSectionTitle import hu.bbara.purefin.ui.common.media.MediaDetailSectionTitle
import hu.bbara.purefin.ui.common.media.TvMediaDetailBodyBox import hu.bbara.purefin.ui.common.media.TvMediaDetailBodyBox
@@ -26,10 +30,7 @@ import hu.bbara.purefin.ui.screen.series.components.SeriesFirstSeasonTabTag
import hu.bbara.purefin.ui.screen.series.components.TvEpisodeCarousel import hu.bbara.purefin.ui.screen.series.components.TvEpisodeCarousel
import hu.bbara.purefin.ui.screen.series.components.TvSeasonTabs import hu.bbara.purefin.ui.screen.series.components.TvSeasonTabs
import hu.bbara.purefin.ui.screen.series.components.TvSeriesHeroSection import hu.bbara.purefin.ui.screen.series.components.TvSeriesHeroSection
import hu.bbara.purefin.navigation.SeriesDto import hu.bbara.purefin.ui.screen.waiting.PurefinWaitingScreen
import hu.bbara.purefin.model.Season
import hu.bbara.purefin.model.Series
import hu.bbara.purefin.feature.content.series.SeriesViewModel
import java.util.UUID import java.util.UUID
@Composable @Composable

View File

@@ -20,6 +20,7 @@ import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
@@ -50,21 +51,20 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import hu.bbara.purefin.ui.common.media.MediaCastRow import hu.bbara.purefin.feature.content.series.SeriesViewModel
import hu.bbara.purefin.ui.common.bar.MediaProgressBar import hu.bbara.purefin.image.ArtworkKind
import hu.bbara.purefin.ui.common.button.MediaResumeButton
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
import hu.bbara.purefin.ui.common.badge.WatchStateBadge
import hu.bbara.purefin.ui.common.media.MediaMetadataFlowRow
import hu.bbara.purefin.ui.common.media.mediaPlaybackProgress
import hu.bbara.purefin.ui.common.media.mediaPlayButtonText
import hu.bbara.purefin.image.ImageUrlBuilder import hu.bbara.purefin.image.ImageUrlBuilder
import hu.bbara.purefin.model.CastMember import hu.bbara.purefin.model.CastMember
import hu.bbara.purefin.model.Episode import hu.bbara.purefin.model.Episode
import hu.bbara.purefin.model.Season import hu.bbara.purefin.model.Season
import hu.bbara.purefin.model.Series import hu.bbara.purefin.model.Series
import hu.bbara.purefin.feature.content.series.SeriesViewModel import hu.bbara.purefin.ui.common.badge.WatchStateBadge
import hu.bbara.purefin.image.ArtworkKind import hu.bbara.purefin.ui.common.bar.MediaProgressBar
import hu.bbara.purefin.ui.common.button.MediaResumeButton
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
import hu.bbara.purefin.ui.common.media.MediaMetadataFlowRow
import hu.bbara.purefin.ui.common.media.mediaPlayButtonText
import hu.bbara.purefin.ui.common.media.mediaPlaybackProgress
internal const val SeriesPlayButtonTag = "series-play-button" internal const val SeriesPlayButtonTag = "series-play-button"
internal const val SeriesFirstSeasonTabTag = "series-first-season-tab" internal const val SeriesFirstSeasonTabTag = "series-first-season-tab"
@@ -367,13 +367,13 @@ private fun TvEpisodeCard(
@Composable @Composable
internal fun CastRow(cast: List<CastMember>, modifier: Modifier = Modifier) { internal fun CastRow(cast: List<CastMember>, modifier: Modifier = Modifier) {
MediaCastRow( // MediaCastRow(
cast = cast, // cast = cast,
modifier = modifier, // modifier = modifier,
cardWidth = 84.dp, // cardWidth = 84.dp,
nameSize = 11.sp, // nameSize = 11.sp,
roleSize = 10.sp // roleSize = 10.sp
) // )
} }
private fun Episode.heroStatusText(): String { private fun Episode.heroStatusText(): String {

View File

@@ -157,7 +157,7 @@ class PlayerManager @Inject constructor(
val nextUpPlayableMedias = playableMediaRepository.getNextUpPlayableMedias( val nextUpPlayableMedias = playableMediaRepository.getNextUpPlayableMedias(
episodeId = _currentMediaId.value ?: return, episodeId = _currentMediaId.value ?: return,
existingIds = _playlist.value.map { it.id }.toSet(), existingIds = _playlist.value.map { it.id }.toSet(),
count = 5 count = 10
) )
addToQueue(nextUpPlayableMedias) addToQueue(nextUpPlayableMedias)
} }

View File

@@ -1,5 +1,6 @@
package hu.bbara.purefin.player.viewmodel package hu.bbara.purefin.player.viewmodel
import android.util.Log
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
@@ -126,7 +127,8 @@ class PlayerViewModel @Inject constructor(
queue = episodes.mapNotNull { episode -> queue = episodes.mapNotNull { episode ->
val episodeValue = episode.first() val episodeValue = episode.first()
if (episodeValue == null) { if (episodeValue == null) {
throw IllegalStateException("Episode not found for media id: $episodeValue") Log.e("PlayerViewModel", "Episode not found for playlist: $playlist")
return@mapNotNull null
} }
PlaylistElementUiModel( PlaylistElementUiModel(
id = episodeValue.id.toString(), id = episodeValue.id.toString(),