ref(sync): pull method to support optional parameters for last watched and limit

This commit is contained in:
tapframe 2026-05-20 16:45:35 +05:30
parent 9818458b9f
commit de68ce2c30
3 changed files with 60 additions and 32 deletions

View file

@ -14,7 +14,11 @@ data class ProgressSyncRecord(
) )
interface ProgressSyncAdapter { interface ProgressSyncAdapter {
suspend fun pull(profileId: Int): List<ProgressSyncRecord> suspend fun pull(
profileId: Int,
sinceLastWatched: Long? = null,
limit: Int? = null,
): List<ProgressSyncRecord>
suspend fun push( suspend fun push(
profileId: Int, profileId: Int,

View file

@ -17,8 +17,20 @@ object SupabaseProgressSyncAdapter : ProgressSyncAdapter {
encodeDefaults = true encodeDefaults = true
} }
override suspend fun pull(profileId: Int): List<ProgressSyncRecord> { override suspend fun pull(
val params = buildJsonObject { put("p_profile_id", profileId) } profileId: Int,
sinceLastWatched: Long?,
limit: Int?,
): List<ProgressSyncRecord> {
val params = buildJsonObject {
put("p_profile_id", profileId)
if (sinceLastWatched != null) {
put("p_since_last_watched", sinceLastWatched)
}
if (limit != null) {
put("p_limit", limit)
}
}
val result = SupabaseProvider.client.postgrest.rpc("sync_pull_watch_progress", params) val result = SupabaseProvider.client.postgrest.rpc("sync_pull_watch_progress", params)
val serverEntries = result.decodeList<WatchProgressSyncEntry>() val serverEntries = result.decodeList<WatchProgressSyncEntry>()
return serverEntries.map { entry -> return serverEntries.map { entry ->

View file

@ -12,6 +12,7 @@ import com.nuvio.app.features.trakt.TraktProgressRepository
import com.nuvio.app.features.trakt.TraktSettingsRepository import com.nuvio.app.features.trakt.TraktSettingsRepository
import com.nuvio.app.features.trakt.shouldUseTraktProgress as shouldUseTraktProgressSource import com.nuvio.app.features.trakt.shouldUseTraktProgress as shouldUseTraktProgressSource
import com.nuvio.app.features.watching.application.WatchingActions import com.nuvio.app.features.watching.application.WatchingActions
import com.nuvio.app.features.watching.sync.ProgressSyncRecord
import com.nuvio.app.features.watching.sync.ProgressSyncAdapter import com.nuvio.app.features.watching.sync.ProgressSyncAdapter
import com.nuvio.app.features.watching.sync.SupabaseProgressSyncAdapter import com.nuvio.app.features.watching.sync.SupabaseProgressSyncAdapter
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
@ -180,38 +181,23 @@ object WatchProgressRepository {
} }
runCatching { runCatching {
val serverEntries = syncAdapter.pull(profileId = profileId) val sinceLastWatched = entriesByVideoId.values
.maxOfOrNull { entry -> entry.lastUpdatedEpochMs }
?.takeIf { hasCompletedInitialNuvioSyncPull }
val serverEntries = syncAdapter.pull(
profileId = profileId,
sinceLastWatched = sinceLastWatched,
)
val isIncrementalPull = sinceLastWatched != null
val oldLocal = entriesByVideoId.toMap() val oldLocal = entriesByVideoId.toMap()
val newMap = mutableMapOf<String, WatchProgressEntry>() val newMap = if (isIncrementalPull) {
entriesByVideoId.toMutableMap()
} else {
mutableMapOf()
}
serverEntries.forEach { entry -> serverEntries.forEach { entry ->
val videoId = entry.videoId newMap[entry.videoId] = entry.toWatchProgressEntry(cached = oldLocal[entry.videoId])
val cached = oldLocal[videoId]
newMap[videoId] = WatchProgressEntry(
contentType = entry.contentType,
parentMetaId = entry.contentId,
parentMetaType = cached?.parentMetaType ?: entry.contentType,
videoId = videoId,
title = cached?.title?.takeIf { it.isNotBlank() } ?: entry.contentId,
logo = cached?.logo,
poster = cached?.poster,
background = cached?.background,
seasonNumber = entry.season,
episodeNumber = entry.episode,
episodeTitle = cached?.episodeTitle,
episodeThumbnail = cached?.episodeThumbnail,
lastPositionMs = entry.position,
durationMs = entry.duration,
lastUpdatedEpochMs = entry.lastWatched,
providerName = cached?.providerName,
providerAddonId = cached?.providerAddonId,
lastStreamTitle = cached?.lastStreamTitle,
lastStreamSubtitle = cached?.lastStreamSubtitle,
pauseDescription = cached?.pauseDescription,
lastSourceUrl = cached?.lastSourceUrl,
isCompleted = isWatchProgressComplete(entry.position, entry.duration, false),
)
} }
entriesByVideoId = newMap entriesByVideoId = newMap
@ -232,6 +218,32 @@ object WatchProgressRepository {
} }
} }
private fun ProgressSyncRecord.toWatchProgressEntry(cached: WatchProgressEntry?): WatchProgressEntry =
WatchProgressEntry(
contentType = contentType,
parentMetaId = contentId,
parentMetaType = cached?.parentMetaType ?: contentType,
videoId = videoId,
title = cached?.title?.takeIf { it.isNotBlank() } ?: contentId,
logo = cached?.logo,
poster = cached?.poster,
background = cached?.background,
seasonNumber = season,
episodeNumber = episode,
episodeTitle = cached?.episodeTitle,
episodeThumbnail = cached?.episodeThumbnail,
lastPositionMs = position,
durationMs = duration,
lastUpdatedEpochMs = lastWatched,
providerName = cached?.providerName,
providerAddonId = cached?.providerAddonId,
lastStreamTitle = cached?.lastStreamTitle,
lastStreamSubtitle = cached?.lastStreamSubtitle,
pauseDescription = cached?.pauseDescription,
lastSourceUrl = cached?.lastSourceUrl,
isCompleted = isWatchProgressComplete(position, duration, false),
)
private fun resolveRemoteMetadata() { private fun resolveRemoteMetadata() {
val needsResolution = entriesByVideoId.values val needsResolution = entriesByVideoId.values
.filter { it.poster.isNullOrBlank() || it.background.isNullOrBlank() } .filter { it.poster.isNullOrBlank() || it.background.isNullOrBlank() }