mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-08-01 00:59:27 +00:00
add back periodic refresh
This commit is contained in:
parent
8fa32ae5a1
commit
4fcf4fb542
3 changed files with 91 additions and 18 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue