mirror of
https://github.com/bbara04/Purefin.git
synced 2026-07-23 19:26:50 +00:00
feat: update media selection logic to handle offline state in view models and DTOs
This commit is contained in:
@@ -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
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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?> {
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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() }
|
||||||
)
|
)
|
||||||
} )
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user