mirror of
https://github.com/bbara04/Purefin.git
synced 2026-07-24 03:36:51 +00:00
refactor: simplify AppContentRepository initialization logic.
This commit is contained in:
@@ -2,8 +2,8 @@ package hu.bbara.purefin.core.data
|
|||||||
|
|
||||||
import hu.bbara.purefin.core.model.Library
|
import hu.bbara.purefin.core.model.Library
|
||||||
import hu.bbara.purefin.core.model.Media
|
import hu.bbara.purefin.core.model.Media
|
||||||
import java.util.UUID
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
interface HomeRepository {
|
interface HomeRepository {
|
||||||
val libraries: StateFlow<List<Library>>
|
val libraries: StateFlow<List<Library>>
|
||||||
@@ -11,6 +11,6 @@ interface HomeRepository {
|
|||||||
val continueWatching: StateFlow<List<Media>>
|
val continueWatching: StateFlow<List<Media>>
|
||||||
val nextUp: StateFlow<List<Media>>
|
val nextUp: StateFlow<List<Media>>
|
||||||
val latestLibraryContent: StateFlow<Map<UUID, List<Media>>>
|
val latestLibraryContent: StateFlow<Map<UUID, List<Media>>>
|
||||||
suspend fun ensureReady()
|
fun ensureReady()
|
||||||
suspend fun refreshHomeData()
|
fun refreshHomeData()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,9 @@ import android.util.Log
|
|||||||
import androidx.datastore.core.DataStore
|
import androidx.datastore.core.DataStore
|
||||||
import hu.bbara.purefin.core.data.HomeRepository
|
import hu.bbara.purefin.core.data.HomeRepository
|
||||||
import hu.bbara.purefin.core.data.NetworkMonitor
|
import hu.bbara.purefin.core.data.NetworkMonitor
|
||||||
import hu.bbara.purefin.core.image.ArtworkKind
|
|
||||||
import hu.bbara.purefin.data.offline.cache.CachedMediaItem
|
|
||||||
import hu.bbara.purefin.data.offline.cache.HomeCache
|
|
||||||
import hu.bbara.purefin.data.jellyfin.client.JellyfinApiClient
|
|
||||||
import hu.bbara.purefin.core.image.ImageUrlBuilder
|
|
||||||
import hu.bbara.purefin.core.data.session.UserSessionRepository
|
import hu.bbara.purefin.core.data.session.UserSessionRepository
|
||||||
|
import hu.bbara.purefin.core.image.ArtworkKind
|
||||||
|
import hu.bbara.purefin.core.image.ImageUrlBuilder
|
||||||
import hu.bbara.purefin.core.model.Episode
|
import hu.bbara.purefin.core.model.Episode
|
||||||
import hu.bbara.purefin.core.model.Library
|
import hu.bbara.purefin.core.model.Library
|
||||||
import hu.bbara.purefin.core.model.LibraryKind
|
import hu.bbara.purefin.core.model.LibraryKind
|
||||||
@@ -17,13 +14,9 @@ import hu.bbara.purefin.core.model.Media
|
|||||||
import hu.bbara.purefin.core.model.Movie
|
import hu.bbara.purefin.core.model.Movie
|
||||||
import hu.bbara.purefin.core.model.Season
|
import hu.bbara.purefin.core.model.Season
|
||||||
import hu.bbara.purefin.core.model.Series
|
import hu.bbara.purefin.core.model.Series
|
||||||
import java.time.LocalDateTime
|
import hu.bbara.purefin.data.jellyfin.client.JellyfinApiClient
|
||||||
import java.time.format.DateTimeFormatter
|
import hu.bbara.purefin.data.offline.cache.CachedMediaItem
|
||||||
import java.util.Locale
|
import hu.bbara.purefin.data.offline.cache.HomeCache
|
||||||
import java.util.UUID
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
@@ -33,11 +26,18 @@ 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.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.sync.Mutex
|
|
||||||
import kotlinx.coroutines.sync.withLock
|
|
||||||
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
|
||||||
import org.jellyfin.sdk.model.api.CollectionType
|
import org.jellyfin.sdk.model.api.CollectionType
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
import java.util.Locale
|
||||||
|
import java.util.UUID
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
import kotlin.concurrent.atomics.AtomicBoolean
|
||||||
|
import kotlin.concurrent.atomics.ExperimentalAtomicApi
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class InMemoryAppContentRepository @Inject constructor(
|
class InMemoryAppContentRepository @Inject constructor(
|
||||||
@@ -48,10 +48,10 @@ class InMemoryAppContentRepository @Inject constructor(
|
|||||||
private val networkMonitor: NetworkMonitor,
|
private val networkMonitor: NetworkMonitor,
|
||||||
) : HomeRepository {
|
) : HomeRepository {
|
||||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||||
private val ensureReadyMutex = Mutex()
|
|
||||||
private var refreshJob: Job? = null
|
private var refreshJob: Job? = null
|
||||||
private var cacheHydrated = false
|
|
||||||
private var initialized = false
|
@OptIn(ExperimentalAtomicApi::class)
|
||||||
|
private val initialized = AtomicBoolean(false)
|
||||||
|
|
||||||
private val librariesState = MutableStateFlow<List<Library>>(emptyList())
|
private val librariesState = MutableStateFlow<List<Library>>(emptyList())
|
||||||
override val libraries: StateFlow<List<Library>> = librariesState.asStateFlow()
|
override val libraries: StateFlow<List<Library>> = librariesState.asStateFlow()
|
||||||
@@ -74,27 +74,24 @@ class InMemoryAppContentRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun ensureReady() {
|
@OptIn(ExperimentalAtomicApi::class)
|
||||||
ensureReadyMutex.withLock {
|
override fun ensureReady() {
|
||||||
hydrateCacheIfNeeded()
|
if (!initialized.compareAndSet(expectedValue = false, newValue = true)) {
|
||||||
if (initialized) {
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
initialized = true
|
|
||||||
}
|
}
|
||||||
refreshHomeData()
|
Log.d(TAG, "Initializing home repository")
|
||||||
|
scope.launch { loadHomeCache() }
|
||||||
|
scope.launch { refreshHomeData() }
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun refreshHomeData() {
|
@Synchronized
|
||||||
ensureReadyMutex.withLock {
|
override fun refreshHomeData() {
|
||||||
hydrateCacheIfNeeded()
|
|
||||||
}
|
|
||||||
if (refreshJob?.isActive == true) {
|
if (refreshJob?.isActive == true) {
|
||||||
refreshJob?.join()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val job = scope.launch {
|
val job = scope.launch {
|
||||||
runCatching {
|
runCatching {
|
||||||
|
Log.d(TAG, "Refreshing home data")
|
||||||
if (!networkMonitor.isOnline.first()) {
|
if (!networkMonitor.isOnline.first()) {
|
||||||
return@runCatching
|
return@runCatching
|
||||||
}
|
}
|
||||||
@@ -103,22 +100,17 @@ class InMemoryAppContentRepository @Inject constructor(
|
|||||||
loadContinueWatching()
|
loadContinueWatching()
|
||||||
loadNextUp()
|
loadNextUp()
|
||||||
loadLatestLibraryContent()
|
loadLatestLibraryContent()
|
||||||
|
Log.d(TAG, "Home refresh successful")
|
||||||
persistHomeCache()
|
persistHomeCache()
|
||||||
}.onFailure { error ->
|
}.onFailure { error ->
|
||||||
Log.w(TAG, "Home refresh failed; keeping cached content", error)
|
Log.w(TAG, "Home refresh failed; keeping cached content", error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
refreshJob = job
|
refreshJob = job
|
||||||
job.join()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun hydrateCacheIfNeeded() {
|
private suspend fun loadHomeCache() {
|
||||||
if (cacheHydrated) return
|
Log.d(TAG, "Loading home cache")
|
||||||
loadFromCache()
|
|
||||||
cacheHydrated = true
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun loadFromCache() {
|
|
||||||
val cache = homeCacheDataStore.data.first()
|
val cache = homeCacheDataStore.data.first()
|
||||||
if (cache.suggestions.isNotEmpty()) {
|
if (cache.suggestions.isNotEmpty()) {
|
||||||
suggestionsState.value = cache.suggestions.mapNotNull { it.toMedia() }
|
suggestionsState.value = cache.suggestions.mapNotNull { it.toMedia() }
|
||||||
@@ -135,6 +127,7 @@ class InMemoryAppContentRepository @Inject constructor(
|
|||||||
uuid to items.mapNotNull { it.toMedia() }
|
uuid to items.mapNotNull { it.toMedia() }
|
||||||
}.toMap()
|
}.toMap()
|
||||||
}
|
}
|
||||||
|
Log.d(TAG, "Home cache loaded")
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun persistHomeCache() {
|
private suspend fun persistHomeCache() {
|
||||||
|
|||||||
Reference in New Issue
Block a user