added skip opening for tv shows
* available for tv shows, where metaDB has the needed information
This commit is contained in:
parent
be6e9979a9
commit
876ed97d6d
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue