mirror of
https://github.com/bbara04/Purefin.git
synced 2026-03-31 17:10:08 +02:00
temp: room db refactor
This commit is contained in:
@@ -1,95 +0,0 @@
|
||||
package hu.bbara.purefin.core.data
|
||||
|
||||
import hu.bbara.purefin.core.data.local.room.OfflineRepository
|
||||
import hu.bbara.purefin.core.data.local.room.OnlineRepository
|
||||
import hu.bbara.purefin.core.data.session.UserSessionRepository
|
||||
import hu.bbara.purefin.core.model.Episode
|
||||
import hu.bbara.purefin.core.model.Library
|
||||
import hu.bbara.purefin.core.model.Media
|
||||
import hu.bbara.purefin.core.model.MediaRepositoryState
|
||||
import hu.bbara.purefin.core.model.Movie
|
||||
import hu.bbara.purefin.core.model.Series
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Active media repository that delegates to either online or offline repository
|
||||
* based on user preference.
|
||||
*/
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Singleton
|
||||
class ActiveMediaRepository @Inject constructor(
|
||||
@OnlineRepository private val onlineRepository: MediaRepository,
|
||||
@OfflineRepository private val offlineRepository: MediaRepository,
|
||||
private val userSessionRepository: UserSessionRepository
|
||||
) : MediaRepository {
|
||||
|
||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
|
||||
// Switch between repositories based on offline mode preference
|
||||
private val activeRepository: StateFlow<MediaRepository> =
|
||||
userSessionRepository.isOfflineMode
|
||||
.map { isOffline ->
|
||||
if (isOffline) offlineRepository else onlineRepository
|
||||
}
|
||||
.stateIn(scope, SharingStarted.Eagerly, onlineRepository)
|
||||
|
||||
// Delegate all MediaRepository interface methods to the active repository
|
||||
override val libraries: StateFlow<List<Library>> =
|
||||
activeRepository.flatMapLatest { it.libraries }
|
||||
.stateIn(scope, SharingStarted.Eagerly, emptyList())
|
||||
|
||||
override val movies: StateFlow<Map<UUID, Movie>> =
|
||||
activeRepository.flatMapLatest { it.movies }
|
||||
.stateIn(scope, SharingStarted.Eagerly, emptyMap())
|
||||
|
||||
override val series: StateFlow<Map<UUID, Series>> =
|
||||
activeRepository.flatMapLatest { it.series }
|
||||
.stateIn(scope, SharingStarted.Eagerly, emptyMap())
|
||||
|
||||
override val episodes: StateFlow<Map<UUID, Episode>> =
|
||||
activeRepository.flatMapLatest { it.episodes }
|
||||
.stateIn(scope, SharingStarted.Eagerly, emptyMap())
|
||||
|
||||
override val state: StateFlow<MediaRepositoryState> =
|
||||
activeRepository.flatMapLatest { it.state }
|
||||
.stateIn(scope, SharingStarted.Eagerly, MediaRepositoryState.Loading)
|
||||
|
||||
override val continueWatching: StateFlow<List<Media>> =
|
||||
activeRepository.flatMapLatest { it.continueWatching }
|
||||
.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>>> =
|
||||
activeRepository.flatMapLatest { it.latestLibraryContent }
|
||||
.stateIn(scope, SharingStarted.Eagerly, emptyMap())
|
||||
|
||||
override fun observeSeriesWithContent(seriesId: UUID): Flow<Series?> =
|
||||
activeRepository.flatMapLatest { it.observeSeriesWithContent(seriesId) }
|
||||
|
||||
override suspend fun ensureReady() {
|
||||
activeRepository.value.ensureReady()
|
||||
}
|
||||
|
||||
override suspend fun updateWatchProgress(mediaId: UUID, positionMs: Long, durationMs: Long) {
|
||||
activeRepository.value.updateWatchProgress(mediaId, positionMs, durationMs)
|
||||
}
|
||||
|
||||
override suspend fun refreshHomeData() {
|
||||
activeRepository.value.refreshHomeData()
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,9 @@ import hu.bbara.purefin.core.data.cache.CachedMediaItem
|
||||
import hu.bbara.purefin.core.data.cache.HomeCache
|
||||
import hu.bbara.purefin.core.data.client.JellyfinApiClient
|
||||
import hu.bbara.purefin.core.data.image.JellyfinImageHelper
|
||||
import hu.bbara.purefin.core.data.local.room.OfflineDatabase
|
||||
import hu.bbara.purefin.core.data.local.room.OfflineRoomMediaLocalDataSource
|
||||
import hu.bbara.purefin.core.data.local.room.RoomMediaLocalDataSource
|
||||
import hu.bbara.purefin.core.data.room.OfflineDatabase
|
||||
import hu.bbara.purefin.core.data.room.local.RoomMediaLocalDataSource
|
||||
import hu.bbara.purefin.core.data.room.offline.OfflineRoomMediaLocalDataSource
|
||||
import hu.bbara.purefin.core.data.session.UserSessionRepository
|
||||
import hu.bbara.purefin.core.model.Episode
|
||||
import hu.bbara.purefin.core.model.Library
|
||||
@@ -185,7 +185,6 @@ class InMemoryMediaRepository @Inject constructor(
|
||||
it.toLibrary()
|
||||
}
|
||||
localDataSource.saveLibraries(emptyLibraries)
|
||||
offlineDataSource.saveLibraries(emptyLibraries)
|
||||
|
||||
val filledLibraries = emptyLibraries.map { library ->
|
||||
return@map loadLibrary(library)
|
||||
|
||||
@@ -4,22 +4,12 @@ import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import hu.bbara.purefin.core.data.local.room.OfflineRepository
|
||||
import hu.bbara.purefin.core.data.local.room.OnlineRepository
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
abstract class MediaRepositoryModule {
|
||||
|
||||
@Binds
|
||||
@OnlineRepository
|
||||
abstract fun bindOnlineMediaRepository(impl: InMemoryMediaRepository): MediaRepository
|
||||
abstract fun bindInMemoryMediaRepository(impl: InMemoryMediaRepository): MediaRepository
|
||||
|
||||
@Binds
|
||||
@OfflineRepository
|
||||
abstract fun bindOfflineMediaRepository(impl: OfflineMediaRepository): MediaRepository
|
||||
|
||||
// Default binding delegates to online/offline based on user preference
|
||||
@Binds
|
||||
abstract fun bindDefaultMediaRepository(impl: ActiveMediaRepository): MediaRepository
|
||||
}
|
||||
|
||||
@@ -1,21 +1,16 @@
|
||||
package hu.bbara.purefin.core.data
|
||||
|
||||
import hu.bbara.purefin.core.data.local.room.OfflineDatabase
|
||||
import hu.bbara.purefin.core.data.local.room.OfflineRoomMediaLocalDataSource
|
||||
import hu.bbara.purefin.core.data.room.OfflineDatabase
|
||||
import hu.bbara.purefin.core.data.room.offline.OfflineRoomMediaLocalDataSource
|
||||
import hu.bbara.purefin.core.model.Episode
|
||||
import hu.bbara.purefin.core.model.Library
|
||||
import hu.bbara.purefin.core.model.Media
|
||||
import hu.bbara.purefin.core.model.MediaRepositoryState
|
||||
import hu.bbara.purefin.core.model.Movie
|
||||
import hu.bbara.purefin.core.model.Series
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
@@ -28,40 +23,23 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class OfflineMediaRepository @Inject constructor(
|
||||
@OfflineDatabase private val localDataSource: OfflineRoomMediaLocalDataSource
|
||||
) : MediaRepository {
|
||||
|
||||
) {
|
||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
|
||||
// Offline repository is always ready (no network loading required)
|
||||
private val _state: MutableStateFlow<MediaRepositoryState> = MutableStateFlow(MediaRepositoryState.Ready)
|
||||
override val state: StateFlow<MediaRepositoryState> = _state.asStateFlow()
|
||||
|
||||
override val libraries: StateFlow<List<Library>> = localDataSource.librariesFlow
|
||||
.stateIn(scope, SharingStarted.Eagerly, emptyList())
|
||||
|
||||
override val movies: StateFlow<Map<UUID, Movie>> = localDataSource.moviesFlow
|
||||
val movies: StateFlow<Map<UUID, Movie>> = localDataSource.moviesFlow
|
||||
.stateIn(scope, SharingStarted.Eagerly, emptyMap())
|
||||
|
||||
override val series: StateFlow<Map<UUID, Series>> = localDataSource.seriesFlow
|
||||
val series: StateFlow<Map<UUID, Series>> = localDataSource.seriesFlow
|
||||
.stateIn(scope, SharingStarted.Eagerly, emptyMap())
|
||||
|
||||
override val episodes: StateFlow<Map<UUID, Episode>> = localDataSource.episodesFlow
|
||||
val episodes: StateFlow<Map<UUID, Episode>> = localDataSource.episodesFlow
|
||||
.stateIn(scope, SharingStarted.Eagerly, emptyMap())
|
||||
|
||||
// Offline mode doesn't support these server-side features
|
||||
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 fun observeSeriesWithContent(seriesId: UUID): Flow<Series?> {
|
||||
fun observeSeriesWithContent(seriesId: UUID): Flow<Series?> {
|
||||
return localDataSource.observeSeriesWithContent(seriesId)
|
||||
}
|
||||
|
||||
override suspend fun ensureReady() {
|
||||
// Offline repository is always ready - no initialization needed
|
||||
}
|
||||
|
||||
override suspend fun updateWatchProgress(mediaId: UUID, positionMs: Long, durationMs: Long) {
|
||||
suspend fun updateWatchProgress(mediaId: UUID, positionMs: Long, durationMs: Long) {
|
||||
if (durationMs <= 0) return
|
||||
val progressPercent = (positionMs.toDouble() / durationMs.toDouble()) * 100.0
|
||||
val watched = progressPercent >= 90.0
|
||||
@@ -69,7 +47,4 @@ class OfflineMediaRepository @Inject constructor(
|
||||
localDataSource.updateWatchProgress(mediaId, progressPercent, watched)
|
||||
}
|
||||
|
||||
override suspend fun refreshHomeData() {
|
||||
// No-op for offline repository - no network refresh available
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
package hu.bbara.purefin.core.data.local.room
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import hu.bbara.purefin.core.data.local.room.dao.CastMemberDao
|
||||
import hu.bbara.purefin.core.data.local.room.dao.EpisodeDao
|
||||
import hu.bbara.purefin.core.data.local.room.dao.LibraryDao
|
||||
import hu.bbara.purefin.core.data.local.room.dao.MovieDao
|
||||
import hu.bbara.purefin.core.data.local.room.dao.SeasonDao
|
||||
import hu.bbara.purefin.core.data.local.room.dao.SeriesDao
|
||||
|
||||
@Database(
|
||||
entities = [
|
||||
MovieEntity::class,
|
||||
SeriesEntity::class,
|
||||
SeasonEntity::class,
|
||||
EpisodeEntity::class,
|
||||
LibraryEntity::class,
|
||||
CastMemberEntity::class
|
||||
],
|
||||
version = 3,
|
||||
exportSchema = false
|
||||
)
|
||||
@TypeConverters(UuidConverters::class)
|
||||
abstract class MediaDatabase : RoomDatabase() {
|
||||
abstract fun movieDao(): MovieDao
|
||||
abstract fun seriesDao(): SeriesDao
|
||||
abstract fun seasonDao(): SeasonDao
|
||||
abstract fun episodeDao(): EpisodeDao
|
||||
abstract fun libraryDao(): LibraryDao
|
||||
abstract fun castMemberDao(): CastMemberDao
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package hu.bbara.purefin.core.data.local.room
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import hu.bbara.purefin.core.data.local.room.dao.CastMemberDao
|
||||
import hu.bbara.purefin.core.data.local.room.dao.EpisodeDao
|
||||
import hu.bbara.purefin.core.data.local.room.dao.LibraryDao
|
||||
import hu.bbara.purefin.core.data.local.room.dao.MovieDao
|
||||
import hu.bbara.purefin.core.data.local.room.dao.SeasonDao
|
||||
import hu.bbara.purefin.core.data.local.room.dao.SeriesDao
|
||||
|
||||
@Database(
|
||||
entities = [
|
||||
MovieEntity::class,
|
||||
SeriesEntity::class,
|
||||
SeasonEntity::class,
|
||||
EpisodeEntity::class,
|
||||
LibraryEntity::class,
|
||||
CastMemberEntity::class
|
||||
],
|
||||
version = 3,
|
||||
exportSchema = false
|
||||
)
|
||||
@TypeConverters(UuidConverters::class)
|
||||
abstract class OfflineMediaDatabase : RoomDatabase() {
|
||||
abstract fun movieDao(): MovieDao
|
||||
abstract fun seriesDao(): SeriesDao
|
||||
abstract fun seasonDao(): SeasonDao
|
||||
abstract fun episodeDao(): EpisodeDao
|
||||
abstract fun libraryDao(): LibraryDao
|
||||
abstract fun castMemberDao(): CastMemberDao
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package hu.bbara.purefin.core.data.local.room
|
||||
package hu.bbara.purefin.core.data.room
|
||||
|
||||
import javax.inject.Qualifier
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package hu.bbara.purefin.core.data.local.room
|
||||
package hu.bbara.purefin.core.data.room
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Room
|
||||
@@ -7,12 +7,16 @@ import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import hu.bbara.purefin.core.data.local.room.dao.CastMemberDao
|
||||
import hu.bbara.purefin.core.data.local.room.dao.EpisodeDao
|
||||
import hu.bbara.purefin.core.data.local.room.dao.LibraryDao
|
||||
import hu.bbara.purefin.core.data.local.room.dao.MovieDao
|
||||
import hu.bbara.purefin.core.data.local.room.dao.SeasonDao
|
||||
import hu.bbara.purefin.core.data.local.room.dao.SeriesDao
|
||||
import hu.bbara.purefin.core.data.room.dao.CastMemberDao
|
||||
import hu.bbara.purefin.core.data.room.dao.EpisodeDao
|
||||
import hu.bbara.purefin.core.data.room.dao.LibraryDao
|
||||
import hu.bbara.purefin.core.data.room.dao.MovieDao
|
||||
import hu.bbara.purefin.core.data.room.dao.SeasonDao
|
||||
import hu.bbara.purefin.core.data.room.dao.SeriesDao
|
||||
import hu.bbara.purefin.core.data.room.local.MediaDatabase
|
||||
import hu.bbara.purefin.core.data.room.local.RoomMediaLocalDataSource
|
||||
import hu.bbara.purefin.core.data.room.offline.OfflineMediaDatabase
|
||||
import hu.bbara.purefin.core.data.room.offline.OfflineRoomMediaLocalDataSource
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@@ -77,13 +81,6 @@ object MediaDatabaseModule {
|
||||
@OfflineDatabase
|
||||
fun provideOfflineEpisodeDao(@OfflineDatabase db: OfflineMediaDatabase) = db.episodeDao()
|
||||
|
||||
@Provides
|
||||
@OfflineDatabase
|
||||
fun provideOfflineCastMemberDao(@OfflineDatabase db: OfflineMediaDatabase) = db.castMemberDao()
|
||||
|
||||
@Provides
|
||||
@OfflineDatabase
|
||||
fun provideOfflineLibraryDao(@OfflineDatabase db: OfflineMediaDatabase) = db.libraryDao()
|
||||
|
||||
// Data Sources
|
||||
@Provides
|
||||
@@ -109,11 +106,9 @@ object MediaDatabaseModule {
|
||||
@OfflineDatabase movieDao: MovieDao,
|
||||
@OfflineDatabase seriesDao: SeriesDao,
|
||||
@OfflineDatabase seasonDao: SeasonDao,
|
||||
@OfflineDatabase episodeDao: EpisodeDao,
|
||||
@OfflineDatabase castMemberDao: CastMemberDao,
|
||||
@OfflineDatabase libraryDao: LibraryDao
|
||||
@OfflineDatabase episodeDao: EpisodeDao
|
||||
): OfflineRoomMediaLocalDataSource = OfflineRoomMediaLocalDataSource(
|
||||
database, movieDao, seriesDao, seasonDao, episodeDao, castMemberDao, libraryDao
|
||||
database, movieDao, seriesDao, seasonDao, episodeDao
|
||||
)
|
||||
|
||||
// Default (unqualified) data source for backward compatibility
|
||||
@@ -1,7 +1,12 @@
|
||||
package hu.bbara.purefin.core.data.local.room
|
||||
package hu.bbara.purefin.core.data.room
|
||||
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Relation
|
||||
import hu.bbara.purefin.core.data.room.entity.EpisodeEntity
|
||||
import hu.bbara.purefin.core.data.room.entity.LibraryEntity
|
||||
import hu.bbara.purefin.core.data.room.entity.MovieEntity
|
||||
import hu.bbara.purefin.core.data.room.entity.SeasonEntity
|
||||
import hu.bbara.purefin.core.data.room.entity.SeriesEntity
|
||||
|
||||
data class SeasonWithEpisodes(
|
||||
@Embedded val season: SeasonEntity,
|
||||
@@ -1,4 +1,4 @@
|
||||
package hu.bbara.purefin.core.data.local.room
|
||||
package hu.bbara.purefin.core.data.room
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import java.util.UUID
|
||||
@@ -1,9 +1,9 @@
|
||||
package hu.bbara.purefin.core.data.local.room.dao
|
||||
package hu.bbara.purefin.core.data.room.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import androidx.room.Upsert
|
||||
import hu.bbara.purefin.core.data.local.room.CastMemberEntity
|
||||
import hu.bbara.purefin.core.data.room.entity.CastMemberEntity
|
||||
import java.util.UUID
|
||||
|
||||
@Dao
|
||||
@@ -1,9 +1,9 @@
|
||||
package hu.bbara.purefin.core.data.local.room.dao
|
||||
package hu.bbara.purefin.core.data.room.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import androidx.room.Upsert
|
||||
import hu.bbara.purefin.core.data.local.room.EpisodeEntity
|
||||
import hu.bbara.purefin.core.data.room.entity.EpisodeEntity
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import java.util.UUID
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package hu.bbara.purefin.core.data.local.room.dao
|
||||
package hu.bbara.purefin.core.data.room.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import androidx.room.Upsert
|
||||
import hu.bbara.purefin.core.data.local.room.LibraryEntity
|
||||
import hu.bbara.purefin.core.data.local.room.LibraryWithContent
|
||||
import hu.bbara.purefin.core.data.room.LibraryWithContent
|
||||
import hu.bbara.purefin.core.data.room.entity.LibraryEntity
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
@@ -1,9 +1,9 @@
|
||||
package hu.bbara.purefin.core.data.local.room.dao
|
||||
package hu.bbara.purefin.core.data.room.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import androidx.room.Upsert
|
||||
import hu.bbara.purefin.core.data.local.room.MovieEntity
|
||||
import hu.bbara.purefin.core.data.room.entity.MovieEntity
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import java.util.UUID
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package hu.bbara.purefin.core.data.local.room.dao
|
||||
package hu.bbara.purefin.core.data.room.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import androidx.room.Upsert
|
||||
import hu.bbara.purefin.core.data.local.room.SeasonEntity
|
||||
import hu.bbara.purefin.core.data.room.entity.SeasonEntity
|
||||
import java.util.UUID
|
||||
|
||||
@Dao
|
||||
@@ -1,11 +1,11 @@
|
||||
package hu.bbara.purefin.core.data.local.room.dao
|
||||
package hu.bbara.purefin.core.data.room.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import androidx.room.Upsert
|
||||
import hu.bbara.purefin.core.data.local.room.SeriesEntity
|
||||
import hu.bbara.purefin.core.data.local.room.SeriesWithSeasonsAndEpisodes
|
||||
import hu.bbara.purefin.core.data.room.SeriesWithSeasonsAndEpisodes
|
||||
import hu.bbara.purefin.core.data.room.entity.SeriesEntity
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import java.util.UUID
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package hu.bbara.purefin.core.data.local.room
|
||||
package hu.bbara.purefin.core.data.room.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Index
|
||||
@@ -1,4 +1,4 @@
|
||||
package hu.bbara.purefin.core.data.local.room
|
||||
package hu.bbara.purefin.core.data.room.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
@@ -1,4 +1,4 @@
|
||||
package hu.bbara.purefin.core.data.local.room
|
||||
package hu.bbara.purefin.core.data.room.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
@@ -1,4 +1,4 @@
|
||||
package hu.bbara.purefin.core.data.local.room
|
||||
package hu.bbara.purefin.core.data.room.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
@@ -1,4 +1,4 @@
|
||||
package hu.bbara.purefin.core.data.local.room
|
||||
package hu.bbara.purefin.core.data.room.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
@@ -20,7 +20,7 @@ import java.util.UUID
|
||||
)
|
||||
data class SeasonEntity(
|
||||
@PrimaryKey val id: UUID,
|
||||
val seriesId: UUID,
|
||||
val seriesId: UUID?,
|
||||
val name: String,
|
||||
val index: Int,
|
||||
val unwatchedEpisodeCount: Int,
|
||||
@@ -1,4 +1,4 @@
|
||||
package hu.bbara.purefin.core.data.local.room
|
||||
package hu.bbara.purefin.core.data.room.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
@@ -12,14 +12,15 @@ import java.util.UUID
|
||||
ForeignKey(
|
||||
entity = LibraryEntity::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["libraryId"]
|
||||
childColumns = ["libraryId"],
|
||||
deferred = true
|
||||
),
|
||||
],
|
||||
indices = [Index("libraryId")]
|
||||
)
|
||||
data class SeriesEntity(
|
||||
@PrimaryKey val id: UUID,
|
||||
val libraryId: UUID,
|
||||
val libraryId: UUID?,
|
||||
val name: String,
|
||||
val synopsis: String,
|
||||
val year: String,
|
||||
@@ -0,0 +1,40 @@
|
||||
package hu.bbara.purefin.core.data.room.local
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import hu.bbara.purefin.core.data.room.UuidConverters
|
||||
import hu.bbara.purefin.core.data.room.dao.CastMemberDao
|
||||
import hu.bbara.purefin.core.data.room.dao.EpisodeDao
|
||||
import hu.bbara.purefin.core.data.room.dao.LibraryDao
|
||||
import hu.bbara.purefin.core.data.room.dao.MovieDao
|
||||
import hu.bbara.purefin.core.data.room.dao.SeasonDao
|
||||
import hu.bbara.purefin.core.data.room.dao.SeriesDao
|
||||
import hu.bbara.purefin.core.data.room.entity.CastMemberEntity
|
||||
import hu.bbara.purefin.core.data.room.entity.EpisodeEntity
|
||||
import hu.bbara.purefin.core.data.room.entity.LibraryEntity
|
||||
import hu.bbara.purefin.core.data.room.entity.MovieEntity
|
||||
import hu.bbara.purefin.core.data.room.entity.SeasonEntity
|
||||
import hu.bbara.purefin.core.data.room.entity.SeriesEntity
|
||||
|
||||
@Database(
|
||||
entities = [
|
||||
MovieEntity::class,
|
||||
SeriesEntity::class,
|
||||
SeasonEntity::class,
|
||||
EpisodeEntity::class,
|
||||
LibraryEntity::class,
|
||||
CastMemberEntity::class
|
||||
],
|
||||
version = 3,
|
||||
exportSchema = false
|
||||
)
|
||||
@TypeConverters(UuidConverters::class)
|
||||
abstract class MediaDatabase : RoomDatabase() {
|
||||
abstract fun movieDao(): MovieDao
|
||||
abstract fun seriesDao(): SeriesDao
|
||||
abstract fun seasonDao(): SeasonDao
|
||||
abstract fun episodeDao(): EpisodeDao
|
||||
abstract fun libraryDao(): LibraryDao
|
||||
abstract fun castMemberDao(): CastMemberDao
|
||||
}
|
||||
@@ -1,12 +1,18 @@
|
||||
package hu.bbara.purefin.core.data.local.room
|
||||
package hu.bbara.purefin.core.data.room.local
|
||||
|
||||
import androidx.room.withTransaction
|
||||
import hu.bbara.purefin.core.data.local.room.dao.CastMemberDao
|
||||
import hu.bbara.purefin.core.data.local.room.dao.EpisodeDao
|
||||
import hu.bbara.purefin.core.data.local.room.dao.LibraryDao
|
||||
import hu.bbara.purefin.core.data.local.room.dao.MovieDao
|
||||
import hu.bbara.purefin.core.data.local.room.dao.SeasonDao
|
||||
import hu.bbara.purefin.core.data.local.room.dao.SeriesDao
|
||||
import hu.bbara.purefin.core.data.room.dao.CastMemberDao
|
||||
import hu.bbara.purefin.core.data.room.dao.EpisodeDao
|
||||
import hu.bbara.purefin.core.data.room.dao.LibraryDao
|
||||
import hu.bbara.purefin.core.data.room.dao.MovieDao
|
||||
import hu.bbara.purefin.core.data.room.dao.SeasonDao
|
||||
import hu.bbara.purefin.core.data.room.dao.SeriesDao
|
||||
import hu.bbara.purefin.core.data.room.entity.CastMemberEntity
|
||||
import hu.bbara.purefin.core.data.room.entity.EpisodeEntity
|
||||
import hu.bbara.purefin.core.data.room.entity.LibraryEntity
|
||||
import hu.bbara.purefin.core.data.room.entity.MovieEntity
|
||||
import hu.bbara.purefin.core.data.room.entity.SeasonEntity
|
||||
import hu.bbara.purefin.core.data.room.entity.SeriesEntity
|
||||
import hu.bbara.purefin.core.model.CastMember
|
||||
import hu.bbara.purefin.core.model.Episode
|
||||
import hu.bbara.purefin.core.model.Library
|
||||
@@ -0,0 +1,32 @@
|
||||
package hu.bbara.purefin.core.data.room.offline
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import hu.bbara.purefin.core.data.room.UuidConverters
|
||||
import hu.bbara.purefin.core.data.room.dao.EpisodeDao
|
||||
import hu.bbara.purefin.core.data.room.dao.MovieDao
|
||||
import hu.bbara.purefin.core.data.room.dao.SeasonDao
|
||||
import hu.bbara.purefin.core.data.room.dao.SeriesDao
|
||||
import hu.bbara.purefin.core.data.room.entity.EpisodeEntity
|
||||
import hu.bbara.purefin.core.data.room.entity.MovieEntity
|
||||
import hu.bbara.purefin.core.data.room.entity.SeasonEntity
|
||||
import hu.bbara.purefin.core.data.room.entity.SeriesEntity
|
||||
|
||||
@Database(
|
||||
entities = [
|
||||
MovieEntity::class,
|
||||
SeriesEntity::class,
|
||||
SeasonEntity::class,
|
||||
EpisodeEntity::class,
|
||||
],
|
||||
version = 3,
|
||||
exportSchema = false
|
||||
)
|
||||
@TypeConverters(UuidConverters::class)
|
||||
abstract class OfflineMediaDatabase : RoomDatabase() {
|
||||
abstract fun movieDao(): MovieDao
|
||||
abstract fun seriesDao(): SeriesDao
|
||||
abstract fun seasonDao(): SeasonDao
|
||||
abstract fun episodeDao(): EpisodeDao
|
||||
}
|
||||
@@ -1,21 +1,20 @@
|
||||
package hu.bbara.purefin.core.data.local.room
|
||||
package hu.bbara.purefin.core.data.room.offline
|
||||
|
||||
import androidx.room.withTransaction
|
||||
import hu.bbara.purefin.core.data.local.room.dao.CastMemberDao
|
||||
import hu.bbara.purefin.core.data.local.room.dao.EpisodeDao
|
||||
import hu.bbara.purefin.core.data.local.room.dao.LibraryDao
|
||||
import hu.bbara.purefin.core.data.local.room.dao.MovieDao
|
||||
import hu.bbara.purefin.core.data.local.room.dao.SeasonDao
|
||||
import hu.bbara.purefin.core.data.local.room.dao.SeriesDao
|
||||
import hu.bbara.purefin.core.model.CastMember
|
||||
import hu.bbara.purefin.core.data.room.dao.EpisodeDao
|
||||
import hu.bbara.purefin.core.data.room.dao.MovieDao
|
||||
import hu.bbara.purefin.core.data.room.dao.SeasonDao
|
||||
import hu.bbara.purefin.core.data.room.dao.SeriesDao
|
||||
import hu.bbara.purefin.core.data.room.entity.EpisodeEntity
|
||||
import hu.bbara.purefin.core.data.room.entity.MovieEntity
|
||||
import hu.bbara.purefin.core.data.room.entity.SeasonEntity
|
||||
import hu.bbara.purefin.core.data.room.entity.SeriesEntity
|
||||
import hu.bbara.purefin.core.model.Episode
|
||||
import hu.bbara.purefin.core.model.Library
|
||||
import hu.bbara.purefin.core.model.Movie
|
||||
import hu.bbara.purefin.core.model.Season
|
||||
import hu.bbara.purefin.core.model.Series
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.jellyfin.sdk.model.api.CollectionType
|
||||
import java.util.UUID
|
||||
import javax.inject.Singleton
|
||||
|
||||
@@ -26,31 +25,18 @@ class OfflineRoomMediaLocalDataSource(
|
||||
private val seriesDao: SeriesDao,
|
||||
private val seasonDao: SeasonDao,
|
||||
private val episodeDao: EpisodeDao,
|
||||
private val castMemberDao: CastMemberDao,
|
||||
private val libraryDao: LibraryDao
|
||||
) {
|
||||
|
||||
// Lightweight Flows for list screens (home, library)
|
||||
val librariesFlow: Flow<List<Library>> = libraryDao.observeAllWithContent()
|
||||
.map { relation ->
|
||||
relation.map {
|
||||
it.library.toDomain(
|
||||
movies = it.movies.map { e -> e.toDomain(cast = emptyList()) },
|
||||
series = it.series.map { e -> e.toDomain(seasons = emptyList(), cast = emptyList()) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val moviesFlow: Flow<Map<UUID, Movie>> = movieDao.observeAll()
|
||||
.map { entities -> entities.associate { it.id to it.toDomain(cast = emptyList()) } }
|
||||
.map { entities -> entities.associate { it.id to it.toDomain() } }
|
||||
|
||||
val seriesFlow: Flow<Map<UUID, Series>> = seriesDao.observeAll()
|
||||
.map { entities ->
|
||||
entities.associate { it.id to it.toDomain(seasons = emptyList(), cast = emptyList()) }
|
||||
entities.associate { it.id to it.toDomain(seasons = emptyList()) }
|
||||
}
|
||||
|
||||
val episodesFlow: Flow<Map<UUID, Episode>> = episodeDao.observeAll()
|
||||
.map { entities -> entities.associate { it.id to it.toDomain(cast = emptyList()) } }
|
||||
.map { entities -> entities.associate { it.id to it.toDomain() } }
|
||||
|
||||
// Full content Flow for series detail screen (scoped to one series)
|
||||
fun observeSeriesWithContent(seriesId: UUID): Flow<Series?> =
|
||||
@@ -59,21 +45,12 @@ class OfflineRoomMediaLocalDataSource(
|
||||
it.series.toDomain(
|
||||
seasons = it.seasons.map { swe ->
|
||||
swe.season.toDomain(
|
||||
episodes = swe.episodes.map { ep -> ep.toDomain(cast = emptyList()) }
|
||||
episodes = swe.episodes.map { ep -> ep.toDomain() }
|
||||
)
|
||||
},
|
||||
cast = emptyList()
|
||||
)
|
||||
} )
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun saveLibraries(libraries: List<Library>) {
|
||||
database.withTransaction {
|
||||
libraryDao.deleteAll()
|
||||
libraryDao.upsertAll(libraries.map { it.toEntity() })
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun saveMovies(movies: List<Movie>) {
|
||||
database.withTransaction {
|
||||
movieDao.upsertAll(movies.map { it.toEntity() })
|
||||
@@ -115,15 +92,13 @@ class OfflineRoomMediaLocalDataSource(
|
||||
suspend fun getMovies(): List<Movie> {
|
||||
val movies = movieDao.getAll()
|
||||
return movies.map { entity ->
|
||||
val cast = castMemberDao.getByMovieId(entity.id).map { it.toDomain() }
|
||||
entity.toDomain(cast)
|
||||
entity.toDomain()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getMovie(id: UUID): Movie? {
|
||||
val entity = movieDao.getById(id) ?: return null
|
||||
val cast = castMemberDao.getByMovieId(id).map { it.toDomain() }
|
||||
return entity.toDomain(cast)
|
||||
return entity.toDomain()
|
||||
}
|
||||
|
||||
suspend fun getSeries(): List<Series> {
|
||||
@@ -136,24 +111,21 @@ class OfflineRoomMediaLocalDataSource(
|
||||
|
||||
private suspend fun getSeriesInternal(id: UUID, includeContent: Boolean): Series? {
|
||||
val entity = seriesDao.getById(id) ?: return null
|
||||
val cast = castMemberDao.getBySeriesId(id).map { it.toDomain() }
|
||||
val seasons = if (includeContent) {
|
||||
seasonDao.getBySeriesId(id).map { seasonEntity ->
|
||||
val episodes = episodeDao.getBySeasonId(seasonEntity.id).map { episodeEntity ->
|
||||
val episodeCast = castMemberDao.getByEpisodeId(episodeEntity.id).map { it.toDomain() }
|
||||
episodeEntity.toDomain(episodeCast)
|
||||
episodeEntity.toDomain()
|
||||
}
|
||||
seasonEntity.toDomain(episodes)
|
||||
}
|
||||
} else emptyList()
|
||||
return entity.toDomain(seasons, cast)
|
||||
return entity.toDomain(seasons)
|
||||
}
|
||||
|
||||
suspend fun getSeason(seriesId: UUID, seasonId: UUID): Season? {
|
||||
val seasonEntity = seasonDao.getById(seasonId) ?: return null
|
||||
val episodes = episodeDao.getBySeasonId(seasonId).map { episodeEntity ->
|
||||
val episodeCast = castMemberDao.getByEpisodeId(episodeEntity.id).map { it.toDomain() }
|
||||
episodeEntity.toDomain(episodeCast)
|
||||
episodeEntity.toDomain()
|
||||
}
|
||||
return seasonEntity.toDomain(episodes)
|
||||
}
|
||||
@@ -161,8 +133,7 @@ class OfflineRoomMediaLocalDataSource(
|
||||
suspend fun getSeasons(seriesId: UUID): List<Season> {
|
||||
return seasonDao.getBySeriesId(seriesId).map { seasonEntity ->
|
||||
val episodes = episodeDao.getBySeasonId(seasonEntity.id).map { episodeEntity ->
|
||||
val episodeCast = castMemberDao.getByEpisodeId(episodeEntity.id).map { it.toDomain() }
|
||||
episodeEntity.toDomain(episodeCast)
|
||||
episodeEntity.toDomain()
|
||||
}
|
||||
seasonEntity.toDomain(episodes)
|
||||
}
|
||||
@@ -170,14 +141,12 @@ class OfflineRoomMediaLocalDataSource(
|
||||
|
||||
suspend fun getEpisode(seriesId: UUID, seasonId: UUID, episodeId: UUID): Episode? {
|
||||
val episodeEntity = episodeDao.getById(episodeId) ?: return null
|
||||
val cast = castMemberDao.getByEpisodeId(episodeId).map { it.toDomain() }
|
||||
return episodeEntity.toDomain(cast)
|
||||
return episodeEntity.toDomain()
|
||||
}
|
||||
|
||||
suspend fun getEpisodeById(episodeId: UUID): Episode? {
|
||||
val episodeEntity = episodeDao.getById(episodeId) ?: return null
|
||||
val cast = castMemberDao.getByEpisodeId(episodeId).map { it.toDomain() }
|
||||
return episodeEntity.toDomain(cast)
|
||||
return episodeEntity.toDomain()
|
||||
}
|
||||
|
||||
suspend fun updateWatchProgress(mediaId: UUID, progress: Double?, watched: Boolean) {
|
||||
@@ -199,33 +168,10 @@ class OfflineRoomMediaLocalDataSource(
|
||||
|
||||
suspend fun getEpisodesBySeries(seriesId: UUID): List<Episode> {
|
||||
return episodeDao.getBySeriesId(seriesId).map { episodeEntity ->
|
||||
val cast = castMemberDao.getByEpisodeId(episodeEntity.id).map { it.toDomain() }
|
||||
episodeEntity.toDomain(cast)
|
||||
episodeEntity.toDomain()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Library.toEntity() = LibraryEntity(
|
||||
id = id,
|
||||
name = name,
|
||||
type = when (type) {
|
||||
CollectionType.MOVIES -> "MOVIES"
|
||||
CollectionType.TVSHOWS -> "TVSHOWS"
|
||||
else -> throw UnsupportedOperationException("Unsupported library type: $type")
|
||||
}
|
||||
)
|
||||
|
||||
private fun LibraryEntity.toDomain(series: List<Series>, movies: List<Movie>) = Library(
|
||||
id = id,
|
||||
name = name,
|
||||
type = when (type) {
|
||||
"MOVIES" -> CollectionType.MOVIES
|
||||
"TVSHOWS" -> CollectionType.TVSHOWS
|
||||
else -> throw UnsupportedOperationException("Unsupported library type: $type")
|
||||
},
|
||||
movies = if (type == "MOVIES") movies else null,
|
||||
series = if (type == "TVSHOWS") series else null,
|
||||
)
|
||||
|
||||
private fun Movie.toEntity() = MovieEntity(
|
||||
id = id,
|
||||
libraryId = libraryId,
|
||||
@@ -278,7 +224,7 @@ class OfflineRoomMediaLocalDataSource(
|
||||
heroImageUrl = heroImageUrl
|
||||
)
|
||||
|
||||
private fun MovieEntity.toDomain(cast: List<CastMember>) = Movie(
|
||||
private fun MovieEntity.toDomain() = Movie(
|
||||
id = id,
|
||||
libraryId = libraryId,
|
||||
title = title,
|
||||
@@ -292,10 +238,10 @@ class OfflineRoomMediaLocalDataSource(
|
||||
heroImageUrl = heroImageUrl,
|
||||
audioTrack = audioTrack,
|
||||
subtitles = subtitles,
|
||||
cast = cast
|
||||
cast = emptyList()
|
||||
)
|
||||
|
||||
private fun SeriesEntity.toDomain(seasons: List<Season>, cast: List<CastMember>) = Series(
|
||||
private fun SeriesEntity.toDomain(seasons: List<Season>) = Series(
|
||||
id = id,
|
||||
libraryId = libraryId,
|
||||
name = name,
|
||||
@@ -305,7 +251,7 @@ class OfflineRoomMediaLocalDataSource(
|
||||
unwatchedEpisodeCount = unwatchedEpisodeCount,
|
||||
seasonCount = seasonCount,
|
||||
seasons = seasons,
|
||||
cast = cast
|
||||
cast = emptyList()
|
||||
)
|
||||
|
||||
private fun SeasonEntity.toDomain(episodes: List<Episode>) = Season(
|
||||
@@ -318,7 +264,7 @@ class OfflineRoomMediaLocalDataSource(
|
||||
episodes = episodes
|
||||
)
|
||||
|
||||
private fun EpisodeEntity.toDomain(cast: List<CastMember>) = Episode(
|
||||
private fun EpisodeEntity.toDomain() = Episode(
|
||||
id = id,
|
||||
seriesId = seriesId,
|
||||
seasonId = seasonId,
|
||||
@@ -332,33 +278,6 @@ class OfflineRoomMediaLocalDataSource(
|
||||
watched = watched,
|
||||
format = format,
|
||||
heroImageUrl = heroImageUrl,
|
||||
cast = cast
|
||||
)
|
||||
|
||||
private fun CastMember.toMovieEntity(movieId: UUID) = CastMemberEntity(
|
||||
name = name,
|
||||
role = role,
|
||||
imageUrl = imageUrl,
|
||||
movieId = movieId
|
||||
)
|
||||
|
||||
private fun CastMember.toSeriesEntity(seriesId: UUID) = CastMemberEntity(
|
||||
name = name,
|
||||
role = role,
|
||||
imageUrl = imageUrl,
|
||||
seriesId = seriesId
|
||||
)
|
||||
|
||||
private fun CastMember.toEpisodeEntity(episodeId: UUID) = CastMemberEntity(
|
||||
name = name,
|
||||
role = role,
|
||||
imageUrl = imageUrl,
|
||||
episodeId = episodeId
|
||||
)
|
||||
|
||||
private fun CastMemberEntity.toDomain() = CastMember(
|
||||
name = name,
|
||||
role = role,
|
||||
imageUrl = imageUrl
|
||||
cast = emptyList()
|
||||
)
|
||||
}
|
||||
@@ -11,9 +11,9 @@ import androidx.media3.exoplayer.offline.DownloadRequest
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import hu.bbara.purefin.core.data.client.JellyfinApiClient
|
||||
import hu.bbara.purefin.core.data.image.JellyfinImageHelper
|
||||
import hu.bbara.purefin.core.data.local.room.OfflineDatabase
|
||||
import hu.bbara.purefin.core.data.local.room.OfflineRoomMediaLocalDataSource
|
||||
import hu.bbara.purefin.core.data.local.room.dao.MovieDao
|
||||
import hu.bbara.purefin.core.data.room.OfflineDatabase
|
||||
import hu.bbara.purefin.core.data.room.dao.MovieDao
|
||||
import hu.bbara.purefin.core.data.room.offline.OfflineRoomMediaLocalDataSource
|
||||
import hu.bbara.purefin.core.data.session.UserSessionRepository
|
||||
import hu.bbara.purefin.core.model.Movie
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
||||
Reference in New Issue
Block a user