diff --git a/app/src/main/java/hu/bbara/purefin/app/content/ContentMockData.kt b/app/src/main/java/hu/bbara/purefin/app/content/ContentMockData.kt index b964459..1a356fd 100644 --- a/app/src/main/java/hu/bbara/purefin/app/content/ContentMockData.kt +++ b/app/src/main/java/hu/bbara/purefin/app/content/ContentMockData.kt @@ -18,21 +18,27 @@ object ContentMockData { title = "E1: The Beginning", description = "The crew assembles for the first time as the anomaly begins to expand rapidly near Saturn's rings.", duration = "58m", - imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuC6OPszCXCIP_FMO3BJJUrjpCtDNw9aeHYOGyOAXdqF078hDFNrH7KXbaQ7qtipz6aIPLivd8VBBffNMbeAiYIjjWjn5GMb6Xn9iiJz0D2rzhCKi0TBeFrN6tC1IXJkzQyQKJNhTnyokWy9dd-YtN65V7er7RT6hP5jdVBXhtK1xZMjlgrm1bk_FTTmKd8Afu3zPtJCaaC98Z608vav5zhYlkrdA1wKNSTWTpzwMSyDIY3pNQNPFauWf0n-iEu7QsYTAwhCG_zfxz0" + imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuC6OPszCXCIP_FMO3BJJUrjpCtDNw9aeHYOGyOAXdqF078hDFNrH7KXbaQ7qtipz6aIPLivd8VBBffNMbeAiYIjjWjn5GMb6Xn9iiJz0D2rzhCKi0TBeFrN6tC1IXJkzQyQKJNhTnyokWy9dd-YtN65V7er7RT6hP5jdVBXhtK1xZMjlgrm1bk_FTTmKd8Afu3zPtJCaaC98Z608vav5zhYlkrdA1wKNSTWTpzwMSyDIY3pNQNPFauWf0n-iEu7QsYTAwhCG_zfxz0", + progress = 40.0, + watched = false ) val episode2 = SeriesEpisodeUiModel( id = "2", title = "E2: Event Horizon", description = "Dr. Cole discovers a frequency embedded in the rift's radiation that suggests intelligent design.", duration = "54m", - imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuBExsf-wEzAVjMxasU2ImGhlreqQo9biBSN1yHyAbW8MyuhuppRw9ho7OD3vsbySSJ3kNluEgH1Qun45PmLnZWixZsFU4Qc7UGGJNKMS5Nkm4GZAsKdFvb3z_i1tkCvaXXvGpqmwI0qjFuo1QyjjhYPA5Yp3I8ZhrnDYdQv_GxbhR6Vl3mY1rbxd2BIUEE5oMTwTF-QmJztUEaViZkSGSG2VgVXZ5VAREn4xWE902OH2sysllvXQJQIaj439JIC2_Vg61m0-F-F1Vc" + imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuBExsf-wEzAVjMxasU2ImGhlreqQo9biBSN1yHyAbW8MyuhuppRw9ho7OD3vsbySSJ3kNluEgH1Qun45PmLnZWixZsFU4Qc7UGGJNKMS5Nkm4GZAsKdFvb3z_i1tkCvaXXvGpqmwI0qjFuo1QyjjhYPA5Yp3I8ZhrnDYdQv_GxbhR6Vl3mY1rbxd2BIUEE5oMTwTF-QmJztUEaViZkSGSG2VgVXZ5VAREn4xWE902OH2sysllvXQJQIaj439JIC2_Vg61m0-F-F1Vc", + progress = 100.0, + watched = true ) val episode3 = SeriesEpisodeUiModel( id = "3", title = "E3: Singularity", description = "Tension rises as the ship approaches the event horizon, and the AI begins to behave erratically.", duration = "1h 02m", - imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuA5CFDWsWYO4YxdRoLd2QfH5Su2KLhtj5xSDb8qmzWHvPE888ac_HAAj1wu1uqdFNSncdmmJ-bWsc--h6NYKxVXkhd4vHaFWi0XTJXgsR0F3cBu_l2SynSX4TMNSy5C3XWDurgeSH789byOe1HvoxHCHTJYaSf3OyEbil-NOp9g_9mZ24CIZOI79nx57CRzmooxoswycqssPpfTNkrnoYrrAczt5qbncwLM9NVU442YxyBFisr2Ds9H-CNBOakiCtaKnoJ6npznM7U" + imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuA5CFDWsWYO4YxdRoLd2QfH5Su2KLhtj5xSDb8qmzWHvPE888ac_HAAj1wu1uqdFNSncdmmJ-bWsc--h6NYKxVXkhd4vHaFWi0XTJXgsR0F3cBu_l2SynSX4TMNSy5C3XWDurgeSH789byOe1HvoxHCHTJYaSf3OyEbil-NOp9g_9mZ24CIZOI79nx57CRzmooxoswycqssPpfTNkrnoYrrAczt5qbncwLM9NVU442YxyBFisr2Ds9H-CNBOakiCtaKnoJ6npznM7U", + progress = 40.0, + watched = false ) return SeriesUiModel( title = "Interstellar Horizon: The Series", @@ -45,18 +51,18 @@ object ContentMockData { seasonTabs = listOf( SeriesSeasonUiModel( name = "Season 1", - isSelected = true, - episodes = listOf(episode1, episode2, episode3) + episodes = listOf(episode1, episode2, episode3), + unplayedCount = 2 ), SeriesSeasonUiModel( name = "Season 2", - isSelected = false, - episodes = listOf(episode1, episode2, episode3) + episodes = listOf(episode1, episode2, episode3), + unplayedCount = 0 ), SeriesSeasonUiModel( name = "Season 3", - isSelected = false, - episodes = listOf(episode1, episode2, episode3) + episodes = listOf(episode1, episode2, episode3), + unplayedCount = 1 ) ), cast = listOf( diff --git a/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesComponents.kt b/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesComponents.kt index 776b898..4f1f437 100644 --- a/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesComponents.kt +++ b/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesComponents.kt @@ -21,6 +21,7 @@ import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons @@ -34,6 +35,7 @@ import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -169,9 +171,20 @@ private fun SeasonTab( @Composable internal fun EpisodeCarousel(episodes: List, modifier: Modifier = Modifier) { + val listState = rememberLazyListState() + + LaunchedEffect(episodes) { + val firstUnwatchedIndex = episodes.indexOfFirst { !it.watched }.let { if (it == -1) 0 else it } + if (firstUnwatchedIndex != 0) { + listState.animateScrollToItem(firstUnwatchedIndex) + } else { + listState.scrollToItem(0) + } + } + LazyRow( + state = listState, modifier = modifier, -// contentPadding = PaddingValues(horizontal = 20.dp), horizontalArrangement = Arrangement.spacedBy(16.dp) ) { items(episodes) { episode -> diff --git a/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesModels.kt b/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesModels.kt index 17d2087..d9945cb 100644 --- a/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesModels.kt +++ b/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesModels.kt @@ -5,13 +5,15 @@ data class SeriesEpisodeUiModel( val title: String, val description: String, val duration: String, - val imageUrl: String + val imageUrl: String, + val watched: Boolean, + val progress: Double? ) data class SeriesSeasonUiModel( val name: String, - val isSelected: Boolean, - val episodes: List + val episodes: List, + val unplayedCount: Int? ) data class SeriesCastMemberUiModel( @@ -30,4 +32,15 @@ data class SeriesUiModel( val heroImageUrl: String, val seasonTabs: List, val cast: List -) +) { + fun getNextEpisode(): SeriesEpisodeUiModel { + for (season in seasonTabs) { + for (episode in season.episodes) { + if (!episode.watched) { + return episode + } + } + } + return seasonTabs.first().episodes.first() + } +} diff --git a/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesScreen.kt b/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesScreen.kt index 32ef314..072b607 100644 --- a/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesScreen.kt +++ b/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesScreen.kt @@ -61,7 +61,14 @@ private fun SeriesScreenInternal( val textMutedStrong = scheme.onSurfaceVariant.copy(alpha = 0.7f) fun getDefaultSeason() : SeriesSeasonUiModel { - // TODO get next next episodes season selected or add logic to it. + for (season in series.seasonTabs) { + val firstUnwatchedEpisode = season.episodes.firstOrNull { + it.watched.not() + } + if (firstUnwatchedEpisode != null) { + return season + } + } return series.seasonTabs.first() } val selectedSeason = remember { mutableStateOf(getDefaultSeason()) } diff --git a/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesViewModel.kt b/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesViewModel.kt index 4f678af..d6a0228 100644 --- a/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesViewModel.kt +++ b/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesViewModel.kt @@ -79,14 +79,15 @@ class SeriesViewModel @Inject constructor( title = episode.name ?: "Unknown", description = episode.overview ?: "", duration = "58m", - imageUrl = JellyfinImageHelper.toImageUrl(url = serverUrl, itemId = episode.id, type = ImageType.PRIMARY) + imageUrl = JellyfinImageHelper.toImageUrl(url = serverUrl, itemId = episode.id, type = ImageType.PRIMARY), + progress = episode.userData!!.playedPercentage, + watched = episode.userData!!.played ) } SeriesSeasonUiModel( name = season.name ?: "Unknown", episodes = episodeItemUiModels, - // TODO add actual logic or remove - isSelected = false, + unplayedCount = season.userData!!.unplayedItemCount ) } return SeriesUiModel( diff --git a/app/src/main/java/hu/bbara/purefin/client/JellyfinApiClient.kt b/app/src/main/java/hu/bbara/purefin/client/JellyfinApiClient.kt index 44a3c50..61ad40a 100644 --- a/app/src/main/java/hu/bbara/purefin/client/JellyfinApiClient.kt +++ b/app/src/main/java/hu/bbara/purefin/client/JellyfinApiClient.kt @@ -167,6 +167,7 @@ class JellyfinApiClient @Inject constructor( val result = api.tvShowsApi.getSeasons( userId = getUserId(), seriesId = seriesId, + enableUserData = true ) Log.d("getSeasons response: {}", result.content.toString()) return result.content.items