mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-08-17 21:05:43 +00:00
feat: Update storage methods to support profile-specific data; modify load and save functions across addons, library, and watch progress
This commit is contained in:
parent
c1337e8b70
commit
35d6f65993
13 changed files with 89 additions and 43 deletions
|
|
@ -18,19 +18,19 @@ actual object AddonStorage {
|
|||
preferences = context.getSharedPreferences(preferencesName, Context.MODE_PRIVATE)
|
||||
}
|
||||
|
||||
actual fun loadInstalledAddonUrls(): List<String> =
|
||||
actual fun loadInstalledAddonUrls(profileId: Int): List<String> =
|
||||
preferences
|
||||
?.getString(addonUrlsKey, null)
|
||||
?.getString("${addonUrlsKey}_$profileId", null)
|
||||
.orEmpty()
|
||||
.lineSequence()
|
||||
.map { it.trim() }
|
||||
.filter { it.isNotEmpty() }
|
||||
.toList()
|
||||
|
||||
actual fun saveInstalledAddonUrls(urls: List<String>) {
|
||||
actual fun saveInstalledAddonUrls(profileId: Int, urls: List<String>) {
|
||||
preferences
|
||||
?.edit()
|
||||
?.putString(addonUrlsKey, urls.joinToString(separator = "\n"))
|
||||
?.putString("${addonUrlsKey}_$profileId", urls.joinToString(separator = "\n"))
|
||||
?.apply()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import android.content.SharedPreferences
|
|||
|
||||
actual object LibraryStorage {
|
||||
private const val preferencesName = "nuvio_library"
|
||||
private const val payloadKey = "library_payload"
|
||||
private fun payloadKey(profileId: Int) = "library_payload_$profileId"
|
||||
|
||||
private var preferences: SharedPreferences? = null
|
||||
|
||||
|
|
@ -13,13 +13,13 @@ actual object LibraryStorage {
|
|||
preferences = context.getSharedPreferences(preferencesName, Context.MODE_PRIVATE)
|
||||
}
|
||||
|
||||
actual fun loadPayload(): String? =
|
||||
preferences?.getString(payloadKey, null)
|
||||
actual fun loadPayload(profileId: Int): String? =
|
||||
preferences?.getString(payloadKey(profileId), null)
|
||||
|
||||
actual fun savePayload(payload: String) {
|
||||
actual fun savePayload(profileId: Int, payload: String) {
|
||||
preferences
|
||||
?.edit()
|
||||
?.putString(payloadKey, payload)
|
||||
?.putString(payloadKey(profileId), payload)
|
||||
?.apply()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,13 +13,13 @@ actual object WatchProgressStorage {
|
|||
preferences = context.getSharedPreferences(preferencesName, Context.MODE_PRIVATE)
|
||||
}
|
||||
|
||||
actual fun loadPayload(): String? =
|
||||
preferences?.getString(payloadKey, null)
|
||||
actual fun loadPayload(profileId: Int): String? =
|
||||
preferences?.getString("${payloadKey}_$profileId", null)
|
||||
|
||||
actual fun savePayload(payload: String) {
|
||||
actual fun savePayload(profileId: Int, payload: String) {
|
||||
preferences
|
||||
?.edit()
|
||||
?.putString(payloadKey, payload)
|
||||
?.putString("${payloadKey}_$profileId", payload)
|
||||
?.apply()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package com.nuvio.app.features.addons
|
||||
|
||||
internal expect object AddonStorage {
|
||||
fun loadInstalledAddonUrls(): List<String>
|
||||
fun saveInstalledAddonUrls(urls: List<String>)
|
||||
fun loadInstalledAddonUrls(profileId: Int): List<String>
|
||||
fun saveInstalledAddonUrls(profileId: Int, urls: List<String>)
|
||||
}
|
||||
|
||||
expect suspend fun httpGetText(url: String): String
|
||||
|
|
|
|||
|
|
@ -48,13 +48,15 @@ object AddonRepository {
|
|||
|
||||
private var initialized = false
|
||||
private var pulledFromServer = false
|
||||
private var currentProfileId: Int = 1
|
||||
|
||||
fun initialize() {
|
||||
if (initialized) return
|
||||
initialized = true
|
||||
log.d { "initialize() — loading local addons" }
|
||||
currentProfileId = ProfileRepository.activeProfileId
|
||||
log.d { "initialize() — loading local addons for profile $currentProfileId" }
|
||||
|
||||
val storedUrls = AddonStorage.loadInstalledAddonUrls()
|
||||
val storedUrls = AddonStorage.loadInstalledAddonUrls(currentProfileId)
|
||||
log.d { "initialize() — local addon count: ${storedUrls.size}" }
|
||||
if (storedUrls.isEmpty()) return
|
||||
|
||||
|
|
@ -70,7 +72,16 @@ object AddonRepository {
|
|||
storedUrls.forEach(::refreshAddon)
|
||||
}
|
||||
|
||||
fun onProfileChanged(profileId: Int) {
|
||||
if (profileId == currentProfileId && initialized) return
|
||||
currentProfileId = profileId
|
||||
initialized = false
|
||||
pulledFromServer = false
|
||||
_uiState.value = AddonsUiState()
|
||||
}
|
||||
|
||||
suspend fun pullFromServer(profileId: Int) {
|
||||
currentProfileId = profileId
|
||||
log.i { "pullFromServer() — profileId=$profileId, initialized=$initialized, pulledFromServer=$pulledFromServer" }
|
||||
runCatching {
|
||||
val rows = SupabaseProvider.client.postgrest
|
||||
|
|
@ -86,7 +97,7 @@ object AddonRepository {
|
|||
urls.forEachIndexed { i, u -> log.d { " server[$i]: $u" } }
|
||||
|
||||
if (urls.isEmpty() && !pulledFromServer) {
|
||||
val localUrls = AddonStorage.loadInstalledAddonUrls()
|
||||
val localUrls = AddonStorage.loadInstalledAddonUrls(profileId)
|
||||
log.i { "pullFromServer() — server empty, local has ${localUrls.size} addons" }
|
||||
if (localUrls.isNotEmpty()) {
|
||||
log.i { "pullFromServer() — migrating local addons to server for profile $profileId" }
|
||||
|
|
@ -272,6 +283,7 @@ object AddonRepository {
|
|||
|
||||
private fun persist() {
|
||||
AddonStorage.saveInstalledAddonUrls(
|
||||
currentProfileId,
|
||||
_uiState.value.addons.map { it.manifestUrl },
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,13 +53,25 @@ object LibraryRepository {
|
|||
val uiState: StateFlow<LibraryUiState> = _uiState.asStateFlow()
|
||||
|
||||
private var hasLoaded = false
|
||||
private var currentProfileId: Int = 1
|
||||
private var itemsById: MutableMap<String, LibraryItem> = mutableMapOf()
|
||||
|
||||
fun ensureLoaded() {
|
||||
if (hasLoaded) return
|
||||
hasLoaded = true
|
||||
loadFromDisk(ProfileRepository.activeProfileId)
|
||||
}
|
||||
|
||||
val payload = LibraryStorage.loadPayload().orEmpty().trim()
|
||||
fun onProfileChanged(profileId: Int) {
|
||||
if (profileId == currentProfileId && hasLoaded) return
|
||||
loadFromDisk(profileId)
|
||||
}
|
||||
|
||||
private fun loadFromDisk(profileId: Int) {
|
||||
currentProfileId = profileId
|
||||
hasLoaded = true
|
||||
itemsById.clear()
|
||||
|
||||
val payload = LibraryStorage.loadPayload(profileId).orEmpty().trim()
|
||||
if (payload.isNotEmpty()) {
|
||||
val items = runCatching {
|
||||
json.decodeFromString<StoredLibraryPayload>(payload).items
|
||||
|
|
@ -71,6 +83,7 @@ object LibraryRepository {
|
|||
}
|
||||
|
||||
suspend fun pullFromServer(profileId: Int) {
|
||||
currentProfileId = profileId
|
||||
runCatching {
|
||||
val params = buildJsonObject {
|
||||
put("p_profile_id", profileId)
|
||||
|
|
@ -163,6 +176,7 @@ object LibraryRepository {
|
|||
|
||||
private fun persist() {
|
||||
LibraryStorage.savePayload(
|
||||
currentProfileId,
|
||||
json.encodeToString(
|
||||
StoredLibraryPayload(
|
||||
items = itemsById.values.sortedByDescending { it.savedAtEpochMs },
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.nuvio.app.features.library
|
||||
|
||||
internal expect object LibraryStorage {
|
||||
fun loadPayload(): String?
|
||||
fun savePayload(payload: String)
|
||||
fun loadPayload(profileId: Int): String?
|
||||
fun savePayload(profileId: Int, payload: String)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,10 @@ import co.touchlab.kermit.Logger
|
|||
import com.nuvio.app.core.auth.AuthRepository
|
||||
import com.nuvio.app.core.auth.AuthState
|
||||
import com.nuvio.app.core.network.SupabaseProvider
|
||||
import com.nuvio.app.features.addons.AddonRepository
|
||||
import com.nuvio.app.features.library.LibraryRepository
|
||||
import com.nuvio.app.features.watched.WatchedRepository
|
||||
import com.nuvio.app.features.watchprogress.WatchProgressRepository
|
||||
import io.github.jan.supabase.postgrest.postgrest
|
||||
import io.github.jan.supabase.postgrest.rpc
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
|
@ -108,6 +111,9 @@ object ProfileRepository {
|
|||
)
|
||||
persist()
|
||||
WatchedRepository.onProfileChanged(profileIndex)
|
||||
LibraryRepository.onProfileChanged(profileIndex)
|
||||
WatchProgressRepository.onProfileChanged(profileIndex)
|
||||
AddonRepository.onProfileChanged(profileIndex)
|
||||
}
|
||||
|
||||
suspend fun pushProfiles(profiles: List<ProfilePushPayload>) {
|
||||
|
|
|
|||
|
|
@ -45,22 +45,35 @@ object WatchProgressRepository {
|
|||
val uiState: StateFlow<WatchProgressUiState> = _uiState.asStateFlow()
|
||||
|
||||
private var hasLoaded = false
|
||||
private var currentProfileId: Int = 1
|
||||
private var entriesByVideoId: MutableMap<String, WatchProgressEntry> = mutableMapOf()
|
||||
|
||||
fun ensureLoaded() {
|
||||
if (hasLoaded) return
|
||||
loadFromDisk(ProfileRepository.activeProfileId)
|
||||
}
|
||||
|
||||
fun onProfileChanged(profileId: Int) {
|
||||
if (profileId == currentProfileId && hasLoaded) return
|
||||
loadFromDisk(profileId)
|
||||
}
|
||||
|
||||
private fun loadFromDisk(profileId: Int) {
|
||||
currentProfileId = profileId
|
||||
hasLoaded = true
|
||||
entriesByVideoId.clear()
|
||||
|
||||
val payload = WatchProgressStorage.loadPayload().orEmpty().trim()
|
||||
if (payload.isEmpty()) return
|
||||
|
||||
entriesByVideoId = WatchProgressCodec.decodeEntries(payload)
|
||||
.associateBy { it.videoId }
|
||||
.toMutableMap()
|
||||
val payload = WatchProgressStorage.loadPayload(profileId).orEmpty().trim()
|
||||
if (payload.isNotEmpty()) {
|
||||
entriesByVideoId = WatchProgressCodec.decodeEntries(payload)
|
||||
.associateBy { it.videoId }
|
||||
.toMutableMap()
|
||||
}
|
||||
publish()
|
||||
}
|
||||
|
||||
suspend fun pullFromServer(profileId: Int) {
|
||||
currentProfileId = profileId
|
||||
runCatching {
|
||||
val params = buildJsonObject { put("p_profile_id", profileId) }
|
||||
val result = SupabaseProvider.client.postgrest.rpc("sync_pull_watch_progress", params)
|
||||
|
|
@ -292,6 +305,7 @@ object WatchProgressRepository {
|
|||
|
||||
private fun persist() {
|
||||
WatchProgressStorage.savePayload(
|
||||
currentProfileId,
|
||||
WatchProgressCodec.encodeEntries(entriesByVideoId.values),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.nuvio.app.features.watchprogress
|
||||
|
||||
internal expect object WatchProgressStorage {
|
||||
fun loadPayload(): String?
|
||||
fun savePayload(payload: String)
|
||||
fun loadPayload(profileId: Int): String?
|
||||
fun savePayload(profileId: Int, payload: String)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,19 +14,19 @@ import platform.posix.memcpy
|
|||
actual object AddonStorage {
|
||||
private const val addonUrlsKey = "installed_manifest_urls"
|
||||
|
||||
actual fun loadInstalledAddonUrls(): List<String> =
|
||||
actual fun loadInstalledAddonUrls(profileId: Int): List<String> =
|
||||
NSUserDefaults.standardUserDefaults
|
||||
.stringForKey(addonUrlsKey)
|
||||
.stringForKey("${addonUrlsKey}_$profileId")
|
||||
.orEmpty()
|
||||
.lineSequence()
|
||||
.map { it.trim() }
|
||||
.filter { it.isNotEmpty() }
|
||||
.toList()
|
||||
|
||||
actual fun saveInstalledAddonUrls(urls: List<String>) {
|
||||
actual fun saveInstalledAddonUrls(profileId: Int, urls: List<String>) {
|
||||
NSUserDefaults.standardUserDefaults.setObject(
|
||||
urls.joinToString(separator = "\n"),
|
||||
forKey = addonUrlsKey,
|
||||
forKey = "${addonUrlsKey}_$profileId",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@ package com.nuvio.app.features.library
|
|||
import platform.Foundation.NSUserDefaults
|
||||
|
||||
actual object LibraryStorage {
|
||||
private const val payloadKey = "library_payload"
|
||||
private fun payloadKey(profileId: Int) = "library_payload_$profileId"
|
||||
|
||||
actual fun loadPayload(): String? =
|
||||
NSUserDefaults.standardUserDefaults.stringForKey(payloadKey)
|
||||
actual fun loadPayload(profileId: Int): String? =
|
||||
NSUserDefaults.standardUserDefaults.stringForKey(payloadKey(profileId))
|
||||
|
||||
actual fun savePayload(payload: String) {
|
||||
NSUserDefaults.standardUserDefaults.setObject(payload, forKey = payloadKey)
|
||||
actual fun savePayload(profileId: Int, payload: String) {
|
||||
NSUserDefaults.standardUserDefaults.setObject(payload, forKey = payloadKey(profileId))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ import platform.Foundation.NSUserDefaults
|
|||
actual object WatchProgressStorage {
|
||||
private const val payloadKey = "watch_progress_payload"
|
||||
|
||||
actual fun loadPayload(): String? =
|
||||
NSUserDefaults.standardUserDefaults.stringForKey(payloadKey)
|
||||
actual fun loadPayload(profileId: Int): String? =
|
||||
NSUserDefaults.standardUserDefaults.stringForKey("${payloadKey}_$profileId")
|
||||
|
||||
actual fun savePayload(payload: String) {
|
||||
NSUserDefaults.standardUserDefaults.setObject(payload, forKey = payloadKey)
|
||||
actual fun savePayload(profileId: Int, payload: String) {
|
||||
NSUserDefaults.standardUserDefaults.setObject(payload, forKey = "${payloadKey}_$profileId")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue