From f65497e06a68d06244e8128f380e016f4542b7f4 Mon Sep 17 00:00:00 2001 From: Jannik Date: Sat, 29 Jan 2022 19:56:39 +0100 Subject: [PATCH] Player: load media async and use playhead for initial episode --- .../ui/activity/player/PlayerActivity.kt | 23 ++++++----- .../ui/activity/player/PlayerViewModel.kt | 40 ++++++++++--------- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerActivity.kt b/app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerActivity.kt index 677e056..a48969b 100644 --- a/app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerActivity.kt +++ b/app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerActivity.kt @@ -80,7 +80,7 @@ class PlayerActivity : AppCompatActivity() { setContentView(R.layout.activity_player) hideBars() // Initial hide the bars - model.loadMedia( + model.loadMediaAsync( intent.getStringExtra(getString(R.string.intent_season_id)) ?: "", intent.getStringExtra(getString(R.string.intent_episode_id)) ?: "" ) @@ -143,7 +143,7 @@ class PlayerActivity : AppCompatActivity() { // when the intent changed, load the new media and play it intent?.let { - model.loadMedia( + model.loadMediaAsync( it.getStringExtra(getString(R.string.intent_season_id)) ?: "", it.getStringExtra(getString(R.string.intent_episode_id)) ?: "" ) @@ -194,11 +194,6 @@ class PlayerActivity : AppCompatActivity() { } private fun initPlayer() { - if (model.currentEpisode == NoneEpisode) { - Log.e(javaClass.name, "No media was set.") - this.finish() - } - initVideoView() initTimeUpdates() @@ -234,9 +229,10 @@ class PlayerActivity : AppCompatActivity() { } } }) - + + // revert back to the old behaviour (blocking init) in case there are any issues with async init // start playing the current episode, after all needed player components have been initialized - model.playCurrentMedia() + //model.playCurrentMedia(model.currentPlayhead) } @SuppressLint("ClickableViewAccessibility") @@ -353,9 +349,16 @@ class PlayerActivity : AppCompatActivity() { } /** - * update title text and next ep button visibility, set ignoreNextStateEnded + * This methode is called, if the current episode has changed. + * Update title text and next ep button visibility. + * If the currentEpisode changed to NoneEpisode, exit the activity. */ private fun onMediaChanged() { + if (model.currentEpisode == NoneEpisode) { + Log.e(javaClass.name, "No media was set.") + this.finish() + } + exo_text_title.text = model.getMediaTitle() // hide the next episode button, if there is none diff --git a/app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerViewModel.kt b/app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerViewModel.kt index 0ea2834..97f8d93 100644 --- a/app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerViewModel.kt +++ b/app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerViewModel.kt @@ -36,6 +36,8 @@ import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector import com.google.android.exoplayer2.source.hls.HlsMediaSource import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory import com.google.android.exoplayer2.util.Util +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.joinAll import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import org.mosad.teapod.R @@ -49,7 +51,6 @@ import org.mosad.teapod.util.Meta import org.mosad.teapod.util.TVShowMeta import org.mosad.teapod.util.tmdb.TMDBTVSeason import java.util.* -import kotlin.collections.ArrayList /** * PlayerViewModel handles all stuff related to media/episodes. @@ -64,6 +65,7 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application) val currentEpisodeChangedListener = ArrayList<() -> Unit>() private val preferredLanguage = if (Preferences.preferSecondary) Locale.JAPANESE else Locale.GERMAN + private var currentPlayhead: Long = 0 // tmdb/meta data TODO currently not implemented for cr var mediaMeta: Meta? = null @@ -124,19 +126,11 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application) mediaSession.isActive = true } - fun loadMedia(seasonId: String, episodeId: String) { - runBlocking { - episodes = Crunchyroll.episodes(seasonId) - //mediaMeta = loadMediaMeta(media.aodId) // can be done blocking, since it should be cached + fun loadMediaAsync(seasonId: String, episodeId: String) = viewModelScope.launch { + episodes = Crunchyroll.episodes(seasonId) - // TODO replace this with setCurrentEpisode - currentEpisode = episodes.items.find { episode -> - episode.id == episodeId - } ?: NoneEpisode - println("loading playback ${currentEpisode.playback}") - - currentPlayback = Crunchyroll.playback(currentEpisode.playback) - } + setCurrentEpisode(episodeId) + playCurrentMedia(currentPlayhead) // TODO reimplement for cr // run async as it should be loaded by the time the episodes a @@ -183,10 +177,23 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application) episode.id == episodeId } ?: NoneEpisode - // TODO don't run blocking + // update player gui (title, next ep button) after currentEpisode has changed + currentEpisodeChangedListener.forEach { it() } + + // needs to be blocking, currentPlayback must be present when calling playCurrentMedia() runBlocking { - currentPlayback = Crunchyroll.playback(currentEpisode.playback) + joinAll( + viewModelScope.launch(Dispatchers.IO) { + currentPlayback = Crunchyroll.playback(currentEpisode.playback) + }, + viewModelScope.launch(Dispatchers.IO) { + Crunchyroll.playheads(listOf(currentEpisode.id))[currentEpisode.id]?.let { + currentPlayhead = (it.playhead.times(1000)).toLong() + } + } + ) } + println("loaded playback ${currentEpisode.playback}") // TODO update metadata and language (it should not be needed to update the language here!) @@ -201,9 +208,6 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application) * @param seekPosition The seek position for the episode (default = 0). */ fun playCurrentMedia(seekPosition: Long = 0) { - // update player gui (title, next ep button) after nextEpisodeId has been set - currentEpisodeChangedListener.forEach { it() } - // get preferred stream url, set current language if it differs from the preferred one val preferredLocale = currentLanguage val fallbackLocal = Locale.US