mirror of
https://github.com/bbara04/Purefin.git
synced 2026-03-31 17:10:08 +02:00
feat: enhance series data handling with reactive flows and content observation
This commit is contained in:
@@ -8,8 +8,13 @@ import hu.bbara.purefin.data.model.Series
|
|||||||
import hu.bbara.purefin.navigation.EpisodeDto
|
import hu.bbara.purefin.navigation.EpisodeDto
|
||||||
import hu.bbara.purefin.navigation.NavigationManager
|
import hu.bbara.purefin.navigation.NavigationManager
|
||||||
import hu.bbara.purefin.navigation.Route
|
import hu.bbara.purefin.navigation.Route
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.jellyfin.sdk.model.UUID
|
import org.jellyfin.sdk.model.UUID
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -20,8 +25,14 @@ class SeriesViewModel @Inject constructor(
|
|||||||
private val navigationManager: NavigationManager,
|
private val navigationManager: NavigationManager,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _series = MutableStateFlow<Series?>(null)
|
private val _seriesId = MutableStateFlow<UUID?>(null)
|
||||||
val series = _series.asStateFlow()
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
val series: StateFlow<Series?> = _seriesId
|
||||||
|
.flatMapLatest { id ->
|
||||||
|
if (id != null) mediaRepository.observeSeriesWithContent(id) else flowOf(null)
|
||||||
|
}
|
||||||
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch { mediaRepository.ensureReady() }
|
viewModelScope.launch { mediaRepository.ensureReady() }
|
||||||
@@ -43,17 +54,15 @@ class SeriesViewModel @Inject constructor(
|
|||||||
navigationManager.pop()
|
navigationManager.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun onGoHome() {
|
fun onGoHome() {
|
||||||
navigationManager.replaceAll(Route.Home)
|
navigationManager.replaceAll(Route.Home)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun selectSeries(seriesId: UUID) {
|
fun selectSeries(seriesId: UUID) {
|
||||||
|
_seriesId.value = seriesId
|
||||||
|
// Ensure content is loaded from API if not cached
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val series = mediaRepository.getSeriesWithContent(
|
mediaRepository.getSeriesWithContent(seriesId)
|
||||||
seriesId = seriesId
|
|
||||||
)
|
|
||||||
_series.value = series
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,13 @@ import kotlinx.coroutines.CompletableDeferred
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
@@ -39,15 +42,19 @@ class InMemoryMediaRepository @Inject constructor(
|
|||||||
) : MediaRepository {
|
) : MediaRepository {
|
||||||
|
|
||||||
private val ready = CompletableDeferred<Unit>()
|
private val ready = CompletableDeferred<Unit>()
|
||||||
|
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||||
|
|
||||||
private val _state: MutableStateFlow<MediaRepositoryState> = MutableStateFlow(MediaRepositoryState.Loading)
|
private val _state: MutableStateFlow<MediaRepositoryState> = MutableStateFlow(MediaRepositoryState.Loading)
|
||||||
override val state: StateFlow<MediaRepositoryState> = _state.asStateFlow()
|
override val state: StateFlow<MediaRepositoryState> = _state.asStateFlow()
|
||||||
|
|
||||||
private val _movies : MutableStateFlow<Map<UUID, Movie>> = MutableStateFlow(emptyMap())
|
override val movies: StateFlow<Map<UUID, Movie>> = localDataSource.moviesFlow
|
||||||
override val movies: StateFlow<Map<UUID, Movie>> = _movies.asStateFlow()
|
.stateIn(scope, SharingStarted.Eagerly, emptyMap())
|
||||||
|
|
||||||
private val _series : MutableStateFlow<Map<UUID, Series>> = MutableStateFlow(emptyMap())
|
override val series: StateFlow<Map<UUID, Series>> = localDataSource.seriesFlow
|
||||||
override val series: StateFlow<Map<UUID, Series>> = _series.asStateFlow()
|
.stateIn(scope, SharingStarted.Eagerly, emptyMap())
|
||||||
|
|
||||||
|
override fun observeSeriesWithContent(seriesId: UUID): Flow<Series?> =
|
||||||
|
localDataSource.observeSeriesWithContent(seriesId)
|
||||||
|
|
||||||
private val _continueWatching: MutableStateFlow<List<Media>> = MutableStateFlow(emptyList())
|
private val _continueWatching: MutableStateFlow<List<Media>> = MutableStateFlow(emptyList())
|
||||||
val continueWatching: StateFlow<List<Media>> = _continueWatching.asStateFlow()
|
val continueWatching: StateFlow<List<Media>> = _continueWatching.asStateFlow()
|
||||||
@@ -55,8 +62,6 @@ class InMemoryMediaRepository @Inject constructor(
|
|||||||
private val _latestLibraryContent: MutableStateFlow<Map<UUID, List<Media>>> = MutableStateFlow(emptyMap())
|
private val _latestLibraryContent: MutableStateFlow<Map<UUID, List<Media>>> = MutableStateFlow(emptyMap())
|
||||||
val latestLibraryContent: StateFlow<Map<UUID, List<Media>>> = _latestLibraryContent.asStateFlow()
|
val latestLibraryContent: StateFlow<Map<UUID, List<Media>>> = _latestLibraryContent.asStateFlow()
|
||||||
|
|
||||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
runCatching { ensureReady() }
|
runCatching { ensureReady() }
|
||||||
@@ -96,11 +101,9 @@ class InMemoryMediaRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
val movies = filledLibraries.filter { it.type == CollectionType.MOVIES }.flatMap { it.movies.orEmpty() }
|
val movies = filledLibraries.filter { it.type == CollectionType.MOVIES }.flatMap { it.movies.orEmpty() }
|
||||||
localDataSource.saveMovies(movies)
|
localDataSource.saveMovies(movies)
|
||||||
_movies.value = localDataSource.getMovies().associateBy { it.id }
|
|
||||||
|
|
||||||
val series = filledLibraries.filter { it.type == CollectionType.TVSHOWS }.flatMap { it.series.orEmpty() }
|
val series = filledLibraries.filter { it.type == CollectionType.TVSHOWS }.flatMap { it.series.orEmpty() }
|
||||||
localDataSource.saveSeries(series)
|
localDataSource.saveSeries(series)
|
||||||
_series.value = localDataSource.getSeries().associateBy { it.id }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun loadLibrary(library: Library): Library {
|
suspend fun loadLibrary(library: Library): Library {
|
||||||
@@ -123,7 +126,6 @@ class InMemoryMediaRepository @Inject constructor(
|
|||||||
?: throw RuntimeException("Movie not found")
|
?: throw RuntimeException("Movie not found")
|
||||||
val updatedMovie = movieItem.toMovie(serverUrl(), movie.libraryId)
|
val updatedMovie = movieItem.toMovie(serverUrl(), movie.libraryId)
|
||||||
localDataSource.saveMovies(listOf(updatedMovie))
|
localDataSource.saveMovies(listOf(updatedMovie))
|
||||||
_movies.value += (movie.id to updatedMovie)
|
|
||||||
return updatedMovie
|
return updatedMovie
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +134,6 @@ class InMemoryMediaRepository @Inject constructor(
|
|||||||
?: throw RuntimeException("Series not found")
|
?: throw RuntimeException("Series not found")
|
||||||
val updatedSeries = seriesItem.toSeries(serverUrl(), series.libraryId)
|
val updatedSeries = seriesItem.toSeries(serverUrl(), series.libraryId)
|
||||||
localDataSource.saveSeries(listOf(updatedSeries))
|
localDataSource.saveSeries(listOf(updatedSeries))
|
||||||
_series.value += (series.id to updatedSeries)
|
|
||||||
return updatedSeries
|
return updatedSeries
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,31 +205,24 @@ class InMemoryMediaRepository @Inject constructor(
|
|||||||
|
|
||||||
override suspend fun getMovie(movieId: UUID): Movie {
|
override suspend fun getMovie(movieId: UUID): Movie {
|
||||||
awaitReady()
|
awaitReady()
|
||||||
localDataSource.getMovie(movieId)?.let {
|
return localDataSource.getMovie(movieId)
|
||||||
_movies.value += (movieId to it)
|
?: throw RuntimeException("Movie not found")
|
||||||
return it
|
|
||||||
}
|
|
||||||
throw RuntimeException("Movie not found")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getSeries(seriesId: UUID): Series {
|
override suspend fun getSeries(seriesId: UUID): Series {
|
||||||
awaitReady()
|
awaitReady()
|
||||||
localDataSource.getSeriesBasic(seriesId)?.let {
|
return localDataSource.getSeriesBasic(seriesId)
|
||||||
_series.value += (seriesId to it)
|
?: throw RuntimeException("Series not found")
|
||||||
return it
|
|
||||||
}
|
|
||||||
throw RuntimeException("Series not found")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getSeriesWithContent(seriesId: UUID): Series {
|
override suspend fun getSeriesWithContent(seriesId: UUID): Series {
|
||||||
awaitReady()
|
awaitReady()
|
||||||
// Use cached content if available
|
// Use cached content if available
|
||||||
localDataSource.getSeriesWithContent(seriesId)?.takeIf { it.seasons.isNotEmpty() }?.let {
|
localDataSource.getSeriesWithContent(seriesId)?.takeIf { it.seasons.isNotEmpty() }?.let {
|
||||||
_series.value += (seriesId to it)
|
|
||||||
return it
|
return it
|
||||||
}
|
}
|
||||||
|
|
||||||
val series = _series.value[seriesId] ?: throw RuntimeException("Series not found")
|
val series = this.series.value[seriesId] ?: throw RuntimeException("Series not found")
|
||||||
|
|
||||||
val emptySeasonsItem = jellyfinApiClient.getSeasons(seriesId)
|
val emptySeasonsItem = jellyfinApiClient.getSeasons(seriesId)
|
||||||
val emptySeasons = emptySeasonsItem.map { it.toSeason(serverUrl()) }
|
val emptySeasons = emptySeasonsItem.map { it.toSeason(serverUrl()) }
|
||||||
@@ -240,7 +234,6 @@ class InMemoryMediaRepository @Inject constructor(
|
|||||||
val updatedSeries = series.copy(seasons = filledSeasons)
|
val updatedSeries = series.copy(seasons = filledSeasons)
|
||||||
localDataSource.saveSeries(listOf(updatedSeries))
|
localDataSource.saveSeries(listOf(updatedSeries))
|
||||||
localDataSource.saveSeriesContent(updatedSeries)
|
localDataSource.saveSeriesContent(updatedSeries)
|
||||||
_series.value += (series.id to updatedSeries)
|
|
||||||
return updatedSeries
|
return updatedSeries
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,30 +302,8 @@ class InMemoryMediaRepository @Inject constructor(
|
|||||||
if (durationMs <= 0) return
|
if (durationMs <= 0) return
|
||||||
val progressPercent = (positionMs.toDouble() / durationMs.toDouble()) * 100.0
|
val progressPercent = (positionMs.toDouble() / durationMs.toDouble()) * 100.0
|
||||||
val watched = progressPercent >= 90.0
|
val watched = progressPercent >= 90.0
|
||||||
|
// Write to Room — the reactive Flows propagate changes to UI automatically
|
||||||
val result = localDataSource.updateWatchProgress(mediaId, progressPercent, watched) ?: return
|
localDataSource.updateWatchProgress(mediaId, progressPercent, watched)
|
||||||
|
|
||||||
if (result.isMovie) {
|
|
||||||
_movies.value[mediaId]?.let { movie ->
|
|
||||||
_movies.value += (mediaId to movie.copy(progress = progressPercent, watched = watched))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val seriesId = result.seriesId ?: return
|
|
||||||
_series.value[seriesId]?.let { currentSeries ->
|
|
||||||
val updatedSeasons = currentSeries.seasons.map { season ->
|
|
||||||
if (season.id == result.seasonId) {
|
|
||||||
val updatedEpisodes = season.episodes.map { ep ->
|
|
||||||
if (ep.id == mediaId) ep.copy(progress = progressPercent, watched = watched) else ep
|
|
||||||
}
|
|
||||||
season.copy(unwatchedEpisodeCount = result.seasonUnwatchedCount, episodes = updatedEpisodes)
|
|
||||||
} else season
|
|
||||||
}
|
|
||||||
_series.value += (seriesId to currentSeries.copy(
|
|
||||||
unwatchedEpisodeCount = result.seriesUnwatchedCount,
|
|
||||||
seasons = updatedSeasons
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun refreshHomeData() {
|
override suspend fun refreshHomeData() {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import hu.bbara.purefin.data.model.Episode
|
|||||||
import hu.bbara.purefin.data.model.Movie
|
import hu.bbara.purefin.data.model.Movie
|
||||||
import hu.bbara.purefin.data.model.Season
|
import hu.bbara.purefin.data.model.Season
|
||||||
import hu.bbara.purefin.data.model.Series
|
import hu.bbara.purefin.data.model.Series
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@@ -13,6 +14,8 @@ interface MediaRepository {
|
|||||||
val series: StateFlow<Map<UUID, Series>>
|
val series: StateFlow<Map<UUID, Series>>
|
||||||
val state: StateFlow<MediaRepositoryState>
|
val state: StateFlow<MediaRepositoryState>
|
||||||
|
|
||||||
|
fun observeSeriesWithContent(seriesId: UUID): Flow<Series?>
|
||||||
|
|
||||||
suspend fun ensureReady()
|
suspend fun ensureReady()
|
||||||
|
|
||||||
suspend fun getMovie(movieId: UUID) : Movie
|
suspend fun getMovie(movieId: UUID) : Movie
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import java.util.UUID
|
|||||||
data class EpisodeEntity(
|
data class EpisodeEntity(
|
||||||
@PrimaryKey val id: UUID,
|
@PrimaryKey val id: UUID,
|
||||||
val seriesId: UUID,
|
val seriesId: UUID,
|
||||||
val seasonId: UUID?,
|
val seasonId: UUID,
|
||||||
val index: Int,
|
val index: Int,
|
||||||
val title: String,
|
val title: String,
|
||||||
val synopsis: String,
|
val synopsis: String,
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import hu.bbara.purefin.data.model.Episode
|
|||||||
import hu.bbara.purefin.data.model.Movie
|
import hu.bbara.purefin.data.model.Movie
|
||||||
import hu.bbara.purefin.data.model.Season
|
import hu.bbara.purefin.data.model.Season
|
||||||
import hu.bbara.purefin.data.model.Series
|
import hu.bbara.purefin.data.model.Series
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@@ -25,6 +27,30 @@ class RoomMediaLocalDataSource @Inject constructor(
|
|||||||
private val castMemberDao: CastMemberDao
|
private val castMemberDao: CastMemberDao
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
// Lightweight Flows for list screens (home, library)
|
||||||
|
val moviesFlow: Flow<Map<UUID, Movie>> = movieDao.observeAll()
|
||||||
|
.map { entities -> entities.associate { it.id to it.toDomain(cast = emptyList()) } }
|
||||||
|
|
||||||
|
val seriesFlow: Flow<Map<UUID, Series>> = seriesDao.observeAll()
|
||||||
|
.map { entities ->
|
||||||
|
entities.associate { it.id to it.toDomain(seasons = emptyList(), cast = emptyList()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Full content Flow for series detail screen (scoped to one series)
|
||||||
|
fun observeSeriesWithContent(seriesId: UUID): Flow<Series?> =
|
||||||
|
seriesDao.observeWithContent(seriesId).map { relation ->
|
||||||
|
relation?.let {
|
||||||
|
it.series.toDomain(
|
||||||
|
seasons = it.seasons.map { swe ->
|
||||||
|
swe.season.toDomain(
|
||||||
|
episodes = swe.episodes.map { ep -> ep.toDomain(cast = emptyList()) }
|
||||||
|
)
|
||||||
|
},
|
||||||
|
cast = emptyList()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun saveMovies(movies: List<Movie>) {
|
suspend fun saveMovies(movies: List<Movie>) {
|
||||||
database.withTransaction {
|
database.withTransaction {
|
||||||
movieDao.upsertAll(movies.map { it.toEntity() })
|
movieDao.upsertAll(movies.map { it.toEntity() })
|
||||||
@@ -131,39 +157,21 @@ class RoomMediaLocalDataSource @Inject constructor(
|
|||||||
return episodeEntity.toDomain(cast)
|
return episodeEntity.toDomain(cast)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun updateWatchProgress(mediaId: UUID, progress: Double?, watched: Boolean) {
|
||||||
data class WatchProgressResult(
|
|
||||||
val isMovie: Boolean,
|
|
||||||
val seriesId: UUID? = null,
|
|
||||||
val seasonId: UUID? = null,
|
|
||||||
val seriesUnwatchedCount: Int = 0,
|
|
||||||
val seasonUnwatchedCount: Int = 0
|
|
||||||
)
|
|
||||||
|
|
||||||
suspend fun updateWatchProgress(mediaId: UUID, progress: Double?, watched: Boolean): WatchProgressResult? {
|
|
||||||
movieDao.getById(mediaId)?.let {
|
movieDao.getById(mediaId)?.let {
|
||||||
movieDao.updateProgress(mediaId, progress, watched)
|
movieDao.updateProgress(mediaId, progress, watched)
|
||||||
return WatchProgressResult(isMovie = true)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
episodeDao.getById(mediaId)?.let { episode ->
|
episodeDao.getById(mediaId)?.let { episode ->
|
||||||
return database.withTransaction {
|
database.withTransaction {
|
||||||
episodeDao.updateProgress(mediaId, progress, watched)
|
episodeDao.updateProgress(mediaId, progress, watched)
|
||||||
val seasonUnwatched = episodeDao.countUnwatchedBySeason(episode.seasonId!!)
|
val seasonUnwatched = episodeDao.countUnwatchedBySeason(episode.seasonId)
|
||||||
seasonDao.updateUnwatchedCount(episode.seasonId, seasonUnwatched)
|
seasonDao.updateUnwatchedCount(episode.seasonId, seasonUnwatched)
|
||||||
val seriesUnwatched = episodeDao.countUnwatchedBySeries(episode.seriesId)
|
val seriesUnwatched = episodeDao.countUnwatchedBySeries(episode.seriesId)
|
||||||
seriesDao.updateUnwatchedCount(episode.seriesId, seriesUnwatched)
|
seriesDao.updateUnwatchedCount(episode.seriesId, seriesUnwatched)
|
||||||
WatchProgressResult(
|
|
||||||
isMovie = false,
|
|
||||||
seriesId = episode.seriesId,
|
|
||||||
seasonId = episode.seasonId,
|
|
||||||
seriesUnwatchedCount = seriesUnwatched,
|
|
||||||
seasonUnwatchedCount = seasonUnwatched
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getEpisodesBySeries(seriesId: UUID): List<Episode> {
|
suspend fun getEpisodesBySeries(seriesId: UUID): List<Episode> {
|
||||||
@@ -268,7 +276,7 @@ class RoomMediaLocalDataSource @Inject constructor(
|
|||||||
private fun EpisodeEntity.toDomain(cast: List<CastMember>) = Episode(
|
private fun EpisodeEntity.toDomain(cast: List<CastMember>) = Episode(
|
||||||
id = id,
|
id = id,
|
||||||
seriesId = seriesId,
|
seriesId = seriesId,
|
||||||
seasonId = seasonId ?: seriesId, // fallback to series when season is absent
|
seasonId = seasonId,
|
||||||
index = index,
|
index = index,
|
||||||
title = title,
|
title = title,
|
||||||
synopsis = synopsis,
|
synopsis = synopsis,
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package hu.bbara.purefin.data.local.room
|
||||||
|
|
||||||
|
import androidx.room.Embedded
|
||||||
|
import androidx.room.Relation
|
||||||
|
|
||||||
|
data class SeasonWithEpisodes(
|
||||||
|
@Embedded val season: SeasonEntity,
|
||||||
|
@Relation(
|
||||||
|
parentColumn = "id",
|
||||||
|
entityColumn = "seasonId"
|
||||||
|
)
|
||||||
|
val episodes: List<EpisodeEntity>
|
||||||
|
)
|
||||||
|
|
||||||
|
data class SeriesWithSeasonsAndEpisodes(
|
||||||
|
@Embedded val series: SeriesEntity,
|
||||||
|
@Relation(
|
||||||
|
entity = SeasonEntity::class,
|
||||||
|
parentColumn = "id",
|
||||||
|
entityColumn = "seriesId"
|
||||||
|
)
|
||||||
|
val seasons: List<SeasonWithEpisodes>
|
||||||
|
)
|
||||||
@@ -4,6 +4,7 @@ import androidx.room.Dao
|
|||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import androidx.room.Upsert
|
import androidx.room.Upsert
|
||||||
import hu.bbara.purefin.data.local.room.MovieEntity
|
import hu.bbara.purefin.data.local.room.MovieEntity
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
@@ -17,6 +18,9 @@ interface MovieDao {
|
|||||||
@Query("SELECT * FROM movies")
|
@Query("SELECT * FROM movies")
|
||||||
suspend fun getAll(): List<MovieEntity>
|
suspend fun getAll(): List<MovieEntity>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM movies")
|
||||||
|
fun observeAll(): Flow<List<MovieEntity>>
|
||||||
|
|
||||||
@Query("SELECT * FROM movies WHERE id = :id")
|
@Query("SELECT * FROM movies WHERE id = :id")
|
||||||
suspend fun getById(id: UUID): MovieEntity?
|
suspend fun getById(id: UUID): MovieEntity?
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ package hu.bbara.purefin.data.local.room.dao
|
|||||||
|
|
||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
|
import androidx.room.Transaction
|
||||||
import androidx.room.Upsert
|
import androidx.room.Upsert
|
||||||
import hu.bbara.purefin.data.local.room.SeriesEntity
|
import hu.bbara.purefin.data.local.room.SeriesEntity
|
||||||
|
import hu.bbara.purefin.data.local.room.SeriesWithSeasonsAndEpisodes
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
@@ -17,6 +20,13 @@ interface SeriesDao {
|
|||||||
@Query("SELECT * FROM series")
|
@Query("SELECT * FROM series")
|
||||||
suspend fun getAll(): List<SeriesEntity>
|
suspend fun getAll(): List<SeriesEntity>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM series")
|
||||||
|
fun observeAll(): Flow<List<SeriesEntity>>
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
@Query("SELECT * FROM series WHERE id = :id")
|
||||||
|
fun observeWithContent(id: UUID): Flow<SeriesWithSeasonsAndEpisodes?>
|
||||||
|
|
||||||
@Query("SELECT * FROM series WHERE id = :id")
|
@Query("SELECT * FROM series WHERE id = :id")
|
||||||
suspend fun getById(id: UUID): SeriesEntity?
|
suspend fun getById(id: UUID): SeriesEntity?
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user