feat: update media selection logic to handle offline state in view models and DTOs

This commit is contained in:
2026-05-14 12:56:48 +02:00
parent 83730188eb
commit f60aba5a72
19 changed files with 111 additions and 72 deletions

View File

@@ -40,7 +40,7 @@ fun EntryProviderScope<Route>.tvSeriesSection() {
fun EntryProviderScope<Route>.tvEpisodeSection() { fun EntryProviderScope<Route>.tvEpisodeSection() {
entry<Route.EpisodeRoute> { route -> entry<Route.EpisodeRoute> { route ->
TvSeriesScreen( TvSeriesScreen(
series = SeriesDto(id = route.item.seriesId), series = SeriesDto(id = route.item.seriesId, offline = route.item.offline),
focusedSeasonId = route.item.seasonId, focusedSeasonId = route.item.seasonId,
focusedEpisodeId = route.item.id focusedEpisodeId = route.item.id
) )

View File

@@ -37,11 +37,7 @@ fun TvEpisodeScreen(
val navigationManager = LocalNavigationManager.current val navigationManager = LocalNavigationManager.current
LaunchedEffect(episode) { LaunchedEffect(episode) {
viewModel.selectEpisode( viewModel.selectEpisode(episode)
seriesId = episode.seriesId,
seasonId = episode.seasonId,
episodeId = episode.id
)
} }
val episode = viewModel.episode.collectAsState() val episode = viewModel.episode.collectAsState()

View File

@@ -28,8 +28,8 @@ fun TvMovieScreen(
viewModel: MovieScreenViewModel = hiltViewModel(), viewModel: MovieScreenViewModel = hiltViewModel(),
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
LaunchedEffect(movie.id) { LaunchedEffect(movie) {
viewModel.selectMovie(movie.id) viewModel.selectMovie(movie)
} }
val movieItem = viewModel.movie.collectAsState() val movieItem = viewModel.movie.collectAsState()

View File

@@ -42,8 +42,8 @@ fun TvSeriesScreen(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
viewModel: SeriesViewModel = hiltViewModel() viewModel: SeriesViewModel = hiltViewModel()
) { ) {
LaunchedEffect(series.id) { LaunchedEffect(series) {
viewModel.selectSeries(series.id) viewModel.selectSeries(series)
} }
val series = viewModel.series.collectAsState() val series = viewModel.series.collectAsState()

View File

@@ -43,11 +43,7 @@ fun EpisodeScreen(
val previousRoute = remember(backStack) { backStack.getOrNull(backStack.lastIndex - 1) } val previousRoute = remember(backStack) { backStack.getOrNull(backStack.lastIndex - 1) }
LaunchedEffect(episode) { LaunchedEffect(episode) {
viewModel.selectEpisode( viewModel.selectEpisode(episode)
seriesId = episode.seriesId,
seasonId = episode.seasonId,
episodeId = episode.id
)
} }
val episode = viewModel.episode.collectAsState() val episode = viewModel.episode.collectAsState()

View File

@@ -35,8 +35,8 @@ fun MovieScreen(
viewModel: MovieScreenViewModel = hiltViewModel(), viewModel: MovieScreenViewModel = hiltViewModel(),
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
LaunchedEffect(movie.id) { LaunchedEffect(movie) {
viewModel.selectMovie(movie.id) viewModel.selectMovie(movie)
} }
val movieItem = viewModel.movie.collectAsState() val movieItem = viewModel.movie.collectAsState()

View File

@@ -45,13 +45,13 @@ fun SeriesScreen(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
viewModel: SeriesViewModel = hiltViewModel() viewModel: SeriesViewModel = hiltViewModel()
) { ) {
LaunchedEffect(series.id) { LaunchedEffect(series) {
viewModel.selectSeries(series.id) viewModel.selectSeries(series)
} }
val series = viewModel.series.collectAsState() val seriesState = viewModel.series.collectAsState()
val seriesData = series.value val seriesData = seriesState.value
if (seriesData != null && seriesData.seasons.isNotEmpty()) { if (seriesData != null && seriesData.seasons.isNotEmpty()) {
LaunchedEffect(seriesData) { LaunchedEffect(seriesData) {
viewModel.observeSeriesDownloadState(seriesData) viewModel.observeSeriesDownloadState(seriesData)
@@ -75,6 +75,7 @@ fun SeriesScreen(
onLoadSeasonEpisodes = viewModel::loadSeasonEpisodes, onLoadSeasonEpisodes = viewModel::loadSeasonEpisodes,
onObserveSeasonDownloadState = viewModel::observeSeasonDownloadState, onObserveSeasonDownloadState = viewModel::observeSeasonDownloadState,
onBack = viewModel::onGoHome, onBack = viewModel::onGoHome,
offline = series.offline,
modifier = modifier modifier = modifier
) )
} else { } else {
@@ -91,6 +92,7 @@ private fun SeriesScreenInternal(
onLoadSeasonEpisodes: (UUID, UUID) -> Unit, onLoadSeasonEpisodes: (UUID, UUID) -> Unit,
onObserveSeasonDownloadState: (List<Episode>) -> Unit, onObserveSeasonDownloadState: (List<Episode>) -> Unit,
onBack: () -> Unit, onBack: () -> Unit,
offline: Boolean,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val scheme = MaterialTheme.colorScheme val scheme = MaterialTheme.colorScheme
@@ -128,6 +130,7 @@ private fun SeriesScreenInternal(
seriesDownloadState = seriesDownloadState, seriesDownloadState = seriesDownloadState,
selectedSeason = selectedSeason, selectedSeason = selectedSeason,
seasonDownloadState = seasonDownloadState, seasonDownloadState = seasonDownloadState,
offline = offline,
onDownloadOptionSelected = { option -> onDownloadOptionSelected = { option ->
onDownloadOptionSelected(option, selectedSeason) onDownloadOptionSelected(option, selectedSeason)
} }
@@ -188,7 +191,8 @@ private fun SeriesScreenPreview() {
onDownloadOptionSelected = { _, _ -> }, onDownloadOptionSelected = { _, _ -> },
onLoadSeasonEpisodes = { _, _ -> }, onLoadSeasonEpisodes = { _, _ -> },
onObserveSeasonDownloadState = {}, onObserveSeasonDownloadState = {},
onBack = {} onBack = {},
offline = false,
) )
} }
} }

View File

@@ -112,6 +112,7 @@ internal fun SeriesActionButtons(
seriesDownloadState: DownloadState, seriesDownloadState: DownloadState,
selectedSeason: Season, selectedSeason: Season,
seasonDownloadState: DownloadState, seasonDownloadState: DownloadState,
offline: Boolean,
onDownloadOptionSelected: (SeriesDownloadOption) -> Unit, onDownloadOptionSelected: (SeriesDownloadOption) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
@@ -119,8 +120,7 @@ internal fun SeriesActionButtons(
val navigationManager = LocalNavigationManager.current val navigationManager = LocalNavigationManager.current
val scheme = MaterialTheme.colorScheme val scheme = MaterialTheme.colorScheme
var showDownloadDialog by remember { mutableStateOf(false) } var showDownloadDialog by remember { mutableStateOf(false) }
val episodeId = nextUpEpisode?.id val playAction = remember(nextUpEpisode, offline) {
val playAction = remember(nextUpEpisode) {
nextUpEpisode?.let { episode -> nextUpEpisode?.let { episode ->
{ {
navigationManager.navigate( navigationManager.navigate(
@@ -128,7 +128,8 @@ internal fun SeriesActionButtons(
EpisodeDto( EpisodeDto(
id = episode.id, id = episode.id,
seasonId = episode.seasonId, seasonId = episode.seasonId,
seriesId = episode.seriesId seriesId = episode.seriesId,
offline = offline,
) )
) )
) )

View File

@@ -3,14 +3,15 @@ package hu.bbara.purefin.core.feature.content.episode
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import hu.bbara.purefin.core.Offline
import hu.bbara.purefin.core.data.LocalMediaRepository import hu.bbara.purefin.core.data.LocalMediaRepository
import hu.bbara.purefin.core.download.DownloadState import hu.bbara.purefin.core.download.DownloadState
import hu.bbara.purefin.core.download.MediaDownloadController import hu.bbara.purefin.core.download.MediaDownloadController
import hu.bbara.purefin.core.navigation.EpisodeDto
import hu.bbara.purefin.core.navigation.NavigationManager import hu.bbara.purefin.core.navigation.NavigationManager
import hu.bbara.purefin.core.navigation.Route import hu.bbara.purefin.core.navigation.Route
import hu.bbara.purefin.core.navigation.SeriesDto import hu.bbara.purefin.core.navigation.SeriesDto
import hu.bbara.purefin.model.Episode import hu.bbara.purefin.model.Episode
import java.util.UUID
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
@@ -25,28 +26,32 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
class EpisodeScreenViewModel @Inject constructor( class EpisodeScreenViewModel @Inject constructor(
private val mediaCatalogReader: LocalMediaRepository, private val defaultMediaCatalogReader: LocalMediaRepository,
@param:Offline private val offlineMediaCatalogReader: LocalMediaRepository,
private val navigationManager: NavigationManager, private val navigationManager: NavigationManager,
private val mediaDownloadManager: MediaDownloadController, private val mediaDownloadManager: MediaDownloadController,
): ViewModel() { ): ViewModel() {
private val _episodeId = MutableStateFlow<UUID?>(null) private val _episode = MutableStateFlow<EpisodeDto?>(null)
private val _seriesId = MutableStateFlow<UUID?>(null)
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
val episode: StateFlow<Episode?> = _episodeId val episode: StateFlow<Episode?> = _episode
.flatMapLatest { episodeId -> .flatMapLatest { episode ->
if (episodeId == null) flowOf(null) else mediaCatalogReader.getEpisode(episodeId) if (episode == null) {
flowOf(null)
} else {
mediaCatalogReader(episode.offline).getEpisode(episode.id)
}
} }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null)
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
val seriesTitle: StateFlow<String?> = _seriesId val seriesTitle: StateFlow<String?> = _episode
.flatMapLatest { seriesId -> .flatMapLatest { episode ->
if (seriesId == null) { if (episode == null) {
flowOf(null) flowOf(null)
} else { } else {
mediaCatalogReader.getSeries(seriesId).map { it?.name } mediaCatalogReader(episode.offline).getSeries(episode.seriesId).map { it?.name }
} }
} }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null)
@@ -59,22 +64,25 @@ class EpisodeScreenViewModel @Inject constructor(
} }
fun onSeriesClick() { fun onSeriesClick() {
val seriesId = _seriesId.value ?: return val episode = _episode.value ?: return
navigationManager.navigate(Route.SeriesRoute(SeriesDto(id = seriesId))) navigationManager.navigate(
Route.SeriesRoute(
SeriesDto(id = episode.seriesId, offline = episode.offline)
)
)
} }
fun selectEpisode(seriesId: UUID, seasonId: UUID, episodeId: UUID) { fun selectEpisode(episode: EpisodeDto) {
_episodeId.value = episodeId _episode.value = episode
_seriesId.value = seriesId
viewModelScope.launch { viewModelScope.launch {
mediaDownloadManager.observeDownloadState(episodeId.toString()).collect { mediaDownloadManager.observeDownloadState(episode.id.toString()).collect {
_downloadState.value = it _downloadState.value = it
} }
} }
} }
fun onDownloadClick() { fun onDownloadClick() {
val episodeId = _episodeId.value ?: return val episodeId = _episode.value?.id ?: return
viewModelScope.launch { viewModelScope.launch {
when (_downloadState.value) { when (_downloadState.value) {
is DownloadState.NotDownloaded, is DownloadState.Failed -> { is DownloadState.NotDownloaded, is DownloadState.Failed -> {
@@ -87,4 +95,7 @@ class EpisodeScreenViewModel @Inject constructor(
} }
} }
private fun mediaCatalogReader(offline: Boolean): LocalMediaRepository {
return if (offline) offlineMediaCatalogReader else defaultMediaCatalogReader
}
} }

View File

@@ -3,13 +3,14 @@ package hu.bbara.purefin.core.feature.content.movie
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import hu.bbara.purefin.core.Offline
import hu.bbara.purefin.core.data.LocalMediaRepository import hu.bbara.purefin.core.data.LocalMediaRepository
import hu.bbara.purefin.core.download.DownloadState import hu.bbara.purefin.core.download.DownloadState
import hu.bbara.purefin.core.download.MediaDownloadController import hu.bbara.purefin.core.download.MediaDownloadController
import hu.bbara.purefin.core.navigation.MovieDto
import hu.bbara.purefin.core.navigation.NavigationManager import hu.bbara.purefin.core.navigation.NavigationManager
import hu.bbara.purefin.core.navigation.Route import hu.bbara.purefin.core.navigation.Route
import hu.bbara.purefin.model.Movie import hu.bbara.purefin.model.Movie
import java.util.UUID
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
@@ -23,17 +24,22 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
class MovieScreenViewModel @Inject constructor( class MovieScreenViewModel @Inject constructor(
private val mediaCatalogReader: LocalMediaRepository, private val defaultMediaCatalogReader: LocalMediaRepository,
@param:Offline private val offlineMediaCatalogReader: LocalMediaRepository,
private val navigationManager: NavigationManager, private val navigationManager: NavigationManager,
private val mediaDownloadManager: MediaDownloadController, private val mediaDownloadManager: MediaDownloadController,
): ViewModel() { ): ViewModel() {
private val _movieId = MutableStateFlow<UUID?>(null) private val _movie = MutableStateFlow<MovieDto?>(null)
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
val movie: StateFlow<Movie?> = _movieId val movie: StateFlow<Movie?> = _movie
.flatMapLatest { movieId -> .flatMapLatest { movie ->
if (movieId == null) flowOf(null) else mediaCatalogReader.getMovie(movieId) if (movie == null) {
flowOf(null)
} else {
mediaCatalogReader(movie.offline).getMovie(movie.id)
}
} }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null)
@@ -53,10 +59,10 @@ class MovieScreenViewModel @Inject constructor(
navigationManager.replaceAll(Route.Home) navigationManager.replaceAll(Route.Home)
} }
fun selectMovie(movieId: UUID) { fun selectMovie(movie: MovieDto) {
_movieId.value = movieId _movie.value = movie
viewModelScope.launch { viewModelScope.launch {
mediaDownloadManager.observeDownloadState(movieId.toString()).collect { mediaDownloadManager.observeDownloadState(movie.id.toString()).collect {
_downloadState.value = it _downloadState.value = it
} }
} }
@@ -79,4 +85,7 @@ class MovieScreenViewModel @Inject constructor(
} }
} }
private fun mediaCatalogReader(offline: Boolean): LocalMediaRepository {
return if (offline) offlineMediaCatalogReader else defaultMediaCatalogReader
}
} }

View File

@@ -3,12 +3,14 @@ package hu.bbara.purefin.core.feature.content.series
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import hu.bbara.purefin.core.Offline
import hu.bbara.purefin.core.data.LocalMediaRepository import hu.bbara.purefin.core.data.LocalMediaRepository
import hu.bbara.purefin.core.download.DownloadState import hu.bbara.purefin.core.download.DownloadState
import hu.bbara.purefin.core.download.MediaDownloadController import hu.bbara.purefin.core.download.MediaDownloadController
import hu.bbara.purefin.core.navigation.EpisodeDto import hu.bbara.purefin.core.navigation.EpisodeDto
import hu.bbara.purefin.core.navigation.NavigationManager import hu.bbara.purefin.core.navigation.NavigationManager
import hu.bbara.purefin.core.navigation.Route import hu.bbara.purefin.core.navigation.Route
import hu.bbara.purefin.core.navigation.SeriesDto
import hu.bbara.purefin.model.Episode import hu.bbara.purefin.model.Episode
import hu.bbara.purefin.model.Series import hu.bbara.purefin.model.Series
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -27,17 +29,22 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
class SeriesViewModel @Inject constructor( class SeriesViewModel @Inject constructor(
private val mediaCatalogReader: LocalMediaRepository, private val defaultMediaCatalogReader: LocalMediaRepository,
@param:Offline private val offlineMediaCatalogReader: LocalMediaRepository,
private val navigationManager: NavigationManager, private val navigationManager: NavigationManager,
private val mediaDownloadManager: MediaDownloadController, private val mediaDownloadManager: MediaDownloadController,
) : ViewModel() { ) : ViewModel() {
private val _seriesId = MutableStateFlow<UUID?>(null) private val _series = MutableStateFlow<SeriesDto?>(null)
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
val series: StateFlow<Series?> = _seriesId val series: StateFlow<Series?> = _series
.flatMapLatest { seriesId -> .flatMapLatest { series ->
if (seriesId == null) flowOf(null) else mediaCatalogReader.getSeries(seriesId) if (series == null) {
flowOf(null)
} else {
mediaCatalogReader(series.offline).getSeries(series.id)
}
} }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null)
@@ -80,12 +87,13 @@ class SeriesViewModel @Inject constructor(
fun loadSeasonEpisodes(seriesId: UUID, seasonId: UUID) { fun loadSeasonEpisodes(seriesId: UUID, seasonId: UUID) {
viewModelScope.launch { viewModelScope.launch {
mediaCatalogReader.loadSeasonEpisodes(seriesId, seasonId) selectedMediaCatalogReader().loadSeasonEpisodes(seriesId, seasonId)
} }
} }
fun downloadSeason(seriesId: UUID, seasonId: UUID) { fun downloadSeason(seriesId: UUID, seasonId: UUID) {
viewModelScope.launch { viewModelScope.launch {
val mediaCatalogReader = selectedMediaCatalogReader()
mediaCatalogReader.loadSeasonEpisodes(seriesId, seasonId) mediaCatalogReader.loadSeasonEpisodes(seriesId, seasonId)
val episodes = mediaCatalogReader.getSeries(seriesId) val episodes = mediaCatalogReader.getSeries(seriesId)
.first() .first()
@@ -105,6 +113,7 @@ class SeriesViewModel @Inject constructor(
fun downloadSeries(seriesData: Series) { fun downloadSeries(seriesData: Series) {
viewModelScope.launch { viewModelScope.launch {
val mediaCatalogReader = selectedMediaCatalogReader()
seriesData.seasons.forEach { season -> seriesData.seasons.forEach { season ->
mediaCatalogReader.loadSeasonEpisodes(seriesData.id, season.id) mediaCatalogReader.loadSeasonEpisodes(seriesData.id, season.id)
} }
@@ -136,7 +145,8 @@ class SeriesViewModel @Inject constructor(
EpisodeDto( EpisodeDto(
id = episodeId, id = episodeId,
seasonId = seasonId, seasonId = seasonId,
seriesId = seriesId seriesId = seriesId,
offline = _series.value?.offline == true,
) )
)) ))
} }
@@ -153,10 +163,18 @@ class SeriesViewModel @Inject constructor(
navigationManager.replaceAll(Route.Home) navigationManager.replaceAll(Route.Home)
} }
fun selectSeries(seriesId: UUID) { fun selectSeries(series: SeriesDto) {
_seriesId.value = seriesId _series.value = series
viewModelScope.launch { viewModelScope.launch {
mediaCatalogReader.loadSeasons(seriesId) mediaCatalogReader(series.offline).loadSeasons(series.id)
} }
} }
private fun selectedMediaCatalogReader(): LocalMediaRepository {
return mediaCatalogReader(_series.value?.offline == true)
}
private fun mediaCatalogReader(offline: Boolean): LocalMediaRepository {
return if (offline) offlineMediaCatalogReader else defaultMediaCatalogReader
}
} }

View File

@@ -23,7 +23,7 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
class DownloadsViewModel @Inject constructor( class DownloadsViewModel @Inject constructor(
@Offline private val offlineCatalogReader: LocalMediaRepository, @param:Offline private val offlineCatalogReader: LocalMediaRepository,
private val navigationManager: NavigationManager, private val navigationManager: NavigationManager,
private val downloadManager: MediaDownloadController, private val downloadManager: MediaDownloadController,
) : ViewModel() { ) : ViewModel() {
@@ -31,7 +31,7 @@ class DownloadsViewModel @Inject constructor(
fun onMovieSelected(movieId: UUID) { fun onMovieSelected(movieId: UUID) {
navigationManager.navigate( navigationManager.navigate(
Route.MovieRoute( Route.MovieRoute(
MovieDto(id = movieId) MovieDto(id = movieId, offline = true)
)) ))
} }
@@ -39,7 +39,7 @@ class DownloadsViewModel @Inject constructor(
viewModelScope.launch { viewModelScope.launch {
navigationManager.navigate( navigationManager.navigate(
Route.SeriesRoute( Route.SeriesRoute(
SeriesDto(id = seriesId) SeriesDto(id = seriesId, offline = true)
)) ))
} }
} }

View File

@@ -11,4 +11,5 @@ data class EpisodeDto(
val seasonId: UUID, val seasonId: UUID,
@Serializable(with = UuidSerializer::class) @Serializable(with = UuidSerializer::class)
val seriesId: UUID, val seriesId: UUID,
val offline: Boolean = false,
) )

View File

@@ -7,4 +7,5 @@ import kotlinx.serialization.Serializable
data class MovieDto( data class MovieDto(
@Serializable(with = UuidSerializer::class) @Serializable(with = UuidSerializer::class)
val id: UUID, val id: UUID,
val offline: Boolean = false,
) )

View File

@@ -7,4 +7,5 @@ import kotlinx.serialization.Serializable
data class SeriesDto( data class SeriesDto(
@Serializable(with = UuidSerializer::class) @Serializable(with = UuidSerializer::class)
val id: UUID, val id: UUID,
val offline: Boolean = false,
) )

View File

@@ -37,7 +37,7 @@ class OfflineLocalMediaRepository @Inject constructor(
} }
override suspend fun getSeries(id: UUID): Flow<Series?> { override suspend fun getSeries(id: UUID): Flow<Series?> {
return series.map { it[id] } return localDataSource.observeSeriesWithContent(id)
} }
override suspend fun getEpisode(id: UUID): Flow<Episode?> { override suspend fun getEpisode(id: UUID): Flow<Episode?> {

View File

@@ -15,10 +15,10 @@ interface EpisodeDao {
@Upsert @Upsert
suspend fun upsertAll(episodes: List<EpisodeEntity>) suspend fun upsertAll(episodes: List<EpisodeEntity>)
@Query("SELECT * FROM episodes WHERE seriesId = :seriesId") @Query("SELECT * FROM episodes WHERE seriesId = :seriesId ORDER BY seasonIndex ASC, `index` ASC")
suspend fun getBySeriesId(seriesId: UUID): List<EpisodeEntity> suspend fun getBySeriesId(seriesId: UUID): List<EpisodeEntity>
@Query("SELECT * FROM episodes WHERE seasonId = :seasonId") @Query("SELECT * FROM episodes WHERE seasonId = :seasonId ORDER BY `index` ASC")
suspend fun getBySeasonId(seasonId: UUID): List<EpisodeEntity> suspend fun getBySeasonId(seasonId: UUID): List<EpisodeEntity>
@Query("SELECT * FROM episodes") @Query("SELECT * FROM episodes")

View File

@@ -14,7 +14,7 @@ interface SeasonDao {
@Upsert @Upsert
suspend fun upsertAll(seasons: List<SeasonEntity>) suspend fun upsertAll(seasons: List<SeasonEntity>)
@Query("SELECT * FROM seasons WHERE seriesId = :seriesId") @Query("SELECT * FROM seasons WHERE seriesId = :seriesId ORDER BY `index` ASC")
suspend fun getBySeriesId(seriesId: UUID): List<SeasonEntity> suspend fun getBySeriesId(seriesId: UUID): List<SeasonEntity>
@Query("SELECT * FROM seasons WHERE id = :id") @Query("SELECT * FROM seasons WHERE id = :id")

View File

@@ -43,11 +43,12 @@ class OfflineRoomMediaLocalDataSource(
seriesDao.observeWithContent(seriesId).map { relation -> seriesDao.observeWithContent(seriesId).map { relation ->
relation?.let { relation?.let {
it.series.toDomain( it.series.toDomain(
seasons = it.seasons.map { swe -> seasons = it.seasons.sortedBy { swe -> swe.season.index }.map { swe ->
swe.season.toDomain( swe.season.toDomain(
episodes = swe.episodes.map { ep -> ep.toDomain() } episodes = swe.episodes.sortedBy { ep -> ep.index }.map { ep -> ep.toDomain() }
) )
} ) }
)
} }
} }