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