Refresh watch progress when clearing CW cache

This commit is contained in:
tapframe 2026-06-12 20:07:28 +05:30
parent 142afd14a7
commit 32a26951f7
5 changed files with 55 additions and 2 deletions

View file

@ -449,7 +449,7 @@
<string name="settings_advanced_remember_last_profile">Remember Last Profile</string>
<string name="settings_advanced_remember_last_profile_description">Remember last selected profile at startup</string>
<string name="settings_advanced_clear_cw_cache">Clear Continue Watching Cache</string>
<string name="settings_advanced_clear_cw_cache_subtitle">Remove cached thumbnails, titles, and enrichment data for Continue Watching</string>
<string name="settings_advanced_clear_cw_cache_subtitle">Remove cached Continue Watching data and refresh watch progress</string>
<string name="settings_advanced_clear_cw_cache_done">Cache cleared</string>
<string name="settings_licenses_attributions_section_app">APP LICENSE</string>
<string name="settings_licenses_attributions_section_data">DATA &amp; SERVICES</string>

View file

@ -281,11 +281,20 @@ fun HomeScreen(
}
val profileState by ProfileRepository.state.collectAsStateWithLifecycle()
val activeProfileId = profileState.activeProfile?.profileIndex ?: 1
val cwCacheClearVersion by ContinueWatchingEnrichmentCache.cacheCleared.collectAsStateWithLifecycle()
var nextUpItemsBySeries by remember(activeProfileId) { mutableStateOf<Map<String, Pair<Long, ContinueWatchingItem>>>(emptyMap()) }
var processedNextUpContentIds by remember(activeProfileId) { mutableStateOf<Set<String>>(emptySet()) }
val cachedSnapshots = remember(activeProfileId) { ContinueWatchingEnrichmentCache.getSnapshots() }
LaunchedEffect(activeProfileId, cwCacheClearVersion) {
if (cwCacheClearVersion == 0) return@LaunchedEffect
nextUpItemsBySeries = emptyMap()
processedNextUpContentIds = emptySet()
}
val cachedSnapshots = remember(activeProfileId, cwCacheClearVersion) {
ContinueWatchingEnrichmentCache.getSnapshots()
}
val shouldValidateMissingNextUpSeeds = remember(
isTraktProgressActive,
watchProgressUiState.hasLoadedRemoteProgress,

View file

@ -3,10 +3,13 @@ package com.nuvio.app.features.settings
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import com.nuvio.app.features.profiles.ProfileRepository
import com.nuvio.app.features.watchprogress.ContinueWatchingEnrichmentCache
import com.nuvio.app.features.watchprogress.WatchProgressRepository
import kotlinx.coroutines.launch
import nuvio.composeapp.generated.resources.Res
import nuvio.composeapp.generated.resources.settings_advanced_clear_cw_cache
import nuvio.composeapp.generated.resources.settings_advanced_clear_cw_cache_done
@ -43,6 +46,7 @@ internal fun LazyListScope.advancedSettingsContent(
isTablet = isTablet,
) {
SettingsGroup(isTablet = isTablet) {
val scope = rememberCoroutineScope()
var cleared by rememberSaveable { mutableStateOf(false) }
SettingsNavigationRow(
title = stringResource(Res.string.settings_advanced_clear_cw_cache),
@ -56,6 +60,11 @@ internal fun LazyListScope.advancedSettingsContent(
if (!cleared) {
ContinueWatchingEnrichmentCache.clearAll()
cleared = true
scope.launch {
WatchProgressRepository.forceSnapshotRefreshFromServer(
ProfileRepository.activeProfileId,
)
}
}
},
)

View file

@ -1,6 +1,9 @@
package com.nuvio.app.features.watchprogress
import com.nuvio.app.core.storage.ProfileScopedKey
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
@ -63,6 +66,8 @@ internal object ContinueWatchingEnrichmentCache {
private const val storageKey = "cw_enrichment_cache"
private var lastPayloadHash: Int? = null
private val _cacheCleared = MutableStateFlow(0)
val cacheCleared: StateFlow<Int> = _cacheCleared.asStateFlow()
fun getNextUpSnapshot(): List<CachedNextUpItem> =
loadPayload()?.nextUp ?: emptyList()
@ -98,6 +103,7 @@ internal object ContinueWatchingEnrichmentCache {
fun clearAll() {
ContinueWatchingEnrichmentStorage.removePayload(ProfileScopedKey.of(storageKey))
lastPayloadHash = null
_cacheCleared.value += 1
}
private fun loadPayload(): CachedEnrichmentPayload? {

View file

@ -251,6 +251,35 @@ object WatchProgressRepository {
}
}
suspend fun forceSnapshotRefreshFromServer(profileId: Int) {
ensureLoaded()
if (currentProfileId != profileId) {
loadFromDisk(profileId)
}
if (shouldUseTraktProgress()) {
log.d { "Force refreshing Trakt watch progress for profile $profileId" }
runCatching { TraktProgressRepository.refreshNow() }
.onFailure { error ->
if (error is CancellationException) throw error
log.e(error) { "Failed to force refresh Trakt progress" }
}
publish()
return
}
val authState = AuthRepository.state.value
if (authState !is AuthState.Authenticated || authState.isAnonymous) {
log.d { "Skipping force watch progress refresh because Nuvio Sync is not authenticated" }
return
}
deltaCursorEventId = 0L
deltaInitialized = false
persist()
pullFromServer(profileId)
}
private suspend fun pullSupabaseDeltaFromServer(
profileId: Int,
pullStartedEpochMs: Long,