forked from recloudstream/cloudstream
small fixes
This commit is contained in:
parent
415c173524
commit
e24dc692d0
5 changed files with 78 additions and 41 deletions
|
@ -104,7 +104,7 @@ abstract class AbstractPlayerFragment(
|
||||||
throw NotImplementedError()
|
throw NotImplementedError()
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun onTimestamp(timestamp: EpisodeSkip.SkipStamp) {
|
open fun onTimestamp(timestamp: EpisodeSkip.SkipStamp?) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -117,7 +117,7 @@ class CS3IPlayer : IPlayer {
|
||||||
private var playerUpdated: ((Any?) -> Unit)? = null
|
private var playerUpdated: ((Any?) -> Unit)? = null
|
||||||
private var embeddedSubtitlesFetched: ((List<SubtitleData>) -> Unit)? = null
|
private var embeddedSubtitlesFetched: ((List<SubtitleData>) -> Unit)? = null
|
||||||
private var onTracksInfoChanged: (() -> Unit)? = null
|
private var onTracksInfoChanged: (() -> Unit)? = null
|
||||||
private var onTimestampInvoked: ((EpisodeSkip.SkipStamp) -> Unit)? = null
|
private var onTimestampInvoked: ((EpisodeSkip.SkipStamp?) -> Unit)? = null
|
||||||
private var onTimestampSkipped: ((EpisodeSkip.SkipStamp) -> Unit)? = null
|
private var onTimestampSkipped: ((EpisodeSkip.SkipStamp) -> Unit)? = null
|
||||||
|
|
||||||
override fun releaseCallbacks() {
|
override fun releaseCallbacks() {
|
||||||
|
@ -150,7 +150,7 @@ class CS3IPlayer : IPlayer {
|
||||||
subtitlesUpdates: (() -> Unit)?,
|
subtitlesUpdates: (() -> Unit)?,
|
||||||
embeddedSubtitlesFetched: ((List<SubtitleData>) -> Unit)?,
|
embeddedSubtitlesFetched: ((List<SubtitleData>) -> Unit)?,
|
||||||
onTracksInfoChanged: (() -> Unit)?,
|
onTracksInfoChanged: (() -> Unit)?,
|
||||||
onTimestampInvoked: ((EpisodeSkip.SkipStamp) -> Unit)?,
|
onTimestampInvoked: ((EpisodeSkip.SkipStamp?) -> Unit)?,
|
||||||
onTimestampSkipped: ((EpisodeSkip.SkipStamp) -> Unit)?,
|
onTimestampSkipped: ((EpisodeSkip.SkipStamp) -> Unit)?,
|
||||||
) {
|
) {
|
||||||
this.playerUpdated = playerUpdated
|
this.playerUpdated = playerUpdated
|
||||||
|
@ -731,7 +731,7 @@ class CS3IPlayer : IPlayer {
|
||||||
source
|
source
|
||||||
}
|
}
|
||||||
|
|
||||||
println("PLAYBACK POS $playbackPosition")
|
//println("PLAYBACK POS $playbackPosition")
|
||||||
return exoPlayerBuilder.build().apply {
|
return exoPlayerBuilder.build().apply {
|
||||||
setPlayWhenReady(playWhenReady)
|
setPlayWhenReady(playWhenReady)
|
||||||
seekTo(currentWindow, playbackPosition)
|
seekTo(currentWindow, playbackPosition)
|
||||||
|
@ -747,8 +747,22 @@ class CS3IPlayer : IPlayer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updatedTime() {
|
private fun getCurrentTimestamp(writePosition : Long? = null): EpisodeSkip.SkipStamp? {
|
||||||
val position = exoPlayer?.currentPosition
|
val position = writePosition ?: this@CS3IPlayer.getPosition() ?: return null
|
||||||
|
for (lastTimeStamp in lastTimeStamps) {
|
||||||
|
if (lastTimeStamp.startMs <= position && position < lastTimeStamp.endMs) {
|
||||||
|
return lastTimeStamp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updatedTime(writePosition : Long? = null) {
|
||||||
|
getCurrentTimestamp(writePosition)?.let { timestamp ->
|
||||||
|
onTimestampInvoked?.invoke(timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
val position = writePosition ?: exoPlayer?.currentPosition
|
||||||
val duration = exoPlayer?.contentDuration
|
val duration = exoPlayer?.contentDuration
|
||||||
if (duration != null && position != null) {
|
if (duration != null && position != null) {
|
||||||
playerPositionChanged?.invoke(Pair(position, duration))
|
playerPositionChanged?.invoke(Pair(position, duration))
|
||||||
|
@ -760,12 +774,12 @@ class CS3IPlayer : IPlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun seekTo(time: Long) {
|
override fun seekTo(time: Long) {
|
||||||
updatedTime()
|
updatedTime(time)
|
||||||
exoPlayer?.seekTo(time)
|
exoPlayer?.seekTo(time)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ExoPlayer.seekTime(time: Long) {
|
private fun ExoPlayer.seekTime(time: Long) {
|
||||||
updatedTime()
|
updatedTime(currentPosition + time)
|
||||||
seekTo(currentPosition + time)
|
seekTo(currentPosition + time)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -803,18 +817,13 @@ class CS3IPlayer : IPlayer {
|
||||||
CSPlayerEvent.PrevEpisode -> prevEpisode?.invoke()
|
CSPlayerEvent.PrevEpisode -> prevEpisode?.invoke()
|
||||||
CSPlayerEvent.SkipCurrentChapter -> {
|
CSPlayerEvent.SkipCurrentChapter -> {
|
||||||
//val dur = this@CS3IPlayer.getDuration() ?: return@apply
|
//val dur = this@CS3IPlayer.getDuration() ?: return@apply
|
||||||
val pos = this@CS3IPlayer.getPosition() ?: return@apply
|
getCurrentTimestamp()?.let { lastTimeStamp ->
|
||||||
for (lastTimeStamp in lastTimeStamps) {
|
|
||||||
if (lastTimeStamp.startMs <= pos && pos < lastTimeStamp.endMs) {
|
|
||||||
if (lastTimeStamp.skipToNextEpisode) {
|
if (lastTimeStamp.skipToNextEpisode) {
|
||||||
handleEvent(CSPlayerEvent.NextEpisode)
|
handleEvent(CSPlayerEvent.NextEpisode)
|
||||||
} else {
|
} else {
|
||||||
seekTo(lastTimeStamp.endMs)
|
seekTo(lastTimeStamp.endMs + 1L)
|
||||||
}
|
}
|
||||||
|
|
||||||
onTimestampSkipped?.invoke(lastTimeStamp)
|
onTimestampSkipped?.invoke(lastTimeStamp)
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1039,16 +1048,18 @@ class CS3IPlayer : IPlayer {
|
||||||
override fun addTimeStamps(timeStamps: List<EpisodeSkip.SkipStamp>) {
|
override fun addTimeStamps(timeStamps: List<EpisodeSkip.SkipStamp>) {
|
||||||
lastTimeStamps = timeStamps
|
lastTimeStamps = timeStamps
|
||||||
timeStamps.forEach { timestamp ->
|
timeStamps.forEach { timestamp ->
|
||||||
exoPlayer?.createMessage { _, payload ->
|
exoPlayer?.createMessage { _, _ ->
|
||||||
if (payload is EpisodeSkip.SkipStamp) // this should always be true
|
updatedTime()
|
||||||
onTimestampInvoked?.invoke(payload)
|
//if (payload is EpisodeSkip.SkipStamp) // this should always be true
|
||||||
|
// onTimestampInvoked?.invoke(payload)
|
||||||
}
|
}
|
||||||
?.setLooper(Looper.getMainLooper())
|
?.setLooper(Looper.getMainLooper())
|
||||||
?.setPosition(timestamp.startMs)
|
?.setPosition(timestamp.startMs)
|
||||||
?.setPayload(timestamp)
|
//?.setPayload(timestamp)
|
||||||
?.setDeleteAfterDelivery(false)
|
?.setDeleteAfterDelivery(false)
|
||||||
?.send()
|
?.send()
|
||||||
}
|
}
|
||||||
|
updatedTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onRenderFirst() {
|
fun onRenderFirst() {
|
||||||
|
|
|
@ -869,7 +869,7 @@ class GeneratorPlayer : FullScreenPlayer() {
|
||||||
if ((currentMeta as? ResultEpisode)?.tvType == TvType.NSFW) return
|
if ((currentMeta as? ResultEpisode)?.tvType == TvType.NSFW) return
|
||||||
|
|
||||||
val (position, duration) = posDur
|
val (position, duration) = posDur
|
||||||
if (duration == 0L) return // idk how you achieved this, but div by zero crash
|
if (duration <= 0L) return // idk how you achieved this, but div by zero crash
|
||||||
if (!hasRequestedStamps) {
|
if (!hasRequestedStamps) {
|
||||||
hasRequestedStamps = true
|
hasRequestedStamps = true
|
||||||
viewModel.loadStamps(duration)
|
viewModel.loadStamps(duration)
|
||||||
|
@ -1151,8 +1151,13 @@ class GeneratorPlayer : FullScreenPlayer() {
|
||||||
|
|
||||||
var timestampShowState = false
|
var timestampShowState = false
|
||||||
|
|
||||||
|
var skipAnimator: ValueAnimator? = null
|
||||||
|
var skipIndex = 0
|
||||||
|
|
||||||
private fun displayTimeStamp(show: Boolean) {
|
private fun displayTimeStamp(show: Boolean) {
|
||||||
if (timestampShowState == show) return
|
if (timestampShowState == show) return
|
||||||
|
skipIndex++
|
||||||
|
println("displayTimeStamp = $show")
|
||||||
timestampShowState = show
|
timestampShowState = show
|
||||||
skip_chapter_button?.apply {
|
skip_chapter_button?.apply {
|
||||||
val showWidth = 170.toPx
|
val showWidth = 170.toPx
|
||||||
|
@ -1163,14 +1168,14 @@ class GeneratorPlayer : FullScreenPlayer() {
|
||||||
val to = if (show) showWidth else noShowWidth
|
val to = if (show) showWidth else noShowWidth
|
||||||
val from = if (!show) showWidth else noShowWidth
|
val from = if (!show) showWidth else noShowWidth
|
||||||
|
|
||||||
|
skipAnimator?.cancel()
|
||||||
isVisible = true
|
isVisible = true
|
||||||
|
|
||||||
// just in case
|
// just in case
|
||||||
val lay = layoutParams
|
val lay = layoutParams
|
||||||
lay.width = from
|
lay.width = from
|
||||||
layoutParams = lay
|
layoutParams = lay
|
||||||
|
skipAnimator = ValueAnimator.ofInt(
|
||||||
ValueAnimator.ofInt(
|
|
||||||
from, to
|
from, to
|
||||||
).apply {
|
).apply {
|
||||||
addListener(onEnd = {
|
addListener(onEnd = {
|
||||||
|
@ -1192,12 +1197,18 @@ class GeneratorPlayer : FullScreenPlayer() {
|
||||||
displayTimeStamp(false)
|
displayTimeStamp(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTimestamp(timestamp: EpisodeSkip.SkipStamp) {
|
override fun onTimestamp(timestamp: EpisodeSkip.SkipStamp?) {
|
||||||
|
if (timestamp != null) {
|
||||||
skip_chapter_button.setText(timestamp.uiText)
|
skip_chapter_button.setText(timestamp.uiText)
|
||||||
displayTimeStamp(true)
|
displayTimeStamp(true)
|
||||||
|
val currentIndex = skipIndex
|
||||||
skip_chapter_button?.handler?.postDelayed({
|
skip_chapter_button?.handler?.postDelayed({
|
||||||
|
if (skipIndex == currentIndex)
|
||||||
displayTimeStamp(false)
|
displayTimeStamp(false)
|
||||||
}, 6000)
|
}, 6000)
|
||||||
|
} else {
|
||||||
|
displayTimeStamp(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
|
|
@ -126,7 +126,7 @@ interface IPlayer {
|
||||||
subtitlesUpdates: (() -> Unit)? = null, // callback from player to inform that subtitles have updated in some way
|
subtitlesUpdates: (() -> Unit)? = null, // callback from player to inform that subtitles have updated in some way
|
||||||
embeddedSubtitlesFetched: ((List<SubtitleData>) -> Unit)? = null, // callback from player to give all embedded subtitles
|
embeddedSubtitlesFetched: ((List<SubtitleData>) -> Unit)? = null, // callback from player to give all embedded subtitles
|
||||||
onTracksInfoChanged: (() -> Unit)? = null, // Callback when tracks are changed, used for UI changes
|
onTracksInfoChanged: (() -> Unit)? = null, // Callback when tracks are changed, used for UI changes
|
||||||
onTimestampInvoked: ((EpisodeSkip.SkipStamp) -> Unit)? = null, // Callback when timestamps appear
|
onTimestampInvoked: ((EpisodeSkip.SkipStamp?) -> Unit)? = null, // Callback when timestamps appear, null when it should disappear
|
||||||
onTimestampSkipped: ((EpisodeSkip.SkipStamp) -> Unit)? = null, // callback for when a chapter is skipped, aka when event is handled (or for future use when skip automatically ads/sponsor)
|
onTimestampSkipped: ((EpisodeSkip.SkipStamp) -> Unit)? = null, // callback for when a chapter is skipped, aka when event is handled (or for future use when skip automatically ads/sponsor)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.getMalId
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.ui.result.ResultEpisode
|
import com.lagradost.cloudstream3.ui.result.ResultEpisode
|
||||||
import com.lagradost.cloudstream3.ui.result.txt
|
import com.lagradost.cloudstream3.ui.result.txt
|
||||||
|
import java.lang.Long.min
|
||||||
|
|
||||||
object EpisodeSkip {
|
object EpisodeSkip {
|
||||||
private const val TAG = "EpisodeSkip"
|
private const val TAG = "EpisodeSkip"
|
||||||
|
@ -37,7 +38,7 @@ object EpisodeSkip {
|
||||||
private val cachedStamps = HashMap<Int, List<SkipStamp>>()
|
private val cachedStamps = HashMap<Int, List<SkipStamp>>()
|
||||||
|
|
||||||
private fun shouldSkipToNextEpisode(endMs: Long, episodeDurationMs: Long): Boolean {
|
private fun shouldSkipToNextEpisode(endMs: Long, episodeDurationMs: Long): Boolean {
|
||||||
return episodeDurationMs - endMs < 12_000L
|
return episodeDurationMs - endMs < 20_000L // some might have outro that we don't care about tbh
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getStamps(
|
suspend fun getStamps(
|
||||||
|
@ -55,7 +56,14 @@ object EpisodeSkip {
|
||||||
|
|
||||||
if (data is AnimeLoadResponse && (data.type == TvType.Anime || data.type == TvType.OVA)) {
|
if (data is AnimeLoadResponse && (data.type == TvType.Anime || data.type == TvType.OVA)) {
|
||||||
data.getMalId()?.toIntOrNull()?.let { malId ->
|
data.getMalId()?.toIntOrNull()?.let { malId ->
|
||||||
AniSkip.getResult(malId, episode.episode, episodeDurationMs)?.mapNotNull { stamp ->
|
val (resultLength, stamps) = AniSkip.getResult(
|
||||||
|
malId,
|
||||||
|
episode.episode,
|
||||||
|
episodeDurationMs
|
||||||
|
) ?: return@let null
|
||||||
|
// because it also returns an expected episode length we use that just in case it is mismatched with like 2s next episode will still work
|
||||||
|
val dur = min(episodeDurationMs, resultLength)
|
||||||
|
stamps.mapNotNull { stamp ->
|
||||||
val skipType = when (stamp.skipType) {
|
val skipType = when (stamp.skipType) {
|
||||||
"op" -> SkipType.Opening
|
"op" -> SkipType.Opening
|
||||||
"ed" -> SkipType.Ending
|
"ed" -> SkipType.Ending
|
||||||
|
@ -68,7 +76,10 @@ object EpisodeSkip {
|
||||||
val start = (stamp.interval.startTime * 1000.0).toLong()
|
val start = (stamp.interval.startTime * 1000.0).toLong()
|
||||||
SkipStamp(
|
SkipStamp(
|
||||||
type = skipType,
|
type = skipType,
|
||||||
skipToNextEpisode = hasNextEpisode && shouldSkipToNextEpisode(end, episodeDurationMs),
|
skipToNextEpisode = hasNextEpisode && shouldSkipToNextEpisode(
|
||||||
|
end,
|
||||||
|
dur
|
||||||
|
),
|
||||||
startMs = start,
|
startMs = start,
|
||||||
endMs = end
|
endMs = end
|
||||||
)
|
)
|
||||||
|
@ -87,7 +98,11 @@ object EpisodeSkip {
|
||||||
// the following is GPLv3 code https://github.com/saikou-app/saikou/blob/main/LICENSE.md
|
// the following is GPLv3 code https://github.com/saikou-app/saikou/blob/main/LICENSE.md
|
||||||
object AniSkip {
|
object AniSkip {
|
||||||
private const val TAG = "AniSkip"
|
private const val TAG = "AniSkip"
|
||||||
suspend fun getResult(malId: Int, episodeNumber: Int, episodeLength: Long): List<Stamp>? {
|
suspend fun getResult(
|
||||||
|
malId: Int,
|
||||||
|
episodeNumber: Int,
|
||||||
|
episodeLength: Long
|
||||||
|
): Pair<Long, List<Stamp>>? {
|
||||||
return try {
|
return try {
|
||||||
val url =
|
val url =
|
||||||
"https://api.aniskip.com/v2/skip-times/$malId/$episodeNumber?types[]=ed&types[]=mixed-ed&types[]=mixed-op&types[]=op&types[]=recap&episodeLength=${episodeLength / 1000L}"
|
"https://api.aniskip.com/v2/skip-times/$malId/$episodeNumber?types[]=ed&types[]=mixed-ed&types[]=mixed-op&types[]=op&types[]=recap&episodeLength=${episodeLength / 1000L}"
|
||||||
|
@ -96,7 +111,7 @@ object AniSkip {
|
||||||
val a = app.get(url)
|
val a = app.get(url)
|
||||||
val res = a.parsed<AniSkipResponse>()
|
val res = a.parsed<AniSkipResponse>()
|
||||||
Log.i(TAG, "Found ${res.found} with ${res.results?.size} results")
|
Log.i(TAG, "Found ${res.found} with ${res.results?.size} results")
|
||||||
if (res.found) res.results else null
|
if (res.found && !res.results.isNullOrEmpty()) (res.results[0].episodeLength * 1000).toLong() to res.results else null
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
Log.i(TAG, "error = ${t.message}")
|
Log.i(TAG, "error = ${t.message}")
|
||||||
logError(t)
|
logError(t)
|
||||||
|
|
Loading…
Reference in a new issue