added skip opening for tv shows

* available for tv shows, where metaDB has the needed information
This commit is contained in:
Jannik 2021-07-17 19:40:16 +02:00
parent be6e9979a9
commit 876ed97d6d
Signed by: Seil0
GPG Key ID: E8459F3723C52C24
6 changed files with 97 additions and 17 deletions

View File

@ -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

View File

@ -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)

View File

@ -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
)

View File

@ -89,4 +89,20 @@
app:backgroundTint="@color/exo_white"
app:iconGravity="textStart" />
<com.google.android.material.button.MaterialButton
android:id="@+id/button_skip_op"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginEnd="12dp"
android:layout_marginBottom="70dp"
android:gravity="center"
android:text="@string/skip_opening"
android:textAllCaps="false"
android:textColor="@android:color/primary_text_light"
android:textSize="16sp"
android:visibility="gone"
app:backgroundTint="@color/exo_white"
app:iconGravity="textStart" />
</FrameLayout>

View File

@ -69,6 +69,7 @@
<string name="play_pause">Abspielen/Pause</string>
<string name="forward_10">10 Sekunden vorwärts</string>
<string name="next_episode">Nächste Folge</string>
<string name="skip_opening">Intro überspringen</string>
<string name="language">Sprache</string>
<string name="episodes">Folgen</string>
<string name="episode">Folge</string>

View File

@ -88,6 +88,7 @@
<string name="rwd_10_s" translatable="false">- 10 s</string>
<string name="fwd_10_s" translatable="false">+ 10 s</string>
<string name="next_episode">Next Episode</string>
<string name="skip_opening">Skip Opening</string>
<string name="time_min_sec" translatable="false">%1$02d:%2$02d</string>
<string name="time_hour_min_sec" translatable="false">%1$d:%2$02d:%3$02d</string>
<string name="language">Language</string>