From 085b136e5af075f56062228c10d35ae49342b091 Mon Sep 17 00:00:00 2001 From: tapframe <85391825+tapframe@users.noreply.github.com> Date: Fri, 24 Jul 2026 18:18:25 +0530 Subject: [PATCH] fix(simkl): hide on-hold items from continue watching --- .../simkl/SimklApplicationAdapters.kt | 5 ++- .../simkl/SimklPlaybackReconciliation.kt | 17 ++++---- .../simkl/SimklPlaybackReconciliationTest.kt | 42 +++++++++++++++++-- 3 files changed, 51 insertions(+), 13 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/simkl/SimklApplicationAdapters.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/simkl/SimklApplicationAdapters.kt index 11f44e84f..e3c8e4388 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/simkl/SimklApplicationAdapters.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/simkl/SimklApplicationAdapters.kt @@ -197,7 +197,8 @@ object SimklTrackingProgressProvider : TrackingProgressProvider { val state = SimklProgressRepository.uiState.value return TrackingProgressSnapshot( entries = state.entries, - hiddenContentIds = SimklSyncRepository.state.value.snapshot.droppedContentIds(), + hiddenContentIds = SimklSyncRepository.state.value.snapshot + .hiddenFromContinueWatchingContentIds(), hasLoadedRemoteProgress = state.hasLoadedRemoteProgress, errorMessage = state.errorMessage, ) @@ -207,7 +208,7 @@ object SimklTrackingProgressProvider : TrackingProgressProvider { SimklProgressRepository.removeProgress(entries) override fun isHiddenFromProgress(contentId: String): Boolean = - SimklSyncRepository.state.value.snapshot.isDroppedContent(contentId) + SimklSyncRepository.state.value.snapshot.isHiddenFromContinueWatching(contentId) } private const val SIMKL_PLAYBACK_PROGRESS_KEY_PREFIX = "simkl-playback:" diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/simkl/SimklPlaybackReconciliation.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/simkl/SimklPlaybackReconciliation.kt index baebb3c26..9caf01dfc 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/simkl/SimklPlaybackReconciliation.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/simkl/SimklPlaybackReconciliation.kt @@ -15,18 +15,21 @@ internal fun SimklSyncSnapshot.reconcileWatchedPlayback(): SimklSyncSnapshot { return if (retainedPlayback.size == playback.size) this else copy(playback = retainedPlayback) } -internal fun SimklSyncSnapshot.isDroppedContent(contentId: String): Boolean = +internal fun SimklSyncSnapshot.isHiddenFromContinueWatching(contentId: String): Boolean = entries.any { entry -> - entry.status == SimklListStatus.DROPPED && + entry.status.hidesContinueWatching() && entry.matchesContent(contentId = contentId, trackingProviderItemId = null) } -internal fun SimklSyncSnapshot.droppedContentIds(): Set = +internal fun SimklSyncSnapshot.hiddenFromContinueWatchingContentIds(): Set = entries.asSequence() - .filter { entry -> entry.status == SimklListStatus.DROPPED } + .filter { entry -> entry.status.hidesContinueWatching() } .mapNotNull { entry -> entry.media?.canonicalContentId() } .toSet() +internal fun SimklListStatus?.hidesContinueWatching(): Boolean = + this == SimklListStatus.ON_HOLD || this == SimklListStatus.DROPPED + private fun WatchedItem.supersedes(progress: WatchProgressEntry): Boolean { if (!type.equals(progress.contentType, ignoreCase = true)) return false if (season != progress.seasonNumber || episode != progress.episodeNumber) return false @@ -46,9 +49,9 @@ private fun SimklLibraryEntry.hidesPlayback(progress: WatchProgressEntry): Boole ) { return false } - return when (status) { - SimklListStatus.DROPPED -> true - SimklListStatus.COMPLETED -> + return when { + status.hidesContinueWatching() -> true + status == SimklListStatus.COMPLETED -> parseSimklUtcEpochMs(lastWatchedAt)?.let { completedAt -> completedAt >= progress.lastUpdatedEpochMs } == true diff --git a/composeApp/src/commonTest/kotlin/com/nuvio/app/features/simkl/SimklPlaybackReconciliationTest.kt b/composeApp/src/commonTest/kotlin/com/nuvio/app/features/simkl/SimklPlaybackReconciliationTest.kt index 1253b1fbe..ec31e6287 100644 --- a/composeApp/src/commonTest/kotlin/com/nuvio/app/features/simkl/SimklPlaybackReconciliationTest.kt +++ b/composeApp/src/commonTest/kotlin/com/nuvio/app/features/simkl/SimklPlaybackReconciliationTest.kt @@ -254,8 +254,39 @@ class SimklPlaybackReconciliationTest { ) assertTrue(snapshot.reconcileWatchedPlayback().playback.isEmpty()) - assertTrue(snapshot.isDroppedContent("tt4574334")) - assertEquals(setOf("tt4574334"), snapshot.droppedContentIds()) + assertTrue(snapshot.isHiddenFromContinueWatching("tt4574334")) + assertEquals( + setOf("tt4574334"), + snapshot.hiddenFromContinueWatchingContentIds(), + ) + } + + @Test + fun `on hold series discards playback using provider identity`() { + val snapshot = SimklSyncSnapshot( + entries = listOf( + SimklLibraryEntry( + mediaType = SimklMediaType.SHOWS, + status = SimklListStatus.ON_HOLD, + show = media(39687, imdb = "tt4574334"), + ), + ), + playback = listOf( + episodePlayback( + playbackMedia = media(39687, tvdb = "305288"), + season = 1, + episode = 5, + pausedAt = "2024-04-30T22:14:00Z", + ), + ), + ) + + assertTrue(snapshot.reconcileWatchedPlayback().playback.isEmpty()) + assertTrue(snapshot.isHiddenFromContinueWatching("tt4574334")) + assertEquals( + setOf("tt4574334"), + snapshot.hiddenFromContinueWatchingContentIds(), + ) } @Test @@ -279,8 +310,11 @@ class SimklPlaybackReconciliationTest { ) assertEquals(snapshot.playback, snapshot.reconcileWatchedPlayback().playback) - assertFalse(snapshot.isDroppedContent("tt4574334")) - assertEquals(setOf("tt1111111"), snapshot.droppedContentIds()) + assertFalse(snapshot.isHiddenFromContinueWatching("tt4574334")) + assertEquals( + setOf("tt1111111"), + snapshot.hiddenFromContinueWatchingContentIds(), + ) } @Test