add back periodic refresh

This commit is contained in:
tapframe 2026-07-08 21:02:38 +05:30
parent 8fa32ae5a1
commit 4fcf4fb542
3 changed files with 91 additions and 18 deletions

View file

@ -944,6 +944,19 @@ private fun MainAppContent(
}
}
DisposableEffect(authState, profileState.activeProfile?.profileIndex) {
val authenticatedState = authState as? AuthState.Authenticated
val activeProfileId = profileState.activeProfile?.profileIndex
if (authenticatedState != null && !authenticatedState.isAnonymous && activeProfileId != null) {
SyncManager.startPeriodicNuvioSyncPull(activeProfileId)
} else {
SyncManager.stopPeriodicNuvioSyncPull()
}
onDispose {
SyncManager.stopPeriodicNuvioSyncPull()
}
}
LaunchedEffect(authState, profileState.activeProfile?.profileIndex) {
val authenticatedState = authState as? AuthState.Authenticated ?: return@LaunchedEffect
if (authenticatedState.isAnonymous) return@LaunchedEffect

View file

@ -7,11 +7,16 @@ import com.nuvio.app.core.build.AppFeaturePolicy
import com.nuvio.app.features.addons.AddonRepository
import com.nuvio.app.features.collection.CollectionSyncService
import com.nuvio.app.features.home.HomeCatalogSettingsSyncService
import com.nuvio.app.features.library.LibrarySourceMode
import com.nuvio.app.features.library.LibraryRepository
import com.nuvio.app.features.plugins.PluginRepository
import com.nuvio.app.features.profiles.ProfileRepository
import com.nuvio.app.features.trakt.TraktAuthRepository
import com.nuvio.app.features.trakt.TraktCredentialSync
import com.nuvio.app.features.trakt.TraktPlatformClock
import com.nuvio.app.features.trakt.TraktSettingsRepository
import com.nuvio.app.features.trakt.effectiveLibrarySourceMode
import com.nuvio.app.features.trakt.shouldUseTraktProgress
import com.nuvio.app.features.watched.WatchedRepository
import com.nuvio.app.features.watchprogress.WatchProgressRepository
import kotlinx.coroutines.CoroutineScope
@ -19,15 +24,19 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
private const val FOREGROUND_PULL_DELAY_MS = 2500L
private const val FOREGROUND_PULL_MIN_INTERVAL_MS = 30 * 60_000L
private const val PERIODIC_NUVIO_SYNC_PULL_INTERVAL_MS = 60_000L
object SyncManager {
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
private val log = Logger.withTag("SyncManager")
private var foregroundPullJob: Job? = null
private var periodicNuvioSyncPullJob: Job? = null
private var periodicNuvioSyncProfileId: Int? = null
private var lastForegroundPullAtMs: Long = 0L
fun pullAllForProfile(profileId: Int) {
@ -36,7 +45,7 @@ object SyncManager {
if (authState.isAnonymous) return
scope.launch {
log.i { "pullAllForProfile($profileId) — auth=${(authState as AuthState.Authenticated).isAnonymous}" }
log.i { "pullAllForProfile($profileId) — auth=${authState.isAnonymous}" }
log.i { "pullAllForProfile — pulling addons first (await)..." }
runCatching { AddonRepository.pullFromServer(profileId) }
@ -105,6 +114,68 @@ object SyncManager {
}
}
fun startPeriodicNuvioSyncPull(profileId: Int) {
val authState = AuthRepository.state.value
if (authState !is AuthState.Authenticated || authState.isAnonymous) {
stopPeriodicNuvioSyncPull()
return
}
if (periodicNuvioSyncPullJob?.isActive == true && periodicNuvioSyncProfileId == profileId) return
stopPeriodicNuvioSyncPull()
periodicNuvioSyncProfileId = profileId
periodicNuvioSyncPullJob = scope.launch {
while (isActive) {
delay(PERIODIC_NUVIO_SYNC_PULL_INTERVAL_MS)
val currentAuthState = AuthRepository.state.value
if (currentAuthState !is AuthState.Authenticated || currentAuthState.isAnonymous) {
continue
}
if (ProfileRepository.activeProfileId != profileId) {
continue
}
TraktAuthRepository.ensureLoaded()
TraktSettingsRepository.ensureLoaded()
val traktAuthenticated = TraktAuthRepository.isAuthenticated.value
val settings = TraktSettingsRepository.uiState.value
val shouldPullLibrary = effectiveLibrarySourceMode(
isAuthenticated = traktAuthenticated,
source = settings.librarySourceMode,
) == LibrarySourceMode.LOCAL
val shouldPullWatchProgress = !shouldUseTraktProgress(
isAuthenticated = traktAuthenticated,
source = settings.watchProgressSource,
)
if (!shouldPullLibrary && !shouldPullWatchProgress) {
continue
}
log.i {
"Periodic Nuvio sync pull profile=$profileId " +
"library=$shouldPullLibrary watchProgress=$shouldPullWatchProgress"
}
if (shouldPullLibrary) {
runCatching { LibraryRepository.pullFromServer(profileId) }
.onFailure { log.e(it) { "Periodic Nuvio library pull failed" } }
}
if (shouldPullWatchProgress) {
runCatching { WatchProgressRepository.pullFromServer(profileId) }
.onFailure { log.e(it) { "Periodic Nuvio watch progress pull failed" } }
}
}
}
}
fun stopPeriodicNuvioSyncPull() {
periodicNuvioSyncPullJob?.cancel()
periodicNuvioSyncPullJob = null
periodicNuvioSyncProfileId = null
}
fun requestRealtimeSurfacePull(profileId: Int, surface: String) {
val authState = AuthRepository.state.value
if (authState !is AuthState.Authenticated || authState.isAnonymous) return

View file

@ -51,7 +51,6 @@ private const val METADATA_FETCH_TIMEOUT_MS = 3_500L
private const val METADATA_FETCH_CONCURRENCY = 5
private const val METADATA_HYDRATION_LIMIT = 110
private const val REFRESH_BASE_INTERVAL_MS = 60L * 1000L
private const val REFRESH_MAX_INTERVAL_MS = 15L * 60L * 1000L
private const val EPISODE_PROGRESS_CACHE_TTL_MS = 30L * 60L * 1000L
private const val EPISODE_PROGRESS_FETCH_THROTTLE_MS = 60L * 1000L
private const val MILLIS_PER_DAY = 24L * 60L * 60L * 1000L
@ -80,7 +79,6 @@ object TraktProgressRepository {
private val refreshJobMutex = Mutex()
private var inFlightRefresh: Deferred<Unit>? = null
private var refreshIntervalMs = REFRESH_BASE_INTERVAL_MS
private var consecutiveRefreshFailures = 0
private var lastKnownMoviesWatchedAt: String? = null
private var lastKnownEpisodeActivityFingerprint: String? = null
private var lastKnownActivityFingerprint: String? = null
@ -103,17 +101,17 @@ object TraktProgressRepository {
source = TraktSettingsRepository.uiState.value.watchProgressSource,
)
) {
updateRefreshBackoff(success = true)
resetRefreshInterval()
continue
}
val success = runCatching {
runCatching {
refreshIfActivityChanged()
}.onFailure { error ->
if (error is CancellationException) throw error
log.w { "Periodic Trakt activity refresh failed: ${error.message}" }
}.isSuccess
updateRefreshBackoff(success = success)
}
resetRefreshInterval()
}
}
}
@ -272,23 +270,14 @@ object TraktProgressRepository {
return changed
}
private fun updateRefreshBackoff(success: Boolean) {
if (success) {
consecutiveRefreshFailures = 0
refreshIntervalMs = REFRESH_BASE_INTERVAL_MS
return
}
consecutiveRefreshFailures += 1
refreshIntervalMs = (REFRESH_BASE_INTERVAL_MS shl (consecutiveRefreshFailures - 1))
.coerceAtMost(REFRESH_MAX_INTERVAL_MS)
private fun resetRefreshInterval() {
refreshIntervalMs = REFRESH_BASE_INTERVAL_MS
}
private fun resetActivitySnapshot() {
lastKnownMoviesWatchedAt = null
lastKnownEpisodeActivityFingerprint = null
lastKnownActivityFingerprint = null
consecutiveRefreshFailures = 0
refreshIntervalMs = REFRESH_BASE_INTERVAL_MS
}