Player: load media async and use playhead for initial episode
This commit is contained in:
		@ -80,7 +80,7 @@ class PlayerActivity : AppCompatActivity() {
 | 
				
			|||||||
        setContentView(R.layout.activity_player)
 | 
					        setContentView(R.layout.activity_player)
 | 
				
			||||||
        hideBars() // Initial hide the bars
 | 
					        hideBars() // Initial hide the bars
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        model.loadMedia(
 | 
					        model.loadMediaAsync(
 | 
				
			||||||
            intent.getStringExtra(getString(R.string.intent_season_id)) ?: "",
 | 
					            intent.getStringExtra(getString(R.string.intent_season_id)) ?: "",
 | 
				
			||||||
            intent.getStringExtra(getString(R.string.intent_episode_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
 | 
					        // when the intent changed, load the new media and play it
 | 
				
			||||||
        intent?.let {
 | 
					        intent?.let {
 | 
				
			||||||
            model.loadMedia(
 | 
					            model.loadMediaAsync(
 | 
				
			||||||
                it.getStringExtra(getString(R.string.intent_season_id)) ?: "",
 | 
					                it.getStringExtra(getString(R.string.intent_season_id)) ?: "",
 | 
				
			||||||
                it.getStringExtra(getString(R.string.intent_episode_id)) ?: ""
 | 
					                it.getStringExtra(getString(R.string.intent_episode_id)) ?: ""
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
@ -194,11 +194,6 @@ class PlayerActivity : AppCompatActivity() {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private fun initPlayer() {
 | 
					    private fun initPlayer() {
 | 
				
			||||||
        if (model.currentEpisode == NoneEpisode) {
 | 
					 | 
				
			||||||
            Log.e(javaClass.name, "No media was set.")
 | 
					 | 
				
			||||||
            this.finish()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        initVideoView()
 | 
					        initVideoView()
 | 
				
			||||||
        initTimeUpdates()
 | 
					        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
 | 
					        // start playing the current episode, after all needed player components have been initialized
 | 
				
			||||||
        model.playCurrentMedia()
 | 
					        //model.playCurrentMedia(model.currentPlayhead)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @SuppressLint("ClickableViewAccessibility")
 | 
					    @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() {
 | 
					    private fun onMediaChanged() {
 | 
				
			||||||
 | 
					        if (model.currentEpisode == NoneEpisode) {
 | 
				
			||||||
 | 
					            Log.e(javaClass.name, "No media was set.")
 | 
				
			||||||
 | 
					            this.finish()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        exo_text_title.text = model.getMediaTitle()
 | 
					        exo_text_title.text = model.getMediaTitle()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // hide the next episode button, if there is none
 | 
					        // hide the next episode button, if there is none
 | 
				
			||||||
 | 
				
			|||||||
@ -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.source.hls.HlsMediaSource
 | 
				
			||||||
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
 | 
					import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
 | 
				
			||||||
import com.google.android.exoplayer2.util.Util
 | 
					import com.google.android.exoplayer2.util.Util
 | 
				
			||||||
 | 
					import kotlinx.coroutines.Dispatchers
 | 
				
			||||||
 | 
					import kotlinx.coroutines.joinAll
 | 
				
			||||||
import kotlinx.coroutines.launch
 | 
					import kotlinx.coroutines.launch
 | 
				
			||||||
import kotlinx.coroutines.runBlocking
 | 
					import kotlinx.coroutines.runBlocking
 | 
				
			||||||
import org.mosad.teapod.R
 | 
					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.TVShowMeta
 | 
				
			||||||
import org.mosad.teapod.util.tmdb.TMDBTVSeason
 | 
					import org.mosad.teapod.util.tmdb.TMDBTVSeason
 | 
				
			||||||
import java.util.*
 | 
					import java.util.*
 | 
				
			||||||
import kotlin.collections.ArrayList
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * PlayerViewModel handles all stuff related to media/episodes.
 | 
					 * PlayerViewModel handles all stuff related to media/episodes.
 | 
				
			||||||
@ -64,6 +65,7 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    val currentEpisodeChangedListener = ArrayList<() -> Unit>()
 | 
					    val currentEpisodeChangedListener = ArrayList<() -> Unit>()
 | 
				
			||||||
    private val preferredLanguage = if (Preferences.preferSecondary) Locale.JAPANESE else Locale.GERMAN
 | 
					    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
 | 
					    // tmdb/meta data TODO currently not implemented for cr
 | 
				
			||||||
    var mediaMeta: Meta? = null
 | 
					    var mediaMeta: Meta? = null
 | 
				
			||||||
@ -124,19 +126,11 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
 | 
				
			|||||||
        mediaSession.isActive = true
 | 
					        mediaSession.isActive = true
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun loadMedia(seasonId: String, episodeId: String) {
 | 
					    fun loadMediaAsync(seasonId: String, episodeId: String) = viewModelScope.launch {
 | 
				
			||||||
        runBlocking {
 | 
					        episodes = Crunchyroll.episodes(seasonId)
 | 
				
			||||||
            episodes = Crunchyroll.episodes(seasonId)
 | 
					 | 
				
			||||||
            //mediaMeta = loadMediaMeta(media.aodId) // can be done blocking, since it should be cached
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // TODO replace this with setCurrentEpisode
 | 
					        setCurrentEpisode(episodeId)
 | 
				
			||||||
            currentEpisode = episodes.items.find { episode ->
 | 
					        playCurrentMedia(currentPlayhead)
 | 
				
			||||||
                episode.id == episodeId
 | 
					 | 
				
			||||||
            } ?: NoneEpisode
 | 
					 | 
				
			||||||
            println("loading playback ${currentEpisode.playback}")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            currentPlayback = Crunchyroll.playback(currentEpisode.playback)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // TODO reimplement for cr
 | 
					        // TODO reimplement for cr
 | 
				
			||||||
        // run async as it should be loaded by the time the episodes a
 | 
					        // 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
 | 
					            episode.id == episodeId
 | 
				
			||||||
        } ?: NoneEpisode
 | 
					        } ?: 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 {
 | 
					        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!)
 | 
					        // 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).
 | 
					     * @param seekPosition The seek position for the episode (default = 0).
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    fun playCurrentMedia(seekPosition: Long = 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
 | 
					        // get preferred stream url, set current language if it differs from the preferred one
 | 
				
			||||||
        val preferredLocale = currentLanguage
 | 
					        val preferredLocale = currentLanguage
 | 
				
			||||||
        val fallbackLocal = Locale.US
 | 
					        val fallbackLocal = Locale.US
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user