From 0e5ba830d2fc85efc355a78c067d9f797b0ebac4 Mon Sep 17 00:00:00 2001 From: Aniket Tuli Date: Fri, 12 Jun 2026 02:37:10 -0700 Subject: [PATCH] fix: stop scrobbling next episode as watched and surface Trakt save errors Fixes #1196 Fixes #1192 Co-Authored-By: Claude Fable 5 --- .../app/features/library/LibraryRepository.kt | 10 +++++++++- .../player/PlayerScreenRuntimeEffects.kt | 11 ++++++++++- .../PlayerScreenRuntimePlaybackActions.kt | 17 ++++++++++++++++- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/library/LibraryRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/library/LibraryRepository.kt index c4c86bb0..7e5242e1 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/library/LibraryRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/library/LibraryRepository.kt @@ -2,6 +2,7 @@ package com.nuvio.app.features.library import co.touchlab.kermit.Logger import com.nuvio.app.core.auth.AuthRepository +import com.nuvio.app.core.ui.NuvioToastController import com.nuvio.app.core.auth.AuthState import com.nuvio.app.core.network.SupabaseProvider import com.nuvio.app.features.home.PosterShape @@ -40,6 +41,7 @@ import kotlinx.serialization.json.put import nuvio.composeapp.generated.resources.Res import nuvio.composeapp.generated.resources.library_local_tab_title import nuvio.composeapp.generated.resources.library_other +import nuvio.composeapp.generated.resources.trakt_lists_update_failed import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.getString @@ -218,7 +220,13 @@ object LibraryRepository { if (isTraktLibrarySourceActive()) { syncScope.launch { runCatching { TraktLibraryRepository.toggleWatchlist(item) } - .onFailure { e -> log.e(e) { "Failed to toggle Trakt watchlist" } } + .onFailure { e -> + log.e(e) { "Failed to toggle Trakt watchlist" } + NuvioToastController.show( + e.message?.takeIf { it.isNotBlank() } + ?: getString(Res.string.trakt_lists_update_failed), + ) + } publish() } return diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimeEffects.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimeEffects.kt index e1e5f81f..f688458e 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimeEffects.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimeEffects.kt @@ -244,8 +244,17 @@ internal fun PlayerScreenRuntime.BindPlayerRuntimeEffects() { BindPlayerMetadataAndSkipEffects() DisposableEffect(playbackSession.videoId, activeSourceUrl, activeSourceAudioUrl) { + val effectVideoId = playbackSession.videoId + val effectSourceUrl = activeSourceUrl + val effectSourceAudioUrl = activeSourceAudioUrl onDispose { - flushWatchProgress() + if ( + playbackSession.videoId == effectVideoId && + activeSourceUrl == effectSourceUrl && + activeSourceAudioUrl == effectSourceAudioUrl + ) { + flushWatchProgress() + } } } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimePlaybackActions.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimePlaybackActions.kt index 22828532..98c843d3 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimePlaybackActions.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimePlaybackActions.kt @@ -120,8 +120,23 @@ internal fun PlayerScreenRuntime.emitTraktScrobbleStop(progressPercent: Float? = val percent = provided ?: currentPlaybackProgressPercent() val itemSnapshot = currentTraktScrobbleItem + val contentTypeSnapshot = contentType ?: parentMetaType + val parentMetaIdSnapshot = parentMetaId + val videoIdSnapshot = activeVideoId + val titleSnapshot = title + val seasonNumberSnapshot = activeSeasonNumber + val episodeNumberSnapshot = activeEpisodeNumber + val episodeTitleSnapshot = activeEpisodeTitle scope.launch(NonCancellable) { - val item = itemSnapshot ?: currentTraktScrobbleItem() ?: return@launch + val item = itemSnapshot ?: TraktScrobbleRepository.buildItem( + contentType = contentTypeSnapshot, + parentMetaId = parentMetaIdSnapshot, + videoId = videoIdSnapshot, + title = titleSnapshot, + seasonNumber = seasonNumberSnapshot, + episodeNumber = episodeNumberSnapshot, + episodeTitle = episodeTitleSnapshot, + ) ?: return@launch TraktScrobbleRepository.scrobbleStop( item = item, progressPercent = percent,