mirror of
https://github.com/bbara04/Purefin.git
synced 2026-04-01 01:30:08 +02:00
feat: Added library to the database scheme and to the MediaRepository. LibraryViewModel now uses it.
This commit is contained in:
@@ -17,44 +17,38 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.stateIn
|
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 org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
|
import org.jellyfin.sdk.model.api.CollectionType
|
||||||
import org.jellyfin.sdk.model.api.ImageType
|
import org.jellyfin.sdk.model.api.ImageType
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import kotlin.collections.emptyList
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class LibraryViewModel @Inject constructor(
|
class LibraryViewModel @Inject constructor(
|
||||||
private val mediaRepository: MediaRepository,
|
private val mediaRepository: MediaRepository,
|
||||||
private val userSessionRepository: UserSessionRepository,
|
|
||||||
private val jellyfinApiClient: JellyfinApiClient,
|
|
||||||
private val navigationManager: NavigationManager
|
private val navigationManager: NavigationManager
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _url = userSessionRepository.serverUrl.stateIn(
|
private val selectedLibrary = MutableStateFlow<UUID?>(null)
|
||||||
scope = viewModelScope,
|
|
||||||
started = SharingStarted.Eagerly,
|
|
||||||
initialValue = ""
|
|
||||||
)
|
|
||||||
|
|
||||||
private val _libraryItems = MutableStateFlow<List<Media>>(emptyList())
|
val contents: StateFlow<List<PosterItem>> = combine(selectedLibrary, mediaRepository.libraries) {
|
||||||
|
libraryId, libraries ->
|
||||||
val contents: StateFlow<List<PosterItem>> = combine(
|
if (libraryId == null) {
|
||||||
_libraryItems,
|
return@combine emptyList()
|
||||||
mediaRepository.movies,
|
}
|
||||||
mediaRepository.series
|
val library = libraries.find { it.id == libraryId } ?: return@combine emptyList()
|
||||||
) { items, moviesMap, seriesMap ->
|
when (library.type) {
|
||||||
items.mapNotNull { media ->
|
CollectionType.TVSHOWS -> library.series!!.map { series ->
|
||||||
when (media) {
|
PosterItem(type = BaseItemKind.SERIES, series = series)
|
||||||
is Media.MovieMedia -> moviesMap[media.movieId]?.let {
|
|
||||||
PosterItem(type = BaseItemKind.MOVIE, movie = it)
|
|
||||||
}
|
|
||||||
is Media.SeriesMedia -> seriesMap[media.seriesId]?.let {
|
|
||||||
PosterItem(type = BaseItemKind.SERIES, series = it)
|
|
||||||
}
|
|
||||||
else -> null
|
|
||||||
}
|
}
|
||||||
|
CollectionType.MOVIES -> library.movies!!.map { movie ->
|
||||||
|
PosterItem(type = BaseItemKind.MOVIE, movie = movie)
|
||||||
|
}
|
||||||
|
else -> emptyList()
|
||||||
}
|
}
|
||||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList())
|
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList())
|
||||||
|
|
||||||
@@ -86,22 +80,7 @@ class LibraryViewModel @Inject constructor(
|
|||||||
|
|
||||||
fun selectLibrary(libraryId: UUID) {
|
fun selectLibrary(libraryId: UUID) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val libraryItems = jellyfinApiClient.getLibraryContent(libraryId)
|
selectedLibrary.value = libraryId
|
||||||
_libraryItems.value = libraryItems.map {
|
|
||||||
when (it.type) {
|
|
||||||
BaseItemKind.MOVIE -> Media.MovieMedia(movieId = it.id)
|
|
||||||
BaseItemKind.SERIES -> Media.SeriesMedia(seriesId = it.id)
|
|
||||||
else -> throw UnsupportedOperationException("Unsupported item type: ${it.type}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getImageUrl(itemId: UUID, type: ImageType): String {
|
|
||||||
return JellyfinImageHelper.toImageUrl(
|
|
||||||
url = _url.value,
|
|
||||||
itemId = itemId,
|
|
||||||
type = type
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package hu.bbara.purefin.data
|
|||||||
import hu.bbara.purefin.data.local.room.OfflineRepository
|
import hu.bbara.purefin.data.local.room.OfflineRepository
|
||||||
import hu.bbara.purefin.data.local.room.OnlineRepository
|
import hu.bbara.purefin.data.local.room.OnlineRepository
|
||||||
import hu.bbara.purefin.data.model.Episode
|
import hu.bbara.purefin.data.model.Episode
|
||||||
|
import hu.bbara.purefin.data.model.Library
|
||||||
import hu.bbara.purefin.data.model.Media
|
import hu.bbara.purefin.data.model.Media
|
||||||
import hu.bbara.purefin.data.model.Movie
|
import hu.bbara.purefin.data.model.Movie
|
||||||
import hu.bbara.purefin.data.model.Series
|
import hu.bbara.purefin.data.model.Series
|
||||||
@@ -44,6 +45,10 @@ class ActiveMediaRepository @Inject constructor(
|
|||||||
.stateIn(scope, SharingStarted.Eagerly, onlineRepository)
|
.stateIn(scope, SharingStarted.Eagerly, onlineRepository)
|
||||||
|
|
||||||
// Delegate all MediaRepository interface methods to the active repository
|
// 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>> =
|
override val movies: StateFlow<Map<UUID, Movie>> =
|
||||||
activeRepository.flatMapLatest { it.movies }
|
activeRepository.flatMapLatest { it.movies }
|
||||||
.stateIn(scope, SharingStarted.Eagerly, emptyMap())
|
.stateIn(scope, SharingStarted.Eagerly, emptyMap())
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ class InMemoryMediaRepository @Inject constructor(
|
|||||||
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()
|
||||||
|
|
||||||
|
override val libraries: StateFlow<List<Library>> = localDataSource.librariesFlow
|
||||||
|
.stateIn(scope, SharingStarted.Eagerly, emptyList())
|
||||||
override val movies: StateFlow<Map<UUID, Movie>> = localDataSource.moviesFlow
|
override val movies: StateFlow<Map<UUID, Movie>> = localDataSource.moviesFlow
|
||||||
.stateIn(scope, SharingStarted.Eagerly, emptyMap())
|
.stateIn(scope, SharingStarted.Eagerly, emptyMap())
|
||||||
|
|
||||||
@@ -108,6 +110,8 @@ class InMemoryMediaRepository @Inject constructor(
|
|||||||
val emptyLibraries = filteredLibraries.map {
|
val emptyLibraries = filteredLibraries.map {
|
||||||
it.toLibrary()
|
it.toLibrary()
|
||||||
}
|
}
|
||||||
|
localDataSource.saveLibraries(emptyLibraries)
|
||||||
|
|
||||||
val filledLibraries = emptyLibraries.map { library ->
|
val filledLibraries = emptyLibraries.map { library ->
|
||||||
return@map loadLibrary(library)
|
return@map loadLibrary(library)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package hu.bbara.purefin.data
|
package hu.bbara.purefin.data
|
||||||
|
|
||||||
import hu.bbara.purefin.data.model.Episode
|
import hu.bbara.purefin.data.model.Episode
|
||||||
|
import hu.bbara.purefin.data.model.Library
|
||||||
import hu.bbara.purefin.data.model.Media
|
import hu.bbara.purefin.data.model.Media
|
||||||
import hu.bbara.purefin.data.model.Movie
|
import hu.bbara.purefin.data.model.Movie
|
||||||
import hu.bbara.purefin.data.model.Series
|
import hu.bbara.purefin.data.model.Series
|
||||||
@@ -10,6 +11,7 @@ import java.util.UUID
|
|||||||
|
|
||||||
interface MediaRepository {
|
interface MediaRepository {
|
||||||
|
|
||||||
|
val libraries: StateFlow<List<Library>>
|
||||||
val movies: StateFlow<Map<UUID, Movie>>
|
val movies: StateFlow<Map<UUID, Movie>>
|
||||||
val series: StateFlow<Map<UUID, Series>>
|
val series: StateFlow<Map<UUID, Series>>
|
||||||
val episodes: StateFlow<Map<UUID, Episode>>
|
val episodes: StateFlow<Map<UUID, Episode>>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package hu.bbara.purefin.data
|
|||||||
import hu.bbara.purefin.data.local.room.OfflineDatabase
|
import hu.bbara.purefin.data.local.room.OfflineDatabase
|
||||||
import hu.bbara.purefin.data.local.room.OfflineRoomMediaLocalDataSource
|
import hu.bbara.purefin.data.local.room.OfflineRoomMediaLocalDataSource
|
||||||
import hu.bbara.purefin.data.model.Episode
|
import hu.bbara.purefin.data.model.Episode
|
||||||
|
import hu.bbara.purefin.data.model.Library
|
||||||
import hu.bbara.purefin.data.model.Media
|
import hu.bbara.purefin.data.model.Media
|
||||||
import hu.bbara.purefin.data.model.Movie
|
import hu.bbara.purefin.data.model.Movie
|
||||||
import hu.bbara.purefin.data.model.Series
|
import hu.bbara.purefin.data.model.Series
|
||||||
@@ -34,6 +35,9 @@ class OfflineMediaRepository @Inject constructor(
|
|||||||
private val _state: MutableStateFlow<MediaRepositoryState> = MutableStateFlow(MediaRepositoryState.Ready)
|
private val _state: MutableStateFlow<MediaRepositoryState> = MutableStateFlow(MediaRepositoryState.Ready)
|
||||||
override val state: StateFlow<MediaRepositoryState> = _state.asStateFlow()
|
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
|
override val movies: StateFlow<Map<UUID, Movie>> = localDataSource.moviesFlow
|
||||||
.stateIn(scope, SharingStarted.Eagerly, emptyMap())
|
.stateIn(scope, SharingStarted.Eagerly, emptyMap())
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package hu.bbara.purefin.data.local.room
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
@Entity(tableName = "library")
|
||||||
|
data class LibraryEntity (
|
||||||
|
@PrimaryKey
|
||||||
|
val id: UUID,
|
||||||
|
val name: String,
|
||||||
|
val type: String,
|
||||||
|
)
|
||||||
@@ -8,16 +8,17 @@ import hu.bbara.purefin.data.local.room.dao.EpisodeDao
|
|||||||
import hu.bbara.purefin.data.local.room.dao.MovieDao
|
import hu.bbara.purefin.data.local.room.dao.MovieDao
|
||||||
import hu.bbara.purefin.data.local.room.dao.SeasonDao
|
import hu.bbara.purefin.data.local.room.dao.SeasonDao
|
||||||
import hu.bbara.purefin.data.local.room.dao.SeriesDao
|
import hu.bbara.purefin.data.local.room.dao.SeriesDao
|
||||||
|
import hu.bbara.purefin.data.local.room.dao.LibraryDao
|
||||||
@Database(
|
@Database(
|
||||||
entities = [
|
entities = [
|
||||||
MovieEntity::class,
|
MovieEntity::class,
|
||||||
SeriesEntity::class,
|
SeriesEntity::class,
|
||||||
SeasonEntity::class,
|
SeasonEntity::class,
|
||||||
EpisodeEntity::class,
|
EpisodeEntity::class,
|
||||||
|
LibraryEntity::class,
|
||||||
CastMemberEntity::class
|
CastMemberEntity::class
|
||||||
],
|
],
|
||||||
version = 1,
|
version = 3,
|
||||||
exportSchema = false
|
exportSchema = false
|
||||||
)
|
)
|
||||||
@TypeConverters(UuidConverters::class)
|
@TypeConverters(UuidConverters::class)
|
||||||
@@ -26,5 +27,6 @@ abstract class MediaDatabase : RoomDatabase() {
|
|||||||
abstract fun seriesDao(): SeriesDao
|
abstract fun seriesDao(): SeriesDao
|
||||||
abstract fun seasonDao(): SeasonDao
|
abstract fun seasonDao(): SeasonDao
|
||||||
abstract fun episodeDao(): EpisodeDao
|
abstract fun episodeDao(): EpisodeDao
|
||||||
|
abstract fun libraryDao(): LibraryDao
|
||||||
abstract fun castMemberDao(): CastMemberDao
|
abstract fun castMemberDao(): CastMemberDao
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
|||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
import hu.bbara.purefin.data.local.room.dao.CastMemberDao
|
import hu.bbara.purefin.data.local.room.dao.CastMemberDao
|
||||||
import hu.bbara.purefin.data.local.room.dao.EpisodeDao
|
import hu.bbara.purefin.data.local.room.dao.EpisodeDao
|
||||||
|
import hu.bbara.purefin.data.local.room.dao.LibraryDao
|
||||||
import hu.bbara.purefin.data.local.room.dao.MovieDao
|
import hu.bbara.purefin.data.local.room.dao.MovieDao
|
||||||
import hu.bbara.purefin.data.local.room.dao.SeasonDao
|
import hu.bbara.purefin.data.local.room.dao.SeasonDao
|
||||||
import hu.bbara.purefin.data.local.room.dao.SeriesDao
|
import hu.bbara.purefin.data.local.room.dao.SeriesDao
|
||||||
@@ -47,6 +48,10 @@ object MediaDatabaseModule {
|
|||||||
@OnlineDatabase
|
@OnlineDatabase
|
||||||
fun provideOnlineCastMemberDao(@OnlineDatabase db: MediaDatabase) = db.castMemberDao()
|
fun provideOnlineCastMemberDao(@OnlineDatabase db: MediaDatabase) = db.castMemberDao()
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@OnlineDatabase
|
||||||
|
fun provideOnlineLibraryDao(@OnlineDatabase db: MediaDatabase) = db.libraryDao()
|
||||||
|
|
||||||
// Offline Database and DAOs
|
// Offline Database and DAOs
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
@@ -76,6 +81,10 @@ object MediaDatabaseModule {
|
|||||||
@OfflineDatabase
|
@OfflineDatabase
|
||||||
fun provideOfflineCastMemberDao(@OfflineDatabase db: OfflineMediaDatabase) = db.castMemberDao()
|
fun provideOfflineCastMemberDao(@OfflineDatabase db: OfflineMediaDatabase) = db.castMemberDao()
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@OfflineDatabase
|
||||||
|
fun provideOfflineLibraryDao(@OfflineDatabase db: OfflineMediaDatabase) = db.libraryDao()
|
||||||
|
|
||||||
// Data Sources
|
// Data Sources
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
@@ -86,9 +95,10 @@ object MediaDatabaseModule {
|
|||||||
@OnlineDatabase seriesDao: SeriesDao,
|
@OnlineDatabase seriesDao: SeriesDao,
|
||||||
@OnlineDatabase seasonDao: SeasonDao,
|
@OnlineDatabase seasonDao: SeasonDao,
|
||||||
@OnlineDatabase episodeDao: EpisodeDao,
|
@OnlineDatabase episodeDao: EpisodeDao,
|
||||||
@OnlineDatabase castMemberDao: CastMemberDao
|
@OnlineDatabase castMemberDao: CastMemberDao,
|
||||||
|
@OnlineDatabase libraryDao: LibraryDao
|
||||||
): RoomMediaLocalDataSource = RoomMediaLocalDataSource(
|
): RoomMediaLocalDataSource = RoomMediaLocalDataSource(
|
||||||
database, movieDao, seriesDao, seasonDao, episodeDao, castMemberDao
|
database, movieDao, seriesDao, seasonDao, episodeDao, castMemberDao, libraryDao
|
||||||
)
|
)
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@@ -100,9 +110,10 @@ object MediaDatabaseModule {
|
|||||||
@OfflineDatabase seriesDao: SeriesDao,
|
@OfflineDatabase seriesDao: SeriesDao,
|
||||||
@OfflineDatabase seasonDao: SeasonDao,
|
@OfflineDatabase seasonDao: SeasonDao,
|
||||||
@OfflineDatabase episodeDao: EpisodeDao,
|
@OfflineDatabase episodeDao: EpisodeDao,
|
||||||
@OfflineDatabase castMemberDao: CastMemberDao
|
@OfflineDatabase castMemberDao: CastMemberDao,
|
||||||
|
@OfflineDatabase libraryDao: LibraryDao
|
||||||
): OfflineRoomMediaLocalDataSource = OfflineRoomMediaLocalDataSource(
|
): OfflineRoomMediaLocalDataSource = OfflineRoomMediaLocalDataSource(
|
||||||
database, movieDao, seriesDao, seasonDao, episodeDao, castMemberDao
|
database, movieDao, seriesDao, seasonDao, episodeDao, castMemberDao, libraryDao
|
||||||
)
|
)
|
||||||
|
|
||||||
// Default (unqualified) data source for backward compatibility
|
// Default (unqualified) data source for backward compatibility
|
||||||
|
|||||||
@@ -1,10 +1,22 @@
|
|||||||
package hu.bbara.purefin.data.local.room
|
package hu.bbara.purefin.data.local.room
|
||||||
|
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
|
import androidx.room.ForeignKey
|
||||||
|
import androidx.room.Index
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@Entity(tableName = "movies")
|
@Entity(
|
||||||
|
tableName = "movies",
|
||||||
|
foreignKeys = [
|
||||||
|
ForeignKey(
|
||||||
|
entity = LibraryEntity::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["libraryId"],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
indices = [Index("libraryId")]
|
||||||
|
)
|
||||||
data class MovieEntity(
|
data class MovieEntity(
|
||||||
@PrimaryKey val id: UUID,
|
@PrimaryKey val id: UUID,
|
||||||
val libraryId: UUID,
|
val libraryId: UUID,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import hu.bbara.purefin.data.local.room.dao.EpisodeDao
|
|||||||
import hu.bbara.purefin.data.local.room.dao.MovieDao
|
import hu.bbara.purefin.data.local.room.dao.MovieDao
|
||||||
import hu.bbara.purefin.data.local.room.dao.SeasonDao
|
import hu.bbara.purefin.data.local.room.dao.SeasonDao
|
||||||
import hu.bbara.purefin.data.local.room.dao.SeriesDao
|
import hu.bbara.purefin.data.local.room.dao.SeriesDao
|
||||||
|
import hu.bbara.purefin.data.local.room.dao.LibraryDao
|
||||||
|
|
||||||
@Database(
|
@Database(
|
||||||
entities = [
|
entities = [
|
||||||
@@ -15,9 +16,10 @@ import hu.bbara.purefin.data.local.room.dao.SeriesDao
|
|||||||
SeriesEntity::class,
|
SeriesEntity::class,
|
||||||
SeasonEntity::class,
|
SeasonEntity::class,
|
||||||
EpisodeEntity::class,
|
EpisodeEntity::class,
|
||||||
|
LibraryEntity::class,
|
||||||
CastMemberEntity::class
|
CastMemberEntity::class
|
||||||
],
|
],
|
||||||
version = 1,
|
version = 3,
|
||||||
exportSchema = false
|
exportSchema = false
|
||||||
)
|
)
|
||||||
@TypeConverters(UuidConverters::class)
|
@TypeConverters(UuidConverters::class)
|
||||||
@@ -26,5 +28,6 @@ abstract class OfflineMediaDatabase : RoomDatabase() {
|
|||||||
abstract fun seriesDao(): SeriesDao
|
abstract fun seriesDao(): SeriesDao
|
||||||
abstract fun seasonDao(): SeasonDao
|
abstract fun seasonDao(): SeasonDao
|
||||||
abstract fun episodeDao(): EpisodeDao
|
abstract fun episodeDao(): EpisodeDao
|
||||||
|
abstract fun libraryDao(): LibraryDao
|
||||||
abstract fun castMemberDao(): CastMemberDao
|
abstract fun castMemberDao(): CastMemberDao
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,16 +3,19 @@ package hu.bbara.purefin.data.local.room
|
|||||||
import androidx.room.withTransaction
|
import androidx.room.withTransaction
|
||||||
import hu.bbara.purefin.data.local.room.dao.CastMemberDao
|
import hu.bbara.purefin.data.local.room.dao.CastMemberDao
|
||||||
import hu.bbara.purefin.data.local.room.dao.EpisodeDao
|
import hu.bbara.purefin.data.local.room.dao.EpisodeDao
|
||||||
|
import hu.bbara.purefin.data.local.room.dao.LibraryDao
|
||||||
import hu.bbara.purefin.data.local.room.dao.MovieDao
|
import hu.bbara.purefin.data.local.room.dao.MovieDao
|
||||||
import hu.bbara.purefin.data.local.room.dao.SeasonDao
|
import hu.bbara.purefin.data.local.room.dao.SeasonDao
|
||||||
import hu.bbara.purefin.data.local.room.dao.SeriesDao
|
import hu.bbara.purefin.data.local.room.dao.SeriesDao
|
||||||
import hu.bbara.purefin.data.model.CastMember
|
import hu.bbara.purefin.data.model.CastMember
|
||||||
import hu.bbara.purefin.data.model.Episode
|
import hu.bbara.purefin.data.model.Episode
|
||||||
|
import hu.bbara.purefin.data.model.Library
|
||||||
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.Flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
import org.jellyfin.sdk.model.api.CollectionType
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@@ -24,10 +27,21 @@ class OfflineRoomMediaLocalDataSource(
|
|||||||
private val seriesDao: SeriesDao,
|
private val seriesDao: SeriesDao,
|
||||||
private val seasonDao: SeasonDao,
|
private val seasonDao: SeasonDao,
|
||||||
private val episodeDao: EpisodeDao,
|
private val episodeDao: EpisodeDao,
|
||||||
private val castMemberDao: CastMemberDao
|
private val castMemberDao: CastMemberDao,
|
||||||
|
private val libraryDao: LibraryDao
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// Lightweight Flows for list screens (home, library)
|
// 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()
|
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(cast = emptyList()) } }
|
||||||
|
|
||||||
@@ -54,6 +68,13 @@ class OfflineRoomMediaLocalDataSource(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun saveLibraries(libraries: List<Library>) {
|
||||||
|
database.withTransaction {
|
||||||
|
libraryDao.deleteAll()
|
||||||
|
libraryDao.upsertAll(libraries.map { it.toEntity() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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() })
|
||||||
@@ -184,6 +205,28 @@ class OfflineRoomMediaLocalDataSource(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(
|
private fun Movie.toEntity() = MovieEntity(
|
||||||
id = id,
|
id = id,
|
||||||
libraryId = libraryId,
|
libraryId = libraryId,
|
||||||
|
|||||||
@@ -3,19 +3,22 @@ package hu.bbara.purefin.data.local.room
|
|||||||
import androidx.room.withTransaction
|
import androidx.room.withTransaction
|
||||||
import hu.bbara.purefin.data.local.room.dao.CastMemberDao
|
import hu.bbara.purefin.data.local.room.dao.CastMemberDao
|
||||||
import hu.bbara.purefin.data.local.room.dao.EpisodeDao
|
import hu.bbara.purefin.data.local.room.dao.EpisodeDao
|
||||||
|
import hu.bbara.purefin.data.local.room.dao.LibraryDao
|
||||||
import hu.bbara.purefin.data.local.room.dao.MovieDao
|
import hu.bbara.purefin.data.local.room.dao.MovieDao
|
||||||
import hu.bbara.purefin.data.local.room.dao.SeasonDao
|
import hu.bbara.purefin.data.local.room.dao.SeasonDao
|
||||||
import hu.bbara.purefin.data.local.room.dao.SeriesDao
|
import hu.bbara.purefin.data.local.room.dao.SeriesDao
|
||||||
import hu.bbara.purefin.data.model.CastMember
|
import hu.bbara.purefin.data.model.CastMember
|
||||||
import hu.bbara.purefin.data.model.Episode
|
import hu.bbara.purefin.data.model.Episode
|
||||||
|
import hu.bbara.purefin.data.model.Library
|
||||||
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.Flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
import org.jellyfin.sdk.model.api.CollectionType
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
import kotlin.collections.map
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class RoomMediaLocalDataSource(
|
class RoomMediaLocalDataSource(
|
||||||
@@ -24,10 +27,21 @@ class RoomMediaLocalDataSource(
|
|||||||
private val seriesDao: SeriesDao,
|
private val seriesDao: SeriesDao,
|
||||||
private val seasonDao: SeasonDao,
|
private val seasonDao: SeasonDao,
|
||||||
private val episodeDao: EpisodeDao,
|
private val episodeDao: EpisodeDao,
|
||||||
private val castMemberDao: CastMemberDao
|
private val castMemberDao: CastMemberDao,
|
||||||
|
private val libraryDao: LibraryDao
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// Lightweight Flows for list screens (home, library)
|
// Lightweight Flows for list screens (home, library)
|
||||||
|
val librariesFlow: Flow<List<Library>> = libraryDao.observeAllWithContent()
|
||||||
|
.map { relation ->
|
||||||
|
relation.map { libraryEntity ->
|
||||||
|
libraryEntity.library.toDomain(
|
||||||
|
movies = libraryEntity.movies.map { it.toDomain(listOf()) },
|
||||||
|
series = libraryEntity.series.map { it.toDomain(listOf(), listOf()) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val moviesFlow: Flow<Map<UUID, Movie>> = movieDao.observeAll()
|
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(cast = emptyList()) } }
|
||||||
|
|
||||||
@@ -54,6 +68,13 @@ class RoomMediaLocalDataSource(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun saveLibraries(libraries: List<Library>) {
|
||||||
|
database.withTransaction {
|
||||||
|
libraryDao.deleteAll()
|
||||||
|
libraryDao.upsertAll(libraries.map { it.toEntity() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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() })
|
||||||
@@ -184,6 +205,28 @@ class RoomMediaLocalDataSource(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(
|
private fun Movie.toEntity() = MovieEntity(
|
||||||
id = id,
|
id = id,
|
||||||
libraryId = libraryId,
|
libraryId = libraryId,
|
||||||
|
|||||||
@@ -21,3 +21,17 @@ data class SeriesWithSeasonsAndEpisodes(
|
|||||||
)
|
)
|
||||||
val seasons: List<SeasonWithEpisodes>
|
val seasons: List<SeasonWithEpisodes>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class LibraryWithContent(
|
||||||
|
@Embedded val library: LibraryEntity,
|
||||||
|
@Relation(
|
||||||
|
parentColumn = "id",
|
||||||
|
entityColumn = "libraryId"
|
||||||
|
)
|
||||||
|
val series: List<SeriesEntity>,
|
||||||
|
@Relation(
|
||||||
|
parentColumn = "id",
|
||||||
|
entityColumn = "libraryId"
|
||||||
|
)
|
||||||
|
val movies: List<MovieEntity>
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,10 +1,22 @@
|
|||||||
package hu.bbara.purefin.data.local.room
|
package hu.bbara.purefin.data.local.room
|
||||||
|
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
|
import androidx.room.ForeignKey
|
||||||
|
import androidx.room.Index
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@Entity(tableName = "series")
|
@Entity(
|
||||||
|
tableName = "series",
|
||||||
|
foreignKeys = [
|
||||||
|
ForeignKey(
|
||||||
|
entity = LibraryEntity::class,
|
||||||
|
parentColumns = ["id"],
|
||||||
|
childColumns = ["libraryId"],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
indices = [Index("libraryId")]
|
||||||
|
)
|
||||||
data class SeriesEntity(
|
data class SeriesEntity(
|
||||||
@PrimaryKey val id: UUID,
|
@PrimaryKey val id: UUID,
|
||||||
val libraryId: UUID,
|
val libraryId: UUID,
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package hu.bbara.purefin.data.local.room.dao
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Query
|
||||||
|
import androidx.room.Upsert
|
||||||
|
import hu.bbara.purefin.data.local.room.LibraryEntity
|
||||||
|
import hu.bbara.purefin.data.local.room.LibraryWithContent
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface LibraryDao {
|
||||||
|
@Upsert
|
||||||
|
suspend fun upsert(library: LibraryEntity)
|
||||||
|
|
||||||
|
@Upsert
|
||||||
|
suspend fun upsertAll(libraries: List<LibraryEntity>)
|
||||||
|
|
||||||
|
@Query("SELECT * FROM library")
|
||||||
|
fun observeAll(): Flow<List<LibraryEntity>>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM library")
|
||||||
|
fun observeAllWithContent(): Flow<List<LibraryWithContent>>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM library")
|
||||||
|
suspend fun getAll(): List<LibraryEntity>
|
||||||
|
|
||||||
|
@Query("DELETE FROM library")
|
||||||
|
suspend fun deleteAll()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user