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 c4c86bb08..7e5242e13 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 c0436c9d4..886046c6f 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 @@ -239,8 +239,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 22828532e..52ee175e5 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 @@ -80,17 +80,40 @@ internal fun PlayerScreenRuntime.currentPlaybackProgressPercent( .coerceIn(0f, 100f) } -internal suspend fun PlayerScreenRuntime.currentTraktScrobbleItem() = +internal data class TraktScrobbleItemInputs( + val contentType: String, + val parentMetaId: String, + val videoId: String?, + val title: String, + val seasonNumber: Int?, + val episodeNumber: Int?, + val episodeTitle: String?, +) + +internal fun PlayerScreenRuntime.snapshotTraktScrobbleItemInputs() = TraktScrobbleItemInputs( + contentType = contentType ?: parentMetaType, + parentMetaId = parentMetaId, + videoId = activeVideoId, + title = title, + seasonNumber = activeSeasonNumber, + episodeNumber = activeEpisodeNumber, + episodeTitle = activeEpisodeTitle, +) + +private suspend fun TraktScrobbleItemInputs.buildItem() = TraktScrobbleRepository.buildItem( - contentType = contentType ?: parentMetaType, + contentType = contentType, parentMetaId = parentMetaId, - videoId = activeVideoId, + videoId = videoId, title = title, - seasonNumber = activeSeasonNumber, - episodeNumber = activeEpisodeNumber, - episodeTitle = activeEpisodeTitle, + seasonNumber = seasonNumber, + episodeNumber = episodeNumber, + episodeTitle = episodeTitle, ) +internal suspend fun PlayerScreenRuntime.currentTraktScrobbleItem() = + snapshotTraktScrobbleItemInputs().buildItem() + internal fun PlayerScreenRuntime.emitTraktScrobbleStart() { if (hasRequestedScrobbleStartForCurrentItem) return hasRequestedScrobbleStartForCurrentItem = true @@ -120,8 +143,9 @@ internal fun PlayerScreenRuntime.emitTraktScrobbleStop(progressPercent: Float? = val percent = provided ?: currentPlaybackProgressPercent() val itemSnapshot = currentTraktScrobbleItem + val inputsSnapshot = snapshotTraktScrobbleItemInputs() scope.launch(NonCancellable) { - val item = itemSnapshot ?: currentTraktScrobbleItem() ?: return@launch + val item = itemSnapshot ?: inputsSnapshot.buildItem() ?: return@launch TraktScrobbleRepository.scrobbleStop( item = item, progressPercent = percent,