From f77fe0a31e44a4a53beb28949ca76f108a2a6648 Mon Sep 17 00:00:00 2001 From: reduplicated <110570621+reduplicated@users.noreply.github.com> Date: Fri, 4 Nov 2022 18:55:03 +0100 Subject: [PATCH] working --- .../cloudstream3/network/RequestsHelper.kt | 24 +- .../ui/player/AbstractPlayerFragment.kt | 7 +- .../cloudstream3/ui/player/CS3IPlayer.kt | 20 +- .../cloudstream3/ui/player/GeneratorPlayer.kt | 205 ++++++++++-------- .../cloudstream3/ui/player/IPlayer.kt | 1 + .../ui/player/PlayerGeneratorViewModel.kt | 9 +- .../lagradost/cloudstream3/utils/AniSkip.kt | 76 ++++--- .../main/res/layout/player_custom_layout.xml | 6 +- app/src/main/res/values/colors.xml | 2 +- 9 files changed, 215 insertions(+), 135 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/network/RequestsHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/network/RequestsHelper.kt index 67ffa732..50389291 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/network/RequestsHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/network/RequestsHelper.kt @@ -1,15 +1,37 @@ package com.lagradost.cloudstream3.network +import android.annotation.SuppressLint import android.content.Context import androidx.preference.PreferenceManager import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.USER_AGENT import com.lagradost.nicehttp.Requests -import com.lagradost.nicehttp.ignoreAllSSLErrors import okhttp3.* import okhttp3.Headers.Companion.toHeaders import java.io.File +import java.security.SecureRandom +import java.security.cert.X509Certificate +import javax.net.ssl.SSLContext +import javax.net.ssl.TrustManager +import javax.net.ssl.X509TrustManager +fun OkHttpClient.Builder.ignoreAllSSLErrors(): OkHttpClient.Builder { + val naiveTrustManager = @SuppressLint("CustomX509TrustManager") + object : X509TrustManager { + override fun getAcceptedIssuers(): Array = arrayOf() + override fun checkClientTrusted(certs: Array, authType: String) = Unit + override fun checkServerTrusted(certs: Array, authType: String) = Unit + } + + val insecureSocketFactory = SSLContext.getInstance("SSL").apply { + val trustAllCerts = arrayOf(naiveTrustManager) + init(null, trustAllCerts, SecureRandom()) + }.socketFactory + + sslSocketFactory(insecureSocketFactory, naiveTrustManager) + hostnameVerifier { _, _ -> true } + return this +} fun Requests.initClient(context: Context): OkHttpClient { val settingsManager = PreferenceManager.getDefaultSharedPreferences(context) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt index d7076ae6..5037b37d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt @@ -108,6 +108,10 @@ abstract class AbstractPlayerFragment( } + open fun onTimestampSkipped(timestamp: EpisodeSkip.SkipStamp) { + + } + open fun exitedPipMode() { throw NotImplementedError() } @@ -379,7 +383,8 @@ abstract class AbstractPlayerFragment( subtitlesUpdates = ::subtitlesChanged, embeddedSubtitlesFetched = ::embeddedSubtitlesFetched, onTracksInfoChanged = ::onTracksInfoChanged, - onTimestampInvoked = ::onTimestamp + onTimestampInvoked = ::onTimestamp, + onTimestampSkipped = ::onTimestampSkipped ) if (player is CS3IPlayer) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt index 0f8a63e2..dac384b1 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt @@ -118,6 +118,7 @@ class CS3IPlayer : IPlayer { private var embeddedSubtitlesFetched: ((List) -> Unit)? = null private var onTracksInfoChanged: (() -> Unit)? = null private var onTimestampInvoked: ((EpisodeSkip.SkipStamp) -> Unit)? = null + private var onTimestampSkipped: ((EpisodeSkip.SkipStamp) -> Unit)? = null override fun releaseCallbacks() { playerUpdated = null @@ -133,6 +134,7 @@ class CS3IPlayer : IPlayer { onTracksInfoChanged = null onTimestampInvoked = null requestSubtitleUpdate = null + onTimestampSkipped = null } override fun initCallbacks( @@ -149,6 +151,7 @@ class CS3IPlayer : IPlayer { embeddedSubtitlesFetched: ((List) -> Unit)?, onTracksInfoChanged: (() -> Unit)?, onTimestampInvoked: ((EpisodeSkip.SkipStamp) -> Unit)?, + onTimestampSkipped: ((EpisodeSkip.SkipStamp) -> Unit)?, ) { this.playerUpdated = playerUpdated this.updateIsPlaying = updateIsPlaying @@ -163,6 +166,7 @@ class CS3IPlayer : IPlayer { this.embeddedSubtitlesFetched = embeddedSubtitlesFetched this.onTracksInfoChanged = onTracksInfoChanged this.onTimestampInvoked = onTimestampInvoked + this.onTimestampSkipped = onTimestampSkipped } // I know, this is not a perfect solution, however it works for fixing subs @@ -801,8 +805,14 @@ class CS3IPlayer : IPlayer { //val dur = this@CS3IPlayer.getDuration() ?: return@apply val pos = this@CS3IPlayer.getPosition() ?: return@apply for (lastTimeStamp in lastTimeStamps) { - if(lastTimeStamp.startMs <= pos && pos < lastTimeStamp.endMs) { - seekTo(lastTimeStamp.endMs) + if (lastTimeStamp.startMs <= pos && pos < lastTimeStamp.endMs) { + if (lastTimeStamp.skipToNextEpisode) { + handleEvent(CSPlayerEvent.NextEpisode) + } else { + seekTo(lastTimeStamp.endMs) + } + + onTimestampSkipped?.invoke(lastTimeStamp) break } } @@ -1029,12 +1039,14 @@ class CS3IPlayer : IPlayer { override fun addTimeStamps(timeStamps: List) { lastTimeStamps = timeStamps timeStamps.forEach { timestamp -> + println("ADDING: $timestamp") exoPlayer?.createMessage { _, payload -> - + if (payload is EpisodeSkip.SkipStamp) // this should always be true + onTimestampInvoked?.invoke(payload) } ?.setLooper(Looper.getMainLooper()) ?.setPosition(timestamp.startMs) - // .setPayload(customPayloadData) + ?.setPayload(timestamp) ?.setDeleteAfterDelivery(false) ?.send() } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt index 180311f4..70dd53b3 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt @@ -1,5 +1,6 @@ package com.lagradost.cloudstream3.ui.player +import android.animation.ValueAnimator import android.annotation.SuppressLint import android.app.Dialog import android.content.Context @@ -13,6 +14,7 @@ import android.view.ViewGroup import android.widget.* import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AlertDialog +import androidx.core.animation.addListener import androidx.core.content.ContextCompat import androidx.core.view.isGone import androidx.core.view.isVisible @@ -49,6 +51,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe import com.lagradost.cloudstream3.utils.UIHelper.hideSystemUI import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage +import com.lagradost.cloudstream3.utils.UIHelper.toPx import kotlinx.android.synthetic.main.dialog_online_subtitles.* import kotlinx.android.synthetic.main.dialog_online_subtitles.apply_btt import kotlinx.android.synthetic.main.dialog_online_subtitles.cancel_btt @@ -66,8 +69,7 @@ class GeneratorPlayer : FullScreenPlayer() { Log.i(TAG, "newInstance = $syncData") lastUsedGenerator = generator return Bundle().apply { - if (syncData != null) - putSerializable("syncData", syncData) + if (syncData != null) putSerializable("syncData", syncData) } } @@ -180,9 +182,7 @@ class GeneratorPlayer : FullScreenPlayer() { }, currentSubs, (if (sameEpisode) currentSelectedSubtitles else null) ?: getAutoSelectSubtitle( - currentSubs, - settings = true, - downloads = true + currentSubs, settings = true, downloads = true ), ) } @@ -231,9 +231,7 @@ class GeneratorPlayer : FullScreenPlayer() { } override fun openOnlineSubPicker( - context: Context, - imdbId: Long?, - dismissCallback: (() -> Unit) + context: Context, imdbId: Long?, dismissCallback: (() -> Unit) ) { val providers = subsProviders val isSingleProvider = subsProviders.size == 1 @@ -256,8 +254,7 @@ class GeneratorPlayer : FullScreenPlayer() { val arrayAdapter = object : ArrayAdapter(dialog.context, layout) { fun setHearingImpairedIcon( - imageViewEnd: ImageView?, - position: Int + imageViewEnd: ImageView?, position: Int ) { if (imageViewEnd == null) return val isHearingImpaired = @@ -265,13 +262,11 @@ class GeneratorPlayer : FullScreenPlayer() { val drawableEnd = if (isHearingImpaired) { ContextCompat.getDrawable( - context, - R.drawable.ic_baseline_hearing_24 + context, R.drawable.ic_baseline_hearing_24 )?.apply { setTint( ContextCompat.getColor( - context, - R.color.textColor + context, R.color.textColor ) ) } @@ -281,8 +276,7 @@ class GeneratorPlayer : FullScreenPlayer() { } override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { - val view = convertView ?: LayoutInflater.from(context) - .inflate(layout, null) + val view = convertView ?: LayoutInflater.from(context).inflate(layout, null) val item = getItem(position) @@ -337,13 +331,12 @@ class GeneratorPlayer : FullScreenPlayer() { override fun onQueryTextSubmit(query: String?): Boolean { dialog.search_loading_bar?.show() ioSafe { - val search = AbstractSubtitleEntities.SubtitleSearch( - query = query ?: return@ioSafe, - imdb = imdbId, - epNumber = currentTempMeta.episode, - seasonNumber = currentTempMeta.season, - lang = currentLanguageTwoLetters.ifBlank { null } - ) + val search = + AbstractSubtitleEntities.SubtitleSearch(query = query ?: return@ioSafe, + imdb = imdbId, + epNumber = currentTempMeta.episode, + seasonNumber = currentTempMeta.season, + lang = currentLanguageTwoLetters.ifBlank { null }) val results = providers.amap { try { it.search(search) @@ -379,14 +372,12 @@ class GeneratorPlayer : FullScreenPlayer() { dialog.search_filter.setOnClickListener { view -> val lang639_1 = languages.map { it.ISO_639_1 } - activity?.showDialog( - languages.map { it.languageName }, + activity?.showDialog(languages.map { it.languageName }, lang639_1.indexOf(currentLanguageTwoLetters), view?.context?.getString(R.string.subs_subtitle_languages) ?: return@setOnClickListener, true, - { } - ) { index -> + { }) { index -> currentLanguageTwoLetters = lang639_1[index] dialog.subtitles_search.setQuery(dialog.subtitles_search.query, true) } @@ -472,8 +463,8 @@ class GeneratorPlayer : FullScreenPlayer() { if (uri == null) return@normalSafeApiCall val ctx = context ?: AcraApplication.context ?: return@normalSafeApiCall // RW perms for the path - val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or - Intent.FLAG_GRANT_WRITE_URI_PERMISSION + val flags = + Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION ctx.contentResolver.takePersistableUriPermission(uri, flags) @@ -536,11 +527,9 @@ class GeneratorPlayer : FullScreenPlayer() { } if (subsProvidersIsActive) { - val loadFromOpenSubsFooter: TextView = - layoutInflater.inflate( - R.layout.sort_bottom_footer_add_choice, - null - ) as TextView + val loadFromOpenSubsFooter: TextView = layoutInflater.inflate( + R.layout.sort_bottom_footer_add_choice, null + ) as TextView loadFromOpenSubsFooter.text = ctx.getString(R.string.player_load_subtitles_online) @@ -592,8 +581,7 @@ class GeneratorPlayer : FullScreenPlayer() { val subtitleIndexStart = currentSubtitles.indexOf(currentSelectedSubtitles) + 1 var subtitleIndex = subtitleIndexStart - val subsArrayAdapter = - ArrayAdapter(ctx, R.layout.sort_bottom_single_choice) + val subsArrayAdapter = ArrayAdapter(ctx, R.layout.sort_bottom_single_choice) subsArrayAdapter.add(ctx.getString(R.string.no_subtitles)) subsArrayAdapter.addAll(currentSubtitles.map { it.name }) @@ -631,8 +619,7 @@ class GeneratorPlayer : FullScreenPlayer() { val prefValues = ctx.resources.getStringArray(R.array.subtitles_encoding_values) val value = settingsManager.getString( - ctx.getString(R.string.subtitles_encoding_key), - null + ctx.getString(R.string.subtitles_encoding_key), null ) val index = prefValues.indexOf(value) text = prefNames[if (index == -1) 0 else index] @@ -644,28 +631,22 @@ class GeneratorPlayer : FullScreenPlayer() { val prefNames = ctx.resources.getStringArray(R.array.subtitles_encoding_list) val prefValues = ctx.resources.getStringArray(R.array.subtitles_encoding_values) - val currentPrefMedia = - settingsManager.getString( - ctx.getString(R.string.subtitles_encoding_key), - null - ) + val currentPrefMedia = settingsManager.getString( + ctx.getString(R.string.subtitles_encoding_key), null + ) shouldDismiss = false sourceDialog.dismissSafe(activity) val index = prefValues.indexOf(currentPrefMedia) - activity?.showDialog( - prefNames.toList(), + activity?.showDialog(prefNames.toList(), if (index == -1) 0 else index, ctx.getString(R.string.subtitles_encoding), true, {}) { - settingsManager.edit() - .putString( - ctx.getString(R.string.subtitles_encoding_key), - prefValues[it] - ) - .apply() + settingsManager.edit().putString( + ctx.getString(R.string.subtitles_encoding_key), prefValues[it] + ).apply() updateForcedEncoding(ctx) dismiss() @@ -944,17 +925,14 @@ class GeneratorPlayer : FullScreenPlayer() { context?.let { ctx -> val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx) if (settingsManager.getBoolean( - ctx.getString(R.string.episode_sync_enabled_key), - true + ctx.getString(R.string.episode_sync_enabled_key), true ) - ) - maxEpisodeSet = meta.episode + ) maxEpisodeSet = meta.episode sync.modifyMaxEpisode(meta.episode) } } - if (meta.tvType.isAnimeOp()) - isOpVisible = percentage < SKIP_OP_VIDEO_PERCENTAGE + if (meta.tvType.isAnimeOp()) isOpVisible = percentage < SKIP_OP_VIDEO_PERCENTAGE } } player_skip_op?.isVisible = isOpVisible @@ -966,9 +944,7 @@ class GeneratorPlayer : FullScreenPlayer() { } private fun getAutoSelectSubtitle( - subtitles: Set, - settings: Boolean, - downloads: Boolean + subtitles: Set, settings: Boolean, downloads: Boolean ): SubtitleData? { val langCode = preferredAutoSelectSubtitles ?: return null val lang = SubtitleHelper.fromTwoLettersToLanguage(langCode) ?: return null @@ -1014,23 +990,20 @@ class GeneratorPlayer : FullScreenPlayer() { player.handleEvent(CSPlayerEvent.Play) return true } - } else - if (!langCode.isNullOrEmpty()) { - getAutoSelectSubtitle( - currentSubs, - settings = true, - downloads = false - )?.let { sub -> - - if (setSubtitles(sub)) { - player.saveData() - player.reloadPlayer(ctx) - player.handleEvent(CSPlayerEvent.Play) - return true - } + } else if (!langCode.isNullOrEmpty()) { + getAutoSelectSubtitle( + currentSubs, settings = true, downloads = false + )?.let { sub -> + if (setSubtitles(sub)) { + player.saveData() + player.reloadPlayer(ctx) + player.handleEvent(CSPlayerEvent.Play) + return true } + } + } } return false } @@ -1086,17 +1059,17 @@ class GeneratorPlayer : FullScreenPlayer() { context?.let { ctx -> //Generate video title val playerVideoTitle = if (headerName != null) { - (headerName + - if (tvType.isEpisodeBased() && episode != null) - if (season == null) - " - ${ctx.getString(R.string.episode)} $episode" - else - " \"${ctx.getString(R.string.season_short)}${season}:${ - ctx.getString( - R.string.episode_short - ) - }${episode}\"" - else "") + if (subName.isNullOrBlank() || subName == headerName) "" else " - $subName" + (headerName + if (tvType.isEpisodeBased() && episode != null) if (season == null) " - ${ + ctx.getString( + R.string.episode + ) + } $episode" + else " \"${ctx.getString(R.string.season_short)}${season}:${ + ctx.getString( + R.string.episode_short + ) + }${episode}\"" + else "") + if (subName.isNullOrBlank() || subName == headerName) "" else " - $subName" } else { "" } @@ -1136,8 +1109,7 @@ class GeneratorPlayer : FullScreenPlayer() { "" } - val source = currentSelectedLink?.first?.name ?: currentSelectedLink?.second?.name - ?: "NULL" + val source = currentSelectedLink?.first?.name ?: currentSelectedLink?.second?.name ?: "NULL" player_video_title_rez?.text = when (titleRez) { 0 -> "" @@ -1160,14 +1132,11 @@ class GeneratorPlayer : FullScreenPlayer() { } override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { // this is used instead of layout-television to follow the settings and some TV devices are not classified as TV for some reason isTv = isTvSettings() - layout = - if (isTv) R.layout.fragment_player_tv else R.layout.fragment_player + layout = if (isTv) R.layout.fragment_player_tv else R.layout.fragment_player viewModel = ViewModelProvider(this)[PlayerGeneratorViewModel::class.java] sync = ViewModelProvider(this)[SyncViewModel::class.java] @@ -1179,8 +1148,57 @@ class GeneratorPlayer : FullScreenPlayer() { return super.onCreateView(inflater, container, savedInstanceState) } + var timestampShowState = false + + private fun displayTimeStamp(show: Boolean) { + if(timestampShowState == show) return + timestampShowState = show + skip_chapter_button?.apply { + val showWidth = 190.toPx + val noShowWidth = 10.toPx + //if((show && width == showWidth) || (!show && width == noShowWidth)) { + // return + //} + val to = if (show) showWidth else noShowWidth + val from = if (!show) showWidth else noShowWidth + + isVisible = true + + // just in case + val lay = layoutParams + lay.width = from + layoutParams = lay + + ValueAnimator.ofInt( + from, to + ).apply { + addListener(onEnd = { + if (!show) skip_chapter_button?.isVisible = false + }) + addUpdateListener { valueAnimator -> + val value = valueAnimator.animatedValue as Int + val layoutParams: ViewGroup.LayoutParams = layoutParams + layoutParams.width = value + setLayoutParams(layoutParams) + } + duration = 500 + start() + } + } + } + + override fun onTimestampSkipped(timestamp: EpisodeSkip.SkipStamp) { + println("onTimestampSkipped:::$timestamp") + displayTimeStamp(false) + } + override fun onTimestamp(timestamp: EpisodeSkip.SkipStamp) { + println("onTimestamp:::$timestamp") skip_chapter_button.setText(timestamp.uiText) + displayTimeStamp(true) + skip_chapter_button?.handler?.postDelayed({ + displayTimeStamp(false) + }, 6000) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -1198,8 +1216,7 @@ class GeneratorPlayer : FullScreenPlayer() { settingsManager.getBoolean(getString(R.string.filter_sub_lang_key), false) if (filterSubByLang) { val langFromPrefMedia = settingsManager.getStringSet( - this.getString(R.string.provider_lang_key), - mutableSetOf("en") + this.getString(R.string.provider_lang_key), mutableSetOf("en") ) langFilterList = langFromPrefMedia?.mapNotNull { fromTwoLettersToLanguage(it)?.lowercase() ?: return@mapNotNull null diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt index 2f71ef79..6c1f1108 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt @@ -127,6 +127,7 @@ interface IPlayer { embeddedSubtitlesFetched: ((List) -> Unit)? = null, // callback from player to give all embedded subtitles onTracksInfoChanged: (() -> Unit)? = null, // Callback when tracks are changed, used for UI changes onTimestampInvoked: ((EpisodeSkip.SkipStamp) -> Unit)? = null, // Callback when timestamps appear + 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) ) fun releaseCallbacks() diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt index bbd82482..49fcde60 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt @@ -128,7 +128,14 @@ class PlayerGeneratorViewModel : ViewModel() { val page = (generator as? RepoLinkGenerator?)?.page if (page != null && meta is ResultEpisode) { _currentStamps.postValue(listOf()) - _currentStamps.postValue(EpisodeSkip.getStamps(page, meta, duration)) + _currentStamps.postValue( + EpisodeSkip.getStamps( + page, + meta, + duration, + hasNextEpisode() ?: false + ) + ) } } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/AniSkip.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/AniSkip.kt index 35084131..4e3de770 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/AniSkip.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/AniSkip.kt @@ -10,43 +10,67 @@ import com.lagradost.cloudstream3.ui.result.ResultEpisode import com.lagradost.cloudstream3.ui.result.txt object EpisodeSkip { + private const val TAG = "EpisodeSkip" + + enum class SkipType(@StringRes name: Int) { + Opening(R.string.skip_type_op), + Ending(R.string.skip_type_ed), + Recap(R.string.skip_type_recap), + MixedOpening(R.string.skip_type_mixed_op), + MixedEnding(R.string.skip_type_mixed_ed), + Credits(R.string.skip_type_creddits), + Intro(R.string.skip_type_creddits), + } + data class SkipStamp( - @StringRes - private val name: Int, + val type: SkipType, + val skipToNextEpisode: Boolean, val startMs: Long, val endMs: Long, ) { - val uiText = txt(R.string.skip_type_format, txt(name)) + val uiText = if (skipToNextEpisode) txt(R.string.next_episode) else txt( + R.string.skip_type_format, + txt(type.name) + ) } private val cachedStamps = HashMap>() + private fun shouldSkipToNextEpisode(endMs: Long, episodeDurationMs: Long): Boolean { + return (episodeDurationMs - endMs) < 12_000L + } + suspend fun getStamps( data: LoadResponse, episode: ResultEpisode, - episodeDurationMs: Long + episodeDurationMs: Long, + hasNextEpisode : Boolean, ): List { cachedStamps[episode.id]?.let { list -> return list } val out = mutableListOf() - println("CALLING WITH : ${data.syncData} $episode $episodeDurationMs") + Log.i(TAG, "Requesting SkipStamp from ${data.syncData}") + if (data is AnimeLoadResponse && (data.type == TvType.Anime || data.type == TvType.OVA)) { data.getMalId()?.toIntOrNull()?.let { malId -> AniSkip.getResult(malId, episode.episode, episodeDurationMs)?.mapNotNull { stamp -> - val name = when (stamp.skipType) { - "op" -> R.string.skip_type_op - "ed" -> R.string.skip_type_ed - "recap" -> R.string.skip_type_recap - "mixed-ed" -> R.string.skip_type_mixed_ed - "mixed-op" -> R.string.skip_type_mixed_op + val skipType = when (stamp.skipType) { + "op" -> SkipType.Opening + "ed" -> SkipType.Ending + "recap" -> SkipType.Recap + "mixed-ed" -> SkipType.MixedEnding + "mixed-op" -> SkipType.MixedOpening else -> null } ?: return@mapNotNull null + val end = (stamp.interval.endTime * 1000.0).toLong() + val start = (stamp.interval.startTime * 1000.0).toLong() SkipStamp( - name, - (stamp.interval.startTime * 1000.0).toLong(), - (stamp.interval.endTime * 1000.0).toLong() + type = skipType, + skipToNextEpisode = hasNextEpisode && shouldSkipToNextEpisode(end, episodeDurationMs), + startMs = start, + endMs = end ) }?.let { list -> out.addAll(list) @@ -62,25 +86,27 @@ object EpisodeSkip { // taken from https://github.com/saikou-app/saikou/blob/3803f8a7a59b826ca193664d46af3a22bbc989f7/app/src/main/java/ani/saikou/others/AniSkip.kt // the following is GPLv3 code https://github.com/saikou-app/saikou/blob/main/LICENSE.md object AniSkip { + private const val TAG = "AniSkip" suspend fun getResult(malId: Int, episodeNumber: Int, episodeLength: Long): List? { return try { 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}" - println("URLLLL::::$url") + Log.i(TAG, "Requesting $url") val a = app.get(url) - println("GOT RESPONSE:::.") + //println("GOT RESPONSE:::.") val res = a.parsed() - Log.i("AniSkip", "Response = $res") + Log.i(TAG, "Found ${res.found} with ${res.results?.size} results") + + // Log.i("AniSkip", "Response = $res") if (res.found) res.results else null } catch (t: Throwable) { - Log.i("AniSkip", "error = ${t.message}") + Log.i(TAG, "error = ${t.message}") logError(t) null } } - data class AniSkipResponse( @JsonSerialize val found: Boolean, @JsonSerialize val results: List?, @@ -95,18 +121,6 @@ object AniSkip { @JsonSerialize val episodeLength: Double ) - - //fun String.getType(): String { - // - // - // - // - // - // - // - // - //} - data class AniSkipInterval( @JsonSerialize val startTime: Double, @JsonSerialize val endTime: Double diff --git a/app/src/main/res/layout/player_custom_layout.xml b/app/src/main/res/layout/player_custom_layout.xml index 65b24a17..d571d7ca 100644 --- a/app/src/main/res/layout/player_custom_layout.xml +++ b/app/src/main/res/layout/player_custom_layout.xml @@ -321,9 +321,11 @@ #66000000 #C0121212 - #C0121212 + #4D121212 #121212 #66B5B5B5