mirror of
https://github.com/bbara04/Purefin.git
synced 2026-03-31 17:10:08 +02:00
refactor: add CompositeMediaRepository to switch between offline/online based on user session
Extract AppContentRepository interface from MediaRepository for home/library-specific features. CompositeMediaRepository delegates to OfflineMediaRepository or AppContentRepository based on UserSessionRepository.isOfflineMode, allowing content ViewModels to use a single MediaRepository interface that automatically switches data source.
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
package hu.bbara.purefin.core.data
|
||||
|
||||
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.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.util.UUID
|
||||
|
||||
interface AppContentRepository : MediaRepository {
|
||||
|
||||
val libraries: StateFlow<List<Library>>
|
||||
val state: StateFlow<MediaRepositoryState>
|
||||
val continueWatching: StateFlow<List<Media>>
|
||||
val nextUp: StateFlow<List<Media>>
|
||||
val latestLibraryContent: StateFlow<Map<UUID, List<Media>>>
|
||||
suspend fun ensureReady()
|
||||
suspend fun refreshHomeData()
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package hu.bbara.purefin.core.data
|
||||
|
||||
import hu.bbara.purefin.core.data.session.UserSessionRepository
|
||||
import hu.bbara.purefin.core.model.Episode
|
||||
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.stateIn
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Switches between [OfflineMediaRepository] and [AppContentRepository] based on
|
||||
* [UserSessionRepository.isOfflineMode]. When offline mode is enabled, all reads
|
||||
* and writes go through the offline (downloaded) repository; otherwise the online
|
||||
* repository is used.
|
||||
*/
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Singleton
|
||||
class CompositeMediaRepository @Inject constructor(
|
||||
private val offlineRepository: OfflineMediaRepository,
|
||||
private val onlineRepository: AppContentRepository,
|
||||
private val userSessionRepository: UserSessionRepository,
|
||||
) : MediaRepository {
|
||||
|
||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
|
||||
private val activeRepository: Flow<MediaRepository> =
|
||||
userSessionRepository.isOfflineMode.flatMapLatest { offline ->
|
||||
kotlinx.coroutines.flow.flowOf(if (offline) offlineRepository else onlineRepository)
|
||||
}
|
||||
|
||||
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 fun observeSeriesWithContent(seriesId: UUID): Flow<Series?> {
|
||||
return activeRepository.flatMapLatest { it.observeSeriesWithContent(seriesId) }
|
||||
}
|
||||
|
||||
override suspend fun updateWatchProgress(mediaId: UUID, positionMs: Long, durationMs: Long) {
|
||||
val isOffline = userSessionRepository.isOfflineMode.stateIn(scope).value
|
||||
val repo = if (isOffline) offlineRepository else onlineRepository
|
||||
repo.updateWatchProgress(mediaId, positionMs, durationMs)
|
||||
}
|
||||
}
|
||||
@@ -39,11 +39,11 @@ import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class InMemoryMediaRepository @Inject constructor(
|
||||
class InMemoryAppContentRepository @Inject constructor(
|
||||
val userSessionRepository: UserSessionRepository,
|
||||
val jellyfinApiClient: JellyfinApiClient,
|
||||
private val homeCacheDataStore: DataStore<HomeCache>
|
||||
) : MediaRepository {
|
||||
) : AppContentRepository {
|
||||
|
||||
private val ready = CompletableDeferred<Unit>()
|
||||
private val readyMutex = Mutex()
|
||||
@@ -1,9 +1,6 @@
|
||||
package hu.bbara.purefin.core.data
|
||||
|
||||
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.flow.Flow
|
||||
@@ -11,21 +8,9 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
import java.util.UUID
|
||||
|
||||
interface MediaRepository {
|
||||
|
||||
val libraries: StateFlow<List<Library>>
|
||||
val movies: StateFlow<Map<UUID, Movie>>
|
||||
val series: StateFlow<Map<UUID, Series>>
|
||||
val episodes: StateFlow<Map<UUID, Episode>>
|
||||
val state: StateFlow<MediaRepositoryState>
|
||||
|
||||
val continueWatching: StateFlow<List<Media>>
|
||||
val nextUp: StateFlow<List<Media>>
|
||||
val latestLibraryContent: StateFlow<Map<UUID, List<Media>>>
|
||||
|
||||
fun observeSeriesWithContent(seriesId: UUID): Flow<Series?>
|
||||
|
||||
suspend fun ensureReady()
|
||||
|
||||
suspend fun updateWatchProgress(mediaId: UUID, positionMs: Long, durationMs: Long)
|
||||
suspend fun refreshHomeData()
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,9 @@ import dagger.hilt.components.SingletonComponent
|
||||
abstract class MediaRepositoryModule {
|
||||
|
||||
@Binds
|
||||
abstract fun bindInMemoryMediaRepository(impl: InMemoryMediaRepository): MediaRepository
|
||||
abstract fun bindAppContentRepository(impl: InMemoryAppContentRepository): AppContentRepository
|
||||
|
||||
@Binds
|
||||
abstract fun bindMediaRepository(impl: CompositeMediaRepository): MediaRepository
|
||||
|
||||
}
|
||||
|
||||
@@ -22,23 +22,23 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class OfflineMediaRepository @Inject constructor(
|
||||
private val localDataSource: OfflineRoomMediaLocalDataSource
|
||||
) {
|
||||
) : MediaRepository {
|
||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
|
||||
val movies: StateFlow<Map<UUID, Movie>> = localDataSource.moviesFlow
|
||||
override val movies: StateFlow<Map<UUID, Movie>> = localDataSource.moviesFlow
|
||||
.stateIn(scope, SharingStarted.Eagerly, emptyMap())
|
||||
|
||||
val series: StateFlow<Map<UUID, Series>> = localDataSource.seriesFlow
|
||||
override val series: StateFlow<Map<UUID, Series>> = localDataSource.seriesFlow
|
||||
.stateIn(scope, SharingStarted.Eagerly, emptyMap())
|
||||
|
||||
val episodes: StateFlow<Map<UUID, Episode>> = localDataSource.episodesFlow
|
||||
override val episodes: StateFlow<Map<UUID, Episode>> = localDataSource.episodesFlow
|
||||
.stateIn(scope, SharingStarted.Eagerly, emptyMap())
|
||||
|
||||
fun observeSeriesWithContent(seriesId: UUID): Flow<Series?> {
|
||||
override fun observeSeriesWithContent(seriesId: UUID): Flow<Series?> {
|
||||
return localDataSource.observeSeriesWithContent(seriesId)
|
||||
}
|
||||
|
||||
suspend fun updateWatchProgress(mediaId: UUID, positionMs: Long, durationMs: Long) {
|
||||
override 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
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package hu.bbara.purefin.core.data.domain.usecase
|
||||
|
||||
import hu.bbara.purefin.core.data.MediaRepository
|
||||
import hu.bbara.purefin.core.data.AppContentRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
class RefreshHomeDataUseCase @Inject constructor(
|
||||
private val repository: MediaRepository
|
||||
private val repository: AppContentRepository
|
||||
) {
|
||||
suspend operator fun invoke() {
|
||||
repository.refreshHomeData()
|
||||
|
||||
Reference in New Issue
Block a user