From 5061277fd12babe4a722cd2e427124073047c399 Mon Sep 17 00:00:00 2001 From: damontecres <154766448+damontecres@users.noreply.github.com> Date: Thu, 13 Nov 2025 11:36:57 -0500 Subject: [PATCH] Keep activity tracking alive even after errors (#209) Previously if the activity tracker got an error from the server during one of the reports, the timer thread would stop and no further reports would be sent. This PR fixes that so it will continue to attempt to send reports since if playback is continuing without error, the report error is likely transient. For example, I observed the server returning an error during its DB optimization scheduled task. I think this will fix #208 --- .../util/TrackActivityPlaybackListener.kt | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/TrackActivityPlaybackListener.kt b/app/src/main/java/com/github/damontecres/wholphin/util/TrackActivityPlaybackListener.kt index 1e50d4a0..b0c1f593 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/TrackActivityPlaybackListener.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/util/TrackActivityPlaybackListener.kt @@ -4,10 +4,10 @@ import androidx.annotation.OptIn import androidx.media3.common.Player import androidx.media3.common.util.UnstableApi import com.github.damontecres.wholphin.data.model.ItemPlayback +import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.playback.CurrentPlayback import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.jellyfin.sdk.api.client.ApiClient import org.jellyfin.sdk.api.client.extensions.playStateApi @@ -49,7 +49,7 @@ class TrackActivityPlaybackListener( private var initialized = false fun init() { - coroutineScope.launch(Dispatchers.IO + ExceptionHandler()) { + launch("reportPlaybackStart") { Timber.v("reportPlaybackStart for ${itemPlayback.itemId}") api.playStateApi.reportPlaybackStart( PlaybackStartInfo( @@ -77,7 +77,7 @@ class TrackActivityPlaybackListener( task.cancel() TIMER.purge() val position = player.currentPosition.milliseconds - coroutineScope.launch(Dispatchers.IO + ExceptionHandler()) { + launch("reportPlaybackStopped") { Timber.v("reportPlaybackStopped for ${itemPlayback.itemId} at $position") api.playStateApi.reportPlaybackStopped( PlaybackStopInfo( @@ -107,7 +107,7 @@ class TrackActivityPlaybackListener( } private fun saveActivity(position: Long) { - coroutineScope.launch(Dispatchers.IO + ExceptionHandler()) { + launch("saveActivity") { val calcPosition = withContext(Dispatchers.Main) { (if (position >= 0) position else player.currentPosition) @@ -135,6 +135,19 @@ class TrackActivityPlaybackListener( } } + private fun launch( + name: String, + block: suspend CoroutineScope.() -> Unit, + ) { + coroutineScope.launchIO { + try { + block.invoke(this) + } catch (ex: Exception) { + Timber.w(ex, "Exception during %s for %s", name, itemPlayback.itemId) + } + } + } + companion object { private const val TAG = "TrackActivityPlaybackListener"