From 876ed97d6d454bd8ce3f52861256fa1c29e96dbc Mon Sep 17 00:00:00 2001 From: Jannik Date: Sat, 17 Jul 2021 19:40:16 +0200 Subject: [PATCH] added skip opening for tv shows * available for tv shows, where metaDB has the needed information --- .../ui/activity/player/PlayerActivity.kt | 68 ++++++++++++++++--- .../ui/activity/player/PlayerViewModel.kt | 18 ++++- .../org/mosad/teapod/util/MetaDBController.kt | 10 ++- app/src/main/res/layout/activity_player.xml | 16 +++++ app/src/main/res/values-de-rDE/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 6 files changed, 97 insertions(+), 17 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 66728f4..24682f5 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 @@ -32,10 +32,7 @@ import org.mosad.teapod.R import org.mosad.teapod.preferences.Preferences import org.mosad.teapod.ui.components.EpisodesListPlayer import org.mosad.teapod.ui.components.LanguageSettingsPlayer -import org.mosad.teapod.util.DataTypes -import org.mosad.teapod.util.hideBars -import org.mosad.teapod.util.isInPiPMode -import org.mosad.teapod.util.navToLauncherTask +import org.mosad.teapod.util.* import java.util.* import java.util.concurrent.TimeUnit import kotlin.concurrent.scheduleAtFixedRate @@ -226,7 +223,10 @@ class PlayerActivity : AppCompatActivity() { // when the player controls get hidden, hide the bars too video_view.setControllerVisibilityListener { when (it) { - View.GONE -> hideBars() + View.GONE -> { + hideBars() + // TODO also hide the skip op button + } View.VISIBLE -> updateControls() } } @@ -244,6 +244,7 @@ class PlayerActivity : AppCompatActivity() { rwd_10.setOnButtonClickListener { rewind() } ffwd_10.setOnButtonClickListener { fastForward() } button_next_ep.setOnClickListener { playNextEpisode() } + button_skip_op.setOnClickListener { skipOpening() } button_language.setOnClickListener { showLanguageSettings() } button_episodes.setOnClickListener { showEpisodesList() } button_next_ep_c.setOnClickListener { playNextEpisode() } @@ -262,16 +263,20 @@ class PlayerActivity : AppCompatActivity() { timerUpdates = Timer().scheduleAtFixedRate(0, 500) { lifecycleScope.launch { + val currentPosition = model.player.currentPosition val btnNextEpIsVisible = button_next_ep.isVisible val controlsVisible = controller.isVisible + // make sure remaining time is > 0 if (model.player.duration > 0) { - remainingTime = model.player.duration - model.player.currentPosition + remainingTime = model.player.duration - currentPosition remainingTime = if (remainingTime < 0) 0 else remainingTime } + // TODO add metaDB ending_start support + // if remaining time < 20 sec, a next ep is set, autoplay is enabled and not in pip: + // show next ep button if (remainingTime in 1..20000) { - // if the next ep button is not visible, make it visible. Don't show in pip mode if (!btnNextEpIsVisible && model.nextEpisode != null && Preferences.autoplay && !isInPiPMode()) { showButtonNextEp() } @@ -279,6 +284,19 @@ class PlayerActivity : AppCompatActivity() { hideButtonNextEp() } + // if meta data is present and opening_start & opening_duration are valid, show skip opening + model.currentEpisodeMeta?.let { + if (it.openingDuration > 0 && + currentPosition in it.openingStart..(it.openingStart + 10000) && + !button_skip_op.isVisible + ) { + showButtonSkipOp() + } else if (button_skip_op.isVisible && currentPosition !in it.openingStart..(it.openingStart + 10000)) { + // the button should only be visible, if currentEpisodeMeta != null + hideButtonSkipOp() + } + } + // if controls are visible, update them if (controlsVisible) { updateControls() @@ -376,12 +394,21 @@ class PlayerActivity : AppCompatActivity() { hideButtonNextEp() } + private fun skipOpening() { + // calculate the seek time + model.currentEpisodeMeta?.let { + val seekTime = (it.openingStart + it.openingDuration) - model.player.currentPosition + model.seekToOffset(seekTime) + } + + } + /** * show the next episode button * TODO improve the show animation */ private fun showButtonNextEp() { - button_next_ep.visibility = View.VISIBLE + button_next_ep.isVisible = true button_next_ep.alpha = 0.0f button_next_ep.animate() @@ -399,7 +426,28 @@ class PlayerActivity : AppCompatActivity() { .setListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator?) { super.onAnimationEnd(animation) - button_next_ep.visibility = View.GONE + button_next_ep.isVisible = false + } + }) + + } + + private fun showButtonSkipOp() { + button_skip_op.isVisible = true + button_skip_op.alpha = 0.0f + + button_skip_op.animate() + .alpha(1.0f) + .setListener(null) + } + + private fun hideButtonSkipOp() { + button_skip_op.animate() + .alpha(0.0f) + .setListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator?) { + super.onAnimationEnd(animation) + button_skip_op.isVisible = false } }) @@ -437,7 +485,7 @@ class PlayerActivity : AppCompatActivity() { */ override fun onSingleTapConfirmed(e: MotionEvent?): Boolean { if (!isInPiPMode()) { - if (controller.isVisible) controller.hide() else controller.show() + if (controller.isVisible) controller.hide() else controller.show() } return true 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 c34d321..2894e21 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 @@ -27,6 +27,9 @@ import kotlin.collections.ArrayList * PlayerViewModel handles all stuff related to media/episodes. * When currentEpisode is changed the player will start playing it (not initial media), * the next episode will be update and the callback is handled. + * + * TODO rework don't use episodes for everything, use media instead + * this is a major rework of the AoDParser/Player/Media architecture */ class PlayerViewModel(application: Application) : AndroidViewModel(application) { @@ -45,6 +48,8 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application) internal set var mediaMeta: Meta? = null internal set + var currentEpisodeMeta: EpisodeMeta? = null + internal set var currentLanguage: Locale = Locale.ROOT internal set @@ -75,11 +80,12 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application) fun loadMedia(mediaId: Int, episodeId: Int) { runBlocking { media = AoDParser.getMediaById(mediaId) - mediaMeta = loadMediaMeta(media.id) + mediaMeta = loadMediaMeta(media.id) // can be done blocking, since it should be cached } currentEpisode = media.getEpisodeById(episodeId) nextEpisode = selectNextEpisode() + currentEpisodeMeta = getEpisodeMetaByAoDMediaId(currentEpisode.id) currentLanguage = currentEpisode.getPreferredStream(preferredLanguage).language } @@ -121,6 +127,7 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application) currentLanguage = preferredStream.language // update current language, since it may have changed currentEpisode = episode nextEpisode = selectNextEpisode() + currentEpisodeMeta = getEpisodeMetaByAoDMediaId(episode.id) currentEpisodeChangedListener.forEach { it() } // update player gui (title) val mediaSource = HlsMediaSource.Factory(dataSourceFactory).createMediaSource( @@ -160,6 +167,15 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application) } } + fun getEpisodeMetaByAoDMediaId(aodMediaId: Int): EpisodeMeta? { + val meta = mediaMeta + return if (meta is TVShowMeta) { + meta.episodes.firstOrNull { it.aodMediaId == aodMediaId } + } else { + null + } + } + private suspend fun loadMediaMeta(aodId: Int): Meta? { return if (media.type == DataTypes.MediaType.TVSHOW) { MetaDBController().getTVShowMetadata(aodId) diff --git a/app/src/main/java/org/mosad/teapod/util/MetaDBController.kt b/app/src/main/java/org/mosad/teapod/util/MetaDBController.kt index 9a13e43..387a129 100644 --- a/app/src/main/java/org/mosad/teapod/util/MetaDBController.kt +++ b/app/src/main/java/org/mosad/teapod/util/MetaDBController.kt @@ -42,8 +42,6 @@ class MetaDBController { val url = URL("$repoUrl/list.json") val json = url.readText() - Thread.sleep(5000) - mediaList = Gson().fromJson(json, MediaList::class.java) } } @@ -148,11 +146,11 @@ data class EpisodeMeta( @SerializedName("tmdb_number") val tmdbNumber: Int, @SerializedName("opening_start") - val openingStart: Int, + val openingStart: Long, @SerializedName("opening_duration") - val openingDuration: Int, + val openingDuration: Long, @SerializedName("ending_start") - val endingStart: Int, + val endingStart: Long, @SerializedName("ending_duration") - val endingDuration: Int + val endingDuration: Long ) diff --git a/app/src/main/res/layout/activity_player.xml b/app/src/main/res/layout/activity_player.xml index 5a8f6bd..566f92f 100644 --- a/app/src/main/res/layout/activity_player.xml +++ b/app/src/main/res/layout/activity_player.xml @@ -89,4 +89,20 @@ app:backgroundTint="@color/exo_white" app:iconGravity="textStart" /> + + \ No newline at end of file diff --git a/app/src/main/res/values-de-rDE/strings.xml b/app/src/main/res/values-de-rDE/strings.xml index af2731e..a957051 100644 --- a/app/src/main/res/values-de-rDE/strings.xml +++ b/app/src/main/res/values-de-rDE/strings.xml @@ -69,6 +69,7 @@ Abspielen/Pause 10 Sekunden vorwärts Nächste Folge + Intro überspringen Sprache Folgen Folge diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c7ba0b9..a58f3a2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -88,6 +88,7 @@ - 10 s + 10 s Next Episode + Skip Opening %1$02d:%2$02d %1$d:%2$02d:%3$02d Language