mirror of
https://github.com/bbara04/Purefin.git
synced 2026-03-31 17:10:08 +02:00
revert: restore separate ContinueWatching and NextUp sections
Manually reverts c5b613e which combined the two sections. Restores
the NextUp UI section, data flow, and dedicated API call.
This commit is contained in:
@@ -50,6 +50,7 @@ fun HomePage(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
val continueWatching = viewModel.continueWatching.collectAsState()
|
val continueWatching = viewModel.continueWatching.collectAsState()
|
||||||
|
val nextUp = viewModel.nextUp.collectAsState()
|
||||||
val latestLibraryContent = viewModel.latestLibraryContent.collectAsState()
|
val latestLibraryContent = viewModel.latestLibraryContent.collectAsState()
|
||||||
|
|
||||||
LifecycleResumeEffect(Unit) {
|
LifecycleResumeEffect(Unit) {
|
||||||
@@ -95,6 +96,7 @@ fun HomePage(
|
|||||||
libraries = libraries,
|
libraries = libraries,
|
||||||
libraryContent = latestLibraryContent.value,
|
libraryContent = latestLibraryContent.value,
|
||||||
continueWatching = continueWatching.value,
|
continueWatching = continueWatching.value,
|
||||||
|
nextUp = nextUp.value,
|
||||||
onMovieSelected = viewModel::onMovieSelected,
|
onMovieSelected = viewModel::onMovieSelected,
|
||||||
onSeriesSelected = viewModel::onSeriesSelected,
|
onSeriesSelected = viewModel::onSeriesSelected,
|
||||||
onEpisodeSelected = viewModel::onEpisodeSelected,
|
onEpisodeSelected = viewModel::onEpisodeSelected,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
|||||||
import hu.bbara.purefin.app.home.ui.ContinueWatchingItem
|
import hu.bbara.purefin.app.home.ui.ContinueWatchingItem
|
||||||
import hu.bbara.purefin.app.home.ui.HomeNavItem
|
import hu.bbara.purefin.app.home.ui.HomeNavItem
|
||||||
import hu.bbara.purefin.app.home.ui.LibraryItem
|
import hu.bbara.purefin.app.home.ui.LibraryItem
|
||||||
|
import hu.bbara.purefin.app.home.ui.NextUpItem
|
||||||
import hu.bbara.purefin.app.home.ui.PosterItem
|
import hu.bbara.purefin.app.home.ui.PosterItem
|
||||||
import hu.bbara.purefin.data.MediaRepository
|
import hu.bbara.purefin.data.MediaRepository
|
||||||
import hu.bbara.purefin.data.model.Media
|
import hu.bbara.purefin.data.model.Media
|
||||||
@@ -86,6 +87,24 @@ class HomePageViewModel @Inject constructor(
|
|||||||
initialValue = emptyList()
|
initialValue = emptyList()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val nextUp = combine(
|
||||||
|
mediaRepository.nextUp,
|
||||||
|
mediaRepository.episodes
|
||||||
|
) { list, episodesMap ->
|
||||||
|
list.mapNotNull { media ->
|
||||||
|
when (media) {
|
||||||
|
is Media.EpisodeMedia -> episodesMap[media.episodeId]?.let {
|
||||||
|
NextUpItem(episode = it)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}.distinctBy { it.id }
|
||||||
|
}.stateIn(
|
||||||
|
scope = viewModelScope,
|
||||||
|
started = SharingStarted.WhileSubscribed(5_000),
|
||||||
|
initialValue = emptyList()
|
||||||
|
)
|
||||||
|
|
||||||
val latestLibraryContent = combine(
|
val latestLibraryContent = combine(
|
||||||
mediaRepository.latestLibraryContent,
|
mediaRepository.latestLibraryContent,
|
||||||
mediaRepository.movies,
|
mediaRepository.movies,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ fun HomeContent(
|
|||||||
libraries: List<LibraryItem>,
|
libraries: List<LibraryItem>,
|
||||||
libraryContent: Map<UUID, List<PosterItem>>,
|
libraryContent: Map<UUID, List<PosterItem>>,
|
||||||
continueWatching: List<ContinueWatchingItem>,
|
continueWatching: List<ContinueWatchingItem>,
|
||||||
|
nextUp: List<NextUpItem>,
|
||||||
onMovieSelected: (UUID) -> Unit,
|
onMovieSelected: (UUID) -> Unit,
|
||||||
onSeriesSelected: (UUID) -> Unit,
|
onSeriesSelected: (UUID) -> Unit,
|
||||||
onEpisodeSelected: (UUID, UUID, UUID) -> Unit,
|
onEpisodeSelected: (UUID, UUID, UUID) -> Unit,
|
||||||
@@ -40,6 +41,15 @@ fun HomeContent(
|
|||||||
item {
|
item {
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
}
|
}
|
||||||
|
item {
|
||||||
|
NextUpSection(
|
||||||
|
items = nextUp,
|
||||||
|
onEpisodeSelected = onEpisodeSelected
|
||||||
|
)
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
}
|
||||||
items(
|
items(
|
||||||
items = libraries.filter { libraryContent[it.id]?.isEmpty() != true },
|
items = libraries.filter { libraryContent[it.id]?.isEmpty() != true },
|
||||||
key = { it.id }
|
key = { it.id }
|
||||||
|
|||||||
@@ -35,6 +35,14 @@ data class ContinueWatchingItem(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class NextUpItem(
|
||||||
|
val episode: Episode
|
||||||
|
) {
|
||||||
|
val id: UUID = episode.id
|
||||||
|
val primaryText: String = episode.title
|
||||||
|
val secondaryText: String = episode.releaseDate
|
||||||
|
}
|
||||||
|
|
||||||
data class LibraryItem(
|
data class LibraryItem(
|
||||||
val id: UUID,
|
val id: UUID,
|
||||||
val name: String,
|
val name: String,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package hu.bbara.purefin.app.home.ui
|
package hu.bbara.purefin.app.home.ui
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
@@ -12,11 +13,18 @@ import androidx.compose.foundation.layout.aspectRatio
|
|||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.layout.wrapContentHeight
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
import androidx.compose.foundation.lazy.LazyRow
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.PlayArrow
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.IconButtonColors
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -34,6 +42,7 @@ import coil3.request.ImageRequest
|
|||||||
import hu.bbara.purefin.common.ui.PosterCard
|
import hu.bbara.purefin.common.ui.PosterCard
|
||||||
import hu.bbara.purefin.common.ui.components.MediaProgressBar
|
import hu.bbara.purefin.common.ui.components.MediaProgressBar
|
||||||
import hu.bbara.purefin.common.ui.components.PurefinAsyncImage
|
import hu.bbara.purefin.common.ui.components.PurefinAsyncImage
|
||||||
|
import hu.bbara.purefin.player.PlayerActivity
|
||||||
import org.jellyfin.sdk.model.UUID
|
import org.jellyfin.sdk.model.UUID
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
import kotlin.math.nextUp
|
import kotlin.math.nextUp
|
||||||
@@ -154,6 +163,126 @@ fun ContinueWatchingCard(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun NextUpSection(
|
||||||
|
items: List<NextUpItem>,
|
||||||
|
onEpisodeSelected: (UUID, UUID, UUID) -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
SectionHeader(
|
||||||
|
title = "Next Up",
|
||||||
|
action = null
|
||||||
|
)
|
||||||
|
LazyRow(
|
||||||
|
modifier = modifier.fillMaxWidth(),
|
||||||
|
contentPadding = PaddingValues(horizontal = 16.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
|
) {
|
||||||
|
items(
|
||||||
|
items = items, key = { it.id }) { item ->
|
||||||
|
NextUpCard(
|
||||||
|
item = item,
|
||||||
|
onEpisodeSelected = onEpisodeSelected
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun NextUpCard(
|
||||||
|
item: NextUpItem,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onEpisodeSelected: (UUID, UUID, UUID) -> Unit,
|
||||||
|
) {
|
||||||
|
val scheme = MaterialTheme.colorScheme
|
||||||
|
|
||||||
|
val context = LocalContext.current
|
||||||
|
val density = LocalDensity.current
|
||||||
|
|
||||||
|
val imageUrl = item.episode.heroImageUrl
|
||||||
|
|
||||||
|
val cardWidth = 280.dp
|
||||||
|
val cardHeight = cardWidth * 9 / 16
|
||||||
|
|
||||||
|
fun openItem(item: NextUpItem) {
|
||||||
|
val episode = item.episode
|
||||||
|
onEpisodeSelected(episode.seriesId, episode.seasonId, episode.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
val imageRequest = ImageRequest.Builder(context)
|
||||||
|
.data(imageUrl)
|
||||||
|
.size(with(density) { cardWidth.roundToPx() }, with(density) { cardHeight.roundToPx() })
|
||||||
|
.build()
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = modifier
|
||||||
|
.width(cardWidth)
|
||||||
|
.wrapContentHeight()
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.width(cardWidth)
|
||||||
|
.aspectRatio(16f / 9f)
|
||||||
|
.clip(RoundedCornerShape(16.dp))
|
||||||
|
.border(1.dp, scheme.outlineVariant.copy(alpha = 0.3f), RoundedCornerShape(16.dp))
|
||||||
|
.background(scheme.surfaceVariant)
|
||||||
|
) {
|
||||||
|
PurefinAsyncImage(
|
||||||
|
model = imageRequest,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.clickable {
|
||||||
|
openItem(item)
|
||||||
|
},
|
||||||
|
contentScale = ContentScale.Crop,
|
||||||
|
)
|
||||||
|
IconButton(
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.BottomEnd)
|
||||||
|
.padding(end = 8.dp, bottom = 16.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(scheme.secondary)
|
||||||
|
.size(36.dp),
|
||||||
|
onClick = {
|
||||||
|
val intent = Intent(context, PlayerActivity::class.java)
|
||||||
|
intent.putExtra("MEDIA_ID", item.id.toString())
|
||||||
|
context.startActivity(intent)
|
||||||
|
},
|
||||||
|
colors = IconButtonColors(
|
||||||
|
containerColor = scheme.secondary,
|
||||||
|
contentColor = scheme.onSecondary,
|
||||||
|
disabledContainerColor = scheme.secondary,
|
||||||
|
disabledContentColor = scheme.onSecondary
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.PlayArrow,
|
||||||
|
contentDescription = "Play",
|
||||||
|
modifier = Modifier.size(28.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Column(modifier = Modifier.padding(top = 12.dp)) {
|
||||||
|
Text(
|
||||||
|
text = item.primaryText,
|
||||||
|
color = scheme.onBackground,
|
||||||
|
fontSize = 16.sp,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = item.secondaryText,
|
||||||
|
color = scheme.onSurfaceVariant,
|
||||||
|
fontSize = 13.sp,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LibraryPosterSection(
|
fun LibraryPosterSection(
|
||||||
title: String,
|
title: String,
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ class JellyfinApiClient @Inject constructor(
|
|||||||
val getNextUpRequest = GetNextUpRequest(
|
val getNextUpRequest = GetNextUpRequest(
|
||||||
userId = getUserId(),
|
userId = getUserId(),
|
||||||
fields = itemFields,
|
fields = itemFields,
|
||||||
enableResumable = true,
|
enableResumable = false,
|
||||||
)
|
)
|
||||||
val result = api.tvShowsApi.getNextUp(getNextUpRequest)
|
val result = api.tvShowsApi.getNextUp(getNextUpRequest)
|
||||||
Log.d("getNextUpEpisodes", result.content.toString())
|
Log.d("getNextUpEpisodes", result.content.toString())
|
||||||
|
|||||||
@@ -69,6 +69,10 @@ class ActiveMediaRepository @Inject constructor(
|
|||||||
activeRepository.flatMapLatest { it.continueWatching }
|
activeRepository.flatMapLatest { it.continueWatching }
|
||||||
.stateIn(scope, SharingStarted.Eagerly, emptyList())
|
.stateIn(scope, SharingStarted.Eagerly, emptyList())
|
||||||
|
|
||||||
|
override val nextUp: StateFlow<List<Media>> =
|
||||||
|
activeRepository.flatMapLatest { it.nextUp }
|
||||||
|
.stateIn(scope, SharingStarted.Eagerly, emptyList())
|
||||||
|
|
||||||
override val latestLibraryContent: StateFlow<Map<UUID, List<Media>>> =
|
override val latestLibraryContent: StateFlow<Map<UUID, List<Media>>> =
|
||||||
activeRepository.flatMapLatest { it.latestLibraryContent }
|
activeRepository.flatMapLatest { it.latestLibraryContent }
|
||||||
.stateIn(scope, SharingStarted.Eagerly, emptyMap())
|
.stateIn(scope, SharingStarted.Eagerly, emptyMap())
|
||||||
|
|||||||
@@ -79,6 +79,9 @@ class InMemoryMediaRepository @Inject constructor(
|
|||||||
private val _continueWatching: MutableStateFlow<List<Media>> = MutableStateFlow(emptyList())
|
private val _continueWatching: MutableStateFlow<List<Media>> = MutableStateFlow(emptyList())
|
||||||
override val continueWatching: StateFlow<List<Media>> = _continueWatching.asStateFlow()
|
override val continueWatching: StateFlow<List<Media>> = _continueWatching.asStateFlow()
|
||||||
|
|
||||||
|
private val _nextUp: MutableStateFlow<List<Media>> = MutableStateFlow(emptyList())
|
||||||
|
override val nextUp: StateFlow<List<Media>> = _nextUp.asStateFlow()
|
||||||
|
|
||||||
private val _latestLibraryContent: MutableStateFlow<Map<UUID, List<Media>>> = MutableStateFlow(emptyMap())
|
private val _latestLibraryContent: MutableStateFlow<Map<UUID, List<Media>>> = MutableStateFlow(emptyMap())
|
||||||
override val latestLibraryContent: StateFlow<Map<UUID, List<Media>>> = _latestLibraryContent.asStateFlow()
|
override val latestLibraryContent: StateFlow<Map<UUID, List<Media>>> = _latestLibraryContent.asStateFlow()
|
||||||
|
|
||||||
@@ -94,6 +97,9 @@ class InMemoryMediaRepository @Inject constructor(
|
|||||||
if (cache.continueWatching.isNotEmpty()) {
|
if (cache.continueWatching.isNotEmpty()) {
|
||||||
_continueWatching.value = cache.continueWatching.mapNotNull { it.toMedia() }
|
_continueWatching.value = cache.continueWatching.mapNotNull { it.toMedia() }
|
||||||
}
|
}
|
||||||
|
if (cache.nextUp.isNotEmpty()) {
|
||||||
|
_nextUp.value = cache.nextUp.mapNotNull { it.toMedia() }
|
||||||
|
}
|
||||||
if (cache.latestLibraryContent.isNotEmpty()) {
|
if (cache.latestLibraryContent.isNotEmpty()) {
|
||||||
_latestLibraryContent.value = cache.latestLibraryContent.mapNotNull { (key, items) ->
|
_latestLibraryContent.value = cache.latestLibraryContent.mapNotNull { (key, items) ->
|
||||||
val uuid = runCatching { UUID.fromString(key) }.getOrNull() ?: return@mapNotNull null
|
val uuid = runCatching { UUID.fromString(key) }.getOrNull() ?: return@mapNotNull null
|
||||||
@@ -105,6 +111,7 @@ class InMemoryMediaRepository @Inject constructor(
|
|||||||
private suspend fun persistHomeCache() {
|
private suspend fun persistHomeCache() {
|
||||||
val cache = HomeCache(
|
val cache = HomeCache(
|
||||||
continueWatching = _continueWatching.value.map { it.toCachedItem() },
|
continueWatching = _continueWatching.value.map { it.toCachedItem() },
|
||||||
|
nextUp = _nextUp.value.map { it.toCachedItem() },
|
||||||
latestLibraryContent = _latestLibraryContent.value.map { (uuid, items) ->
|
latestLibraryContent = _latestLibraryContent.value.map { (uuid, items) ->
|
||||||
uuid.toString() to items.map { it.toCachedItem() }
|
uuid.toString() to items.map { it.toCachedItem() }
|
||||||
}.toMap()
|
}.toMap()
|
||||||
@@ -115,13 +122,13 @@ class InMemoryMediaRepository @Inject constructor(
|
|||||||
private fun Media.toCachedItem(): CachedMediaItem = when (this) {
|
private fun Media.toCachedItem(): CachedMediaItem = when (this) {
|
||||||
is Media.MovieMedia -> CachedMediaItem(type = "MOVIE", id = movieId.toString())
|
is Media.MovieMedia -> CachedMediaItem(type = "MOVIE", id = movieId.toString())
|
||||||
is Media.SeriesMedia -> CachedMediaItem(type = "SERIES", id = seriesId.toString())
|
is Media.SeriesMedia -> CachedMediaItem(type = "SERIES", id = seriesId.toString())
|
||||||
is Media.SeasonMedia -> CachedMediaItem(type = "SEASON", id = seasonId.toString(), mediaId = seriesId.toString())
|
is Media.SeasonMedia -> CachedMediaItem(type = "SEASON", id = seasonId.toString(), seriesId = seriesId.toString())
|
||||||
is Media.EpisodeMedia -> CachedMediaItem(type = "EPISODE", id = episodeId.toString(), mediaId = seriesId.toString())
|
is Media.EpisodeMedia -> CachedMediaItem(type = "EPISODE", id = episodeId.toString(), seriesId = seriesId.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CachedMediaItem.toMedia(): Media? {
|
private fun CachedMediaItem.toMedia(): Media? {
|
||||||
val uuid = runCatching { UUID.fromString(id) }.getOrNull() ?: return null
|
val uuid = runCatching { UUID.fromString(id) }.getOrNull() ?: return null
|
||||||
val seriesUuid = mediaId?.let { runCatching { UUID.fromString(it) }.getOrNull() }
|
val seriesUuid = seriesId?.let { runCatching { UUID.fromString(it) }.getOrNull() }
|
||||||
return when (type) {
|
return when (type) {
|
||||||
"MOVIE" -> Media.MovieMedia(movieId = uuid)
|
"MOVIE" -> Media.MovieMedia(movieId = uuid)
|
||||||
"SERIES" -> Media.SeriesMedia(seriesId = uuid)
|
"SERIES" -> Media.SeriesMedia(seriesId = uuid)
|
||||||
@@ -146,6 +153,7 @@ class InMemoryMediaRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
loadLibraries()
|
loadLibraries()
|
||||||
loadContinueWatching()
|
loadContinueWatching()
|
||||||
|
loadNextUp()
|
||||||
loadLatestLibraryContent()
|
loadLatestLibraryContent()
|
||||||
persistHomeCache()
|
persistHomeCache()
|
||||||
_state.value = MediaRepositoryState.Ready
|
_state.value = MediaRepositoryState.Ready
|
||||||
@@ -220,7 +228,7 @@ class InMemoryMediaRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun loadContinueWatching() {
|
suspend fun loadContinueWatching() {
|
||||||
val continueWatchingItems = jellyfinApiClient.getNextUpEpisodes()
|
val continueWatchingItems = jellyfinApiClient.getContinueWatching()
|
||||||
val items = continueWatchingItems.mapNotNull { item ->
|
val items = continueWatchingItems.mapNotNull { item ->
|
||||||
when (item.type) {
|
when (item.type) {
|
||||||
BaseItemKind.MOVIE -> Media.MovieMedia(movieId = item.id)
|
BaseItemKind.MOVIE -> Media.MovieMedia(movieId = item.id)
|
||||||
@@ -245,6 +253,23 @@ class InMemoryMediaRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun loadNextUp() {
|
||||||
|
val nextUpItems = jellyfinApiClient.getNextUpEpisodes()
|
||||||
|
val items = nextUpItems.map { item ->
|
||||||
|
Media.EpisodeMedia(
|
||||||
|
episodeId = item.id,
|
||||||
|
seriesId = item.seriesId!!
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_nextUp.value = items
|
||||||
|
|
||||||
|
// Load episodes
|
||||||
|
nextUpItems.forEach { item ->
|
||||||
|
val episode = item.toEpisode(serverUrl())
|
||||||
|
localDataSource.saveEpisode(episode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun loadLatestLibraryContent() {
|
suspend fun loadLatestLibraryContent() {
|
||||||
// TODO Make libraries accessible in a field or something that is not this ugly.
|
// TODO Make libraries accessible in a field or something that is not this ugly.
|
||||||
val librariesItem = jellyfinApiClient.getLibraries()
|
val librariesItem = jellyfinApiClient.getLibraries()
|
||||||
@@ -326,6 +351,7 @@ class InMemoryMediaRepository @Inject constructor(
|
|||||||
|
|
||||||
loadLibraries()
|
loadLibraries()
|
||||||
loadContinueWatching()
|
loadContinueWatching()
|
||||||
|
loadNextUp()
|
||||||
loadLatestLibraryContent()
|
loadLatestLibraryContent()
|
||||||
persistHomeCache()
|
persistHomeCache()
|
||||||
initialLoadTimestamp = System.currentTimeMillis()
|
initialLoadTimestamp = System.currentTimeMillis()
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ interface MediaRepository {
|
|||||||
val state: StateFlow<MediaRepositoryState>
|
val state: StateFlow<MediaRepositoryState>
|
||||||
|
|
||||||
val continueWatching: StateFlow<List<Media>>
|
val continueWatching: StateFlow<List<Media>>
|
||||||
|
val nextUp: StateFlow<List<Media>>
|
||||||
val latestLibraryContent: StateFlow<Map<UUID, List<Media>>>
|
val latestLibraryContent: StateFlow<Map<UUID, List<Media>>>
|
||||||
|
|
||||||
fun observeSeriesWithContent(seriesId: UUID): Flow<Series?>
|
fun observeSeriesWithContent(seriesId: UUID): Flow<Series?>
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ class OfflineMediaRepository @Inject constructor(
|
|||||||
|
|
||||||
// Offline mode doesn't support these server-side features
|
// Offline mode doesn't support these server-side features
|
||||||
override val continueWatching: StateFlow<List<Media>> = MutableStateFlow(emptyList())
|
override val continueWatching: StateFlow<List<Media>> = MutableStateFlow(emptyList())
|
||||||
|
override val nextUp: StateFlow<List<Media>> = MutableStateFlow(emptyList())
|
||||||
override val latestLibraryContent: StateFlow<Map<UUID, List<Media>>> = MutableStateFlow(emptyMap())
|
override val latestLibraryContent: StateFlow<Map<UUID, List<Media>>> = MutableStateFlow(emptyMap())
|
||||||
|
|
||||||
override fun observeSeriesWithContent(seriesId: UUID): Flow<Series?> {
|
override fun observeSeriesWithContent(seriesId: UUID): Flow<Series?> {
|
||||||
|
|||||||
@@ -6,11 +6,12 @@ import kotlinx.serialization.Serializable
|
|||||||
data class CachedMediaItem(
|
data class CachedMediaItem(
|
||||||
val type: String,
|
val type: String,
|
||||||
val id: String,
|
val id: String,
|
||||||
val mediaId: String? = null
|
val seriesId: String? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class HomeCache(
|
data class HomeCache(
|
||||||
val continueWatching: List<CachedMediaItem> = emptyList(),
|
val continueWatching: List<CachedMediaItem> = emptyList(),
|
||||||
|
val nextUp: List<CachedMediaItem> = emptyList(),
|
||||||
val latestLibraryContent: Map<String, List<CachedMediaItem>> = emptyMap()
|
val latestLibraryContent: Map<String, List<CachedMediaItem>> = emptyMap()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import androidx.room.RoomDatabase
|
|||||||
import androidx.room.TypeConverters
|
import androidx.room.TypeConverters
|
||||||
import hu.bbara.purefin.data.local.room.dao.CastMemberDao
|
import hu.bbara.purefin.data.local.room.dao.CastMemberDao
|
||||||
import hu.bbara.purefin.data.local.room.dao.EpisodeDao
|
import hu.bbara.purefin.data.local.room.dao.EpisodeDao
|
||||||
import hu.bbara.purefin.data.local.room.dao.LibraryDao
|
|
||||||
import hu.bbara.purefin.data.local.room.dao.MovieDao
|
import hu.bbara.purefin.data.local.room.dao.MovieDao
|
||||||
import hu.bbara.purefin.data.local.room.dao.SeasonDao
|
import hu.bbara.purefin.data.local.room.dao.SeasonDao
|
||||||
import hu.bbara.purefin.data.local.room.dao.SeriesDao
|
import hu.bbara.purefin.data.local.room.dao.SeriesDao
|
||||||
|
import hu.bbara.purefin.data.local.room.dao.LibraryDao
|
||||||
|
|
||||||
@Database(
|
@Database(
|
||||||
entities = [
|
entities = [
|
||||||
@@ -19,7 +19,7 @@ import hu.bbara.purefin.data.local.room.dao.SeriesDao
|
|||||||
LibraryEntity::class,
|
LibraryEntity::class,
|
||||||
CastMemberEntity::class
|
CastMemberEntity::class
|
||||||
],
|
],
|
||||||
version = 4,
|
version = 3,
|
||||||
exportSchema = false
|
exportSchema = false
|
||||||
)
|
)
|
||||||
@TypeConverters(UuidConverters::class)
|
@TypeConverters(UuidConverters::class)
|
||||||
|
|||||||
@@ -12,8 +12,7 @@ import java.util.UUID
|
|||||||
ForeignKey(
|
ForeignKey(
|
||||||
entity = LibraryEntity::class,
|
entity = LibraryEntity::class,
|
||||||
parentColumns = ["id"],
|
parentColumns = ["id"],
|
||||||
childColumns = ["libraryId"],
|
childColumns = ["libraryId"]
|
||||||
onDelete = ForeignKey.CASCADE
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
indices = [Index("libraryId")]
|
indices = [Index("libraryId")]
|
||||||
|
|||||||
Reference in New Issue
Block a user