diff --git a/app/src/main/java/dev/jdtech/jellyfin/api/JellyfinApi.kt b/app/src/main/java/dev/jdtech/jellyfin/api/JellyfinApi.kt index 2284d04d..3637a91b 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/api/JellyfinApi.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/api/JellyfinApi.kt @@ -34,6 +34,7 @@ class JellyfinApi(context: Context, baseUrl: String) { val sessionApi = SessionApi(api) val videosApi = VideosApi(api) val mediaInfoApi = MediaInfoApi(api) + val playstateApi = PlayStateApi(api) companion object { @Volatile diff --git a/app/src/main/java/dev/jdtech/jellyfin/repository/JellyfinRepository.kt b/app/src/main/java/dev/jdtech/jellyfin/repository/JellyfinRepository.kt index b5298f61..e338a363 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/repository/JellyfinRepository.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/repository/JellyfinRepository.kt @@ -19,4 +19,10 @@ interface JellyfinRepository { suspend fun getMediaSources(itemId: UUID): List suspend fun getStreamUrl(itemId: UUID, mediaSourceId: String): String + + suspend fun postPlaybackStart(itemId: UUID) + + suspend fun postPlaybackStop(itemId: UUID, positionTicks: Long) + + suspend fun postPlaybackProgress(itemId: UUID, positionTicks: Long, isPaused: Boolean) } \ No newline at end of file diff --git a/app/src/main/java/dev/jdtech/jellyfin/repository/JellyfinRepositoryImpl.kt b/app/src/main/java/dev/jdtech/jellyfin/repository/JellyfinRepositoryImpl.kt index dfcdbae7..9cbecb71 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/repository/JellyfinRepositoryImpl.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/repository/JellyfinRepositoryImpl.kt @@ -101,7 +101,7 @@ class JellyfinRepositoryImpl(private val jellyfinApi: JellyfinApi) : JellyfinRep } override suspend fun getStreamUrl(itemId: UUID, mediaSourceId: String): String { - var streamUrl: String = "" + var streamUrl = "" withContext(Dispatchers.IO) { try { streamUrl = jellyfinApi.videosApi.getVideoStreamUrl( @@ -115,4 +115,24 @@ class JellyfinRepositoryImpl(private val jellyfinApi: JellyfinApi) : JellyfinRep } return streamUrl } + + override suspend fun postPlaybackStart(itemId: UUID) { + Log.d("PlayerActivity", "Sending start $itemId") + withContext(Dispatchers.IO) { + jellyfinApi.playstateApi.onPlaybackStart(jellyfinApi.userId!!, itemId) + } + } + + override suspend fun postPlaybackStop(itemId: UUID, positionTicks: Long) { + Log.d("PlayerActivity", "Sending stop $itemId") + withContext(Dispatchers.IO) { + jellyfinApi.playstateApi.onPlaybackStopped(jellyfinApi.userId!!, itemId, positionTicks = positionTicks) + } + } + + override suspend fun postPlaybackProgress(itemId: UUID, positionTicks: Long, isPaused: Boolean) { + withContext(Dispatchers.IO) { + jellyfinApi.playstateApi.onPlaybackProgress(jellyfinApi.userId!!, itemId, positionTicks = positionTicks, isPaused = isPaused) + } + } } \ No newline at end of file diff --git a/app/src/main/java/dev/jdtech/jellyfin/viewmodels/PlayerActivityViewModel.kt b/app/src/main/java/dev/jdtech/jellyfin/viewmodels/PlayerActivityViewModel.kt index d33400fb..f002954f 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/viewmodels/PlayerActivityViewModel.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/viewmodels/PlayerActivityViewModel.kt @@ -1,6 +1,8 @@ package dev.jdtech.jellyfin.viewmodels import android.app.Application +import android.os.Handler +import android.os.Looper import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData @@ -11,6 +13,7 @@ import com.google.android.exoplayer2.trackselection.DefaultTrackSelector import dagger.hilt.android.lifecycle.HiltViewModel import dev.jdtech.jellyfin.repository.JellyfinRepository import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import java.util.* import javax.inject.Inject @@ -30,6 +33,8 @@ constructor( private var playbackPosition: Long = 0 private var _playbackStateListener: PlaybackStateListener + private var itemId: UUID? = null + val playbackStateListener: PlaybackStateListener get() = _playbackStateListener @@ -38,6 +43,8 @@ constructor( } fun initializePlayer(itemId: UUID, mediaSourceId: String, playbackPosition: Long) { + this.itemId = itemId + val renderersFactory = DefaultRenderersFactory(application).setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON) val trackSelector = DefaultTrackSelector(application) @@ -62,10 +69,22 @@ constructor( player.playWhenReady = playWhenReady player.prepare() _player.value = player + + jellyfinRepository.postPlaybackStart(itemId) } + + pollPosition(player, itemId) } private fun releasePlayer() { + itemId?.let { itemId -> + _player.value?.let { player -> + runBlocking { + jellyfinRepository.postPlaybackStop(itemId, player.currentPosition.times(10000)) + } + } + } + if (player.value != null) { playWhenReady = player.value!!.playWhenReady playbackPosition = player.value!!.currentPosition @@ -76,6 +95,27 @@ constructor( } } + private fun pollPosition(player: SimpleExoPlayer, itemId: UUID) { + val handler = Handler(Looper.getMainLooper()) + val runnable: Runnable = object : Runnable { + override fun run() { + viewModelScope.launch { + Log.d( + "PlayerActivity", + "Posting progress of $itemId, position: ${player.currentPosition}" + ) + jellyfinRepository.postPlaybackProgress( + itemId, + player.currentPosition.times(10000), + !player.isPlaying + ) + } + handler.postDelayed(this, 2000) + } + } + handler.post(runnable) + } + class PlaybackStateListener : Player.Listener { private val _navigateBack = MutableLiveData() val navigateBack: LiveData = _navigateBack