diff --git a/app/src/main/java/com/fluxa/app/ui/catalog/PlayerPlaybackRuntimeEffects.kt b/app/src/main/java/com/fluxa/app/ui/catalog/PlayerPlaybackRuntimeEffects.kt index 3ef5543..a4c1368 100644 --- a/app/src/main/java/com/fluxa/app/ui/catalog/PlayerPlaybackRuntimeEffects.kt +++ b/app/src/main/java/com/fluxa/app/ui/catalog/PlayerPlaybackRuntimeEffects.kt @@ -45,6 +45,7 @@ internal fun ExoPlayerListenerEffect( exoEngine: ExoPlayerEngine, useMpvBackend: Boolean, currentUrl: String?, + telemetryAttemptGeneration: Long, currentStreamIndex: Int, currentStreamsSize: Int, autoFallbackOnStreamError: Boolean, @@ -65,7 +66,7 @@ internal fun ExoPlayerListenerEffect( val latestAutoFallbackOnStreamError by rememberUpdatedState(autoFallbackOnStreamError) val latestCurrentStreamIndex by rememberUpdatedState(currentStreamIndex) val latestCurrentStreamsSize by rememberUpdatedState(currentStreamsSize) - val telemetrySession = remember(currentUrl) { + val telemetrySession = remember(currentUrl, telemetryAttemptGeneration) { PlaybackTelemetrySession( id = currentUrl.takeIf { it.isTorrentPlaybackUrl() }?.let { UUID.randomUUID().toString() }, startedAtMs = android.os.SystemClock.elapsedRealtime() diff --git a/app/src/main/java/com/fluxa/app/ui/catalog/PlayerScreen.kt b/app/src/main/java/com/fluxa/app/ui/catalog/PlayerScreen.kt index a6b9860..2cc1b09 100644 --- a/app/src/main/java/com/fluxa/app/ui/catalog/PlayerScreen.kt +++ b/app/src/main/java/com/fluxa/app/ui/catalog/PlayerScreen.kt @@ -459,7 +459,10 @@ fun PlayerScreen( preferredBingeGroup = state.preferredBingeGroupForNextEpisode, viewModel = viewModel, lang = lang, - setCurrentUrl = { state.currentUrl = it }, + setCurrentUrl = { + if (state.currentUrl != it) state.telemetryAttemptGeneration++ + state.currentUrl = it + }, setCurrentStreams = { state.currentStreams = it }, setCurrentStreamIndex = { state.currentStreamIndex = it }, setZeroSpeedTicks = { state.zeroSpeedTicks = it }, @@ -501,6 +504,7 @@ fun PlayerScreen( exoEngine = exoEngine, useMpvBackend = useMpvBackend, currentUrl = state.currentUrl, + telemetryAttemptGeneration = state.telemetryAttemptGeneration, currentStreamIndex = state.currentStreamIndex, currentStreamsSize = state.currentStreams.size, autoFallbackOnStreamError = isCloudstreamPlayback() || activeProfile?.safeAutoRetryNextSource == true, @@ -517,6 +521,7 @@ fun PlayerScreen( val savedUrl = state.resolvedUrl if (savedUrl != null && torrentParseRetryCount.intValue < 2) { torrentParseRetryCount.intValue++ + state.telemetryAttemptGeneration++ scope.launch { exoEngine.clear() state.resolvedUrl = null diff --git a/app/src/main/java/com/fluxa/app/ui/catalog/PlayerScreenState.kt b/app/src/main/java/com/fluxa/app/ui/catalog/PlayerScreenState.kt index 1f1300d..a463006 100644 --- a/app/src/main/java/com/fluxa/app/ui/catalog/PlayerScreenState.kt +++ b/app/src/main/java/com/fluxa/app/ui/catalog/PlayerScreenState.kt @@ -55,6 +55,7 @@ internal class PlayerScreenState( ) var currentUrl by mutableStateOf(null) + var telemetryAttemptGeneration by mutableLongStateOf(0L) var resolvedUrl by mutableStateOf(null) var currentVideoId by mutableStateOf(initialVideoId) var currentStreams by mutableStateOf>(emptyList()) diff --git a/player/src/androidMain/kotlin/com/fluxa/app/player/TorrentStreamManager.kt b/player/src/androidMain/kotlin/com/fluxa/app/player/TorrentStreamManager.kt index 1feafc1..e364c9c 100644 --- a/player/src/androidMain/kotlin/com/fluxa/app/player/TorrentStreamManager.kt +++ b/player/src/androidMain/kotlin/com/fluxa/app/player/TorrentStreamManager.kt @@ -54,6 +54,7 @@ class TorrentStreamManager private constructor() { @Volatile private var engine: TorrentServerEngine? = null @Volatile private var activeTorrentLink: String? = null @Volatile private var activeTelemetrySessionId: String? = null + @Volatile private var activeTelemetryGeneration: Long = 0L private val _status = MutableStateFlow(TorrentStreamStatus()) val status: StateFlow = _status.asStateFlow() @@ -160,6 +161,7 @@ class TorrentStreamManager private constructor() { } startStatusPolling(plan.normalizedLink, videoId) activeTorrentLink = plan.normalizedLink + activeTelemetryGeneration += 1L callback(TorrentStreamResult.Success(plan.streamUrl)) } catch (e: Exception) { Log.e(TAG, "Torrent stream failed", e) @@ -188,18 +190,19 @@ class TorrentStreamManager private constructor() { fun beginPlaybackTelemetry(sessionId: String) { if (sessionId.isBlank() || activeTorrentLink == null) return activeTelemetrySessionId = sessionId - recordPlaybackTelemetry("sessionStarted", sessionId = sessionId) } fun recordPlaybackTelemetry(event: String, elapsedMs: Long? = null, sessionId: String) { val link = activeTorrentLink ?: return if (activeTelemetrySessionId != sessionId) return + val generation = activeTelemetryGeneration scope.launch { runCatching { val payload = gson.toJson( mapOf( "link" to link, "sessionId" to sessionId, + "sessionGeneration" to generation, "event" to event, "elapsedMs" to elapsedMs )