Make TrackActivityPlaybackListener more generic

This commit is contained in:
Damontecres 2026-03-16 13:30:25 -04:00
parent 9badecf086
commit 6c26891bff
No known key found for this signature in database
2 changed files with 49 additions and 23 deletions

View file

@ -63,6 +63,7 @@ import com.github.damontecres.wholphin.ui.showToast
import com.github.damontecres.wholphin.ui.toServerString
import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.LoadingState
import com.github.damontecres.wholphin.util.PlaybackItemState
import com.github.damontecres.wholphin.util.TrackActivityPlaybackListener
import com.github.damontecres.wholphin.util.checkForSupport
import com.github.damontecres.wholphin.util.mpv.mpvDeviceProfile
@ -737,12 +738,12 @@ class PlaybackViewModel
player.removeListener(it)
}
val playbackItemState = PlaybackItemState(playback, currentItemPlayback)
val activityListener =
TrackActivityPlaybackListener(
api = api,
player = player,
playback = playback,
itemPlayback = currentItemPlayback,
getState = { playbackItemState },
)
player.addListener(activityListener)
this@PlaybackViewModel.activityListener = activityListener

View file

@ -11,6 +11,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jellyfin.sdk.api.client.ApiClient
import org.jellyfin.sdk.api.client.extensions.playStateApi
import org.jellyfin.sdk.model.api.PlayMethod
import org.jellyfin.sdk.model.api.PlaybackOrder
import org.jellyfin.sdk.model.api.PlaybackProgressInfo
import org.jellyfin.sdk.model.api.PlaybackStartInfo
@ -20,6 +21,7 @@ import org.jellyfin.sdk.model.extensions.inWholeTicks
import timber.log.Timber
import java.util.Timer
import java.util.TimerTask
import java.util.UUID
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
@ -30,8 +32,7 @@ import kotlin.time.Duration.Companion.seconds
class TrackActivityPlaybackListener(
private val api: ApiClient,
private val player: Player,
val playback: CurrentPlayback,
val itemPlayback: ItemPlayback,
private val getState: () -> PlaybackItemState,
) : Player.Listener {
private val coroutineScope = CoroutineScope(Dispatchers.Main)
private val task: TimerTask =
@ -50,20 +51,21 @@ class TrackActivityPlaybackListener(
fun init() {
launch("reportPlaybackStart") {
Timber.v("reportPlaybackStart for ${itemPlayback.itemId}")
val state = getState.invoke()
Timber.v("reportPlaybackStart for ${state.itemId}")
api.playStateApi.reportPlaybackStart(
PlaybackStartInfo(
canSeek = true,
itemId = itemPlayback.itemId,
itemId = state.itemId,
isPaused = withContext(Dispatchers.Main) { !player.isPlaying },
playMethod = playback.playMethod,
playMethod = state.playMethod,
repeatMode = RepeatMode.REPEAT_NONE,
playbackOrder = PlaybackOrder.DEFAULT,
isMuted = false,
audioStreamIndex = itemPlayback.audioIndex.takeIf { itemPlayback.audioIndexEnabled },
subtitleStreamIndex = itemPlayback.subtitleIndex.takeIf { itemPlayback.subtitleIndexEnabled },
playSessionId = playback.playSessionId,
liveStreamId = playback.liveStreamId,
audioStreamIndex = state.audioStreamIndex,
subtitleStreamIndex = state.subtitleStreamIndex,
playSessionId = state.playSessionId,
liveStreamId = state.liveStreamId,
),
)
val delay = 5.seconds.inWholeMilliseconds
@ -78,14 +80,15 @@ class TrackActivityPlaybackListener(
TIMER.purge()
val position = player.currentPosition.milliseconds
launch("reportPlaybackStopped") {
Timber.v("reportPlaybackStopped for ${itemPlayback.itemId} at $position")
val state = getState.invoke()
Timber.v("reportPlaybackStopped for ${state.itemId} at $position")
api.playStateApi.reportPlaybackStopped(
PlaybackStopInfo(
itemId = itemPlayback.itemId,
itemId = state.itemId,
positionTicks = position.inWholeTicks,
failed = false,
playSessionId = playback.playSessionId,
liveStreamId = playback.liveStreamId,
playSessionId = state.playSessionId,
liveStreamId = state.liveStreamId,
),
)
}
@ -108,27 +111,28 @@ class TrackActivityPlaybackListener(
private fun saveActivity(position: Long) {
launch("saveActivity") {
val state = getState.invoke()
val calcPosition =
withContext(Dispatchers.Main) {
(if (position >= 0) position else player.currentPosition)
}
if (calcPosition > 0) {
val isPaused = withContext(Dispatchers.Main) { !player.isPlaying }
Timber.v("saveActivity: itemId=${itemPlayback.itemId}, pos=$calcPosition")
Timber.v("saveActivity: itemId=${state.itemId}, pos=$calcPosition")
api.playStateApi.reportPlaybackProgress(
PlaybackProgressInfo(
itemId = itemPlayback.itemId,
itemId = state.itemId,
positionTicks = calcPosition.milliseconds.inWholeTicks,
canSeek = true,
isPaused = isPaused,
isMuted = false,
playMethod = playback.playMethod,
playMethod = state.playMethod,
repeatMode = RepeatMode.REPEAT_NONE,
playbackOrder = PlaybackOrder.DEFAULT,
audioStreamIndex = itemPlayback.audioIndex.takeIf { itemPlayback.audioIndexEnabled },
subtitleStreamIndex = itemPlayback.subtitleIndex.takeIf { itemPlayback.subtitleIndexEnabled },
playSessionId = playback.playSessionId,
liveStreamId = playback.liveStreamId,
audioStreamIndex = state.audioStreamIndex,
subtitleStreamIndex = state.subtitleStreamIndex,
playSessionId = state.playSessionId,
liveStreamId = state.liveStreamId,
),
)
}
@ -143,7 +147,7 @@ class TrackActivityPlaybackListener(
try {
block.invoke(this)
} catch (ex: Exception) {
Timber.w(ex, "Exception during %s for %s", name, itemPlayback.itemId)
Timber.w(ex, "Exception during %s", name)
}
}
}
@ -154,3 +158,24 @@ class TrackActivityPlaybackListener(
private val TIMER by lazy { Timer("$TAG-timer", true) }
}
}
data class PlaybackItemState(
val itemId: UUID,
val playMethod: PlayMethod,
val audioStreamIndex: Int? = null,
val subtitleStreamIndex: Int? = null,
val playSessionId: String? = null,
val liveStreamId: String? = null,
) {
constructor(
playback: CurrentPlayback,
itemPlayback: ItemPlayback,
) : this(
itemId = itemPlayback.itemId,
playMethod = playback.playMethod,
audioStreamIndex = itemPlayback.audioIndex.takeIf { itemPlayback.audioIndexEnabled },
subtitleStreamIndex = itemPlayback.subtitleIndex.takeIf { itemPlayback.subtitleIndexEnabled },
playSessionId = playback.playSessionId,
liveStreamId = playback.liveStreamId,
)
}