From 7619b7e9d99772f6528a719d2dba0b471b834aa2 Mon Sep 17 00:00:00 2001 From: reduplicated <110570621+reduplicated@users.noreply.github.com> Date: Fri, 4 Nov 2022 12:46:30 +0100 Subject: [PATCH 1/7] aniskip groundwork --- .../lagradost/cloudstream3/CommonActivity.kt | 3 + .../lagradost/cloudstream3/MainActivity.kt | 4 + .../cloudstream3/network/RequestsHelper.kt | 4 +- .../ui/player/AbstractPlayerFragment.kt | 8 +- .../cloudstream3/ui/player/CS3IPlayer.kt | 35 ++- .../ui/player/FullScreenPlayer.kt | 7 + .../cloudstream3/ui/player/GeneratorPlayer.kt | 21 +- .../cloudstream3/ui/player/IPlayer.kt | 13 +- .../ui/player/PlayerGeneratorViewModel.kt | 23 +- .../ui/player/RepoLinkGenerator.kt | 4 +- .../ui/result/ResultViewModel2.kt | 8 +- .../lagradost/cloudstream3/utils/AniSkip.kt | 114 ++++++++++ .../cloudstream3/widget/TopCropImageView.kt | 56 +++++ app/src/main/res/layout/fragment_player.xml | 204 +++++++++--------- .../main/res/layout/player_custom_layout.xml | 16 ++ app/src/main/res/values/colors.xml | 1 + app/src/main/res/values/strings.xml | 9 + 17 files changed, 409 insertions(+), 121 deletions(-) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/utils/AniSkip.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/widget/TopCropImageView.kt diff --git a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt index 32df314f..63895cab 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt @@ -337,6 +337,9 @@ object CommonActivity { KeyEvent.KEYCODE_C, KeyEvent.KEYCODE_NUMPAD_4, KeyEvent.KEYCODE_4 -> { PlayerEventType.SkipOp } + KeyEvent.KEYCODE_V, KeyEvent.KEYCODE_NUMPAD_5, KeyEvent.KEYCODE_5 -> { + PlayerEventType.SkipOp + } KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, KeyEvent.KEYCODE_P, KeyEvent.KEYCODE_SPACE, KeyEvent.KEYCODE_NUMPAD_ENTER, KeyEvent.KEYCODE_ENTER -> { // space is not captured due to navigation PlayerEventType.PlayPauseToggle } diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index eb83b08a..b2395bea 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -88,6 +88,9 @@ import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.fragment_result_swipe.* import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock +import okhttp3.ConnectionSpec +import okhttp3.OkHttpClient +import okhttp3.internal.applyConnectionSpec import java.io.File import java.net.URI import java.nio.charset.Charset @@ -179,6 +182,7 @@ var app = Requests(responseParser = object : ResponseParser { } }).apply { defaultHeaders = mapOf("user-agent" to USER_AGENT) + //baseClient = baseClient.newBuilder().connectionSpecs(listOf(ConnectionSpec.COMPATIBLE_TLS)).build() } class MainActivity : AppCompatActivity(), ColorPickerDialogListener { 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 13299002..67ffa732 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/network/RequestsHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/network/RequestsHelper.kt @@ -6,10 +6,8 @@ import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.USER_AGENT import com.lagradost.nicehttp.Requests import com.lagradost.nicehttp.ignoreAllSSLErrors -import okhttp3.Cache -import okhttp3.Headers +import okhttp3.* import okhttp3.Headers.Companion.toHeaders -import okhttp3.OkHttpClient import java.io.File 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 9a0debcf..d7076ae6 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 @@ -38,6 +38,7 @@ import com.lagradost.cloudstream3.ui.subtitles.SaveCaptionStyle import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment import com.lagradost.cloudstream3.utils.AppUtils import com.lagradost.cloudstream3.utils.AppUtils.requestLocalAudioFocus +import com.lagradost.cloudstream3.utils.EpisodeSkip import com.lagradost.cloudstream3.utils.UIHelper import com.lagradost.cloudstream3.utils.UIHelper.hideSystemUI import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage @@ -103,6 +104,10 @@ abstract class AbstractPlayerFragment( throw NotImplementedError() } + open fun onTimestamp(timestamp: EpisodeSkip.SkipStamp) { + + } + open fun exitedPipMode() { throw NotImplementedError() } @@ -373,7 +378,8 @@ abstract class AbstractPlayerFragment( ), subtitlesUpdates = ::subtitlesChanged, embeddedSubtitlesFetched = ::embeddedSubtitlesFetched, - onTracksInfoChanged = ::onTracksInfoChanged + onTracksInfoChanged = ::onTracksInfoChanged, + onTimestampInvoked = ::onTimestamp ) 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 f60d8c78..0f8a63e2 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 @@ -18,7 +18,10 @@ import com.google.android.exoplayer2.trackselection.DefaultTrackSelector import com.google.android.exoplayer2.trackselection.TrackSelectionOverride import com.google.android.exoplayer2.trackselection.TrackSelector import com.google.android.exoplayer2.ui.SubtitleView -import com.google.android.exoplayer2.upstream.* +import com.google.android.exoplayer2.upstream.DataSource +import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory +import com.google.android.exoplayer2.upstream.DefaultHttpDataSource +import com.google.android.exoplayer2.upstream.HttpDataSource import com.google.android.exoplayer2.upstream.cache.CacheDataSource import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor import com.google.android.exoplayer2.upstream.cache.SimpleCache @@ -32,6 +35,7 @@ import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.ui.subtitles.SaveCaptionStyle +import com.lagradost.cloudstream3.utils.EpisodeSkip import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLinkPlayList import com.lagradost.cloudstream3.utils.ExtractorUri @@ -113,6 +117,7 @@ class CS3IPlayer : IPlayer { private var playerUpdated: ((Any?) -> Unit)? = null private var embeddedSubtitlesFetched: ((List) -> Unit)? = null private var onTracksInfoChanged: (() -> Unit)? = null + private var onTimestampInvoked: ((EpisodeSkip.SkipStamp) -> Unit)? = null override fun releaseCallbacks() { playerUpdated = null @@ -126,6 +131,7 @@ class CS3IPlayer : IPlayer { prevEpisode = null subtitlesUpdates = null onTracksInfoChanged = null + onTimestampInvoked = null requestSubtitleUpdate = null } @@ -142,6 +148,7 @@ class CS3IPlayer : IPlayer { subtitlesUpdates: (() -> Unit)?, embeddedSubtitlesFetched: ((List) -> Unit)?, onTracksInfoChanged: (() -> Unit)?, + onTimestampInvoked: ((EpisodeSkip.SkipStamp) -> Unit)?, ) { this.playerUpdated = playerUpdated this.updateIsPlaying = updateIsPlaying @@ -155,6 +162,7 @@ class CS3IPlayer : IPlayer { this.subtitlesUpdates = subtitlesUpdates this.embeddedSubtitlesFetched = embeddedSubtitlesFetched this.onTracksInfoChanged = onTracksInfoChanged + this.onTimestampInvoked = onTimestampInvoked } // I know, this is not a perfect solution, however it works for fixing subs @@ -789,6 +797,16 @@ class CS3IPlayer : IPlayer { CSPlayerEvent.SeekBack -> seekTime(-seekActionTime) CSPlayerEvent.NextEpisode -> nextEpisode?.invoke() CSPlayerEvent.PrevEpisode -> prevEpisode?.invoke() + CSPlayerEvent.SkipCurrentChapter -> { + //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) + break + } + } + } } } } catch (e: Exception) { @@ -1007,6 +1025,21 @@ class CS3IPlayer : IPlayer { } } + private var lastTimeStamps: List = emptyList() + override fun addTimeStamps(timeStamps: List) { + lastTimeStamps = timeStamps + timeStamps.forEach { timestamp -> + exoPlayer?.createMessage { _, payload -> + + } + ?.setLooper(Looper.getMainLooper()) + ?.setPosition(timestamp.startMs) + // .setPayload(customPayloadData) + ?.setDeleteAfterDelivery(false) + ?.send() + } + } + fun onRenderFirst() { if (!hasUsedFirstRender) { // this insures that we only call this once per player load Log.i(TAG, "Rendered first frame") diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt index 52125c68..0f9a6548 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt @@ -1141,6 +1141,9 @@ open class FullScreenPlayer : AbstractPlayerFragment() { PlayerEventType.Play -> { player.handleEvent(CSPlayerEvent.Play) } + PlayerEventType.SkipCurrentChapter -> { + player.handleEvent(CSPlayerEvent.SkipCurrentChapter) + } PlayerEventType.Resize -> { nextResize() } @@ -1254,6 +1257,10 @@ open class FullScreenPlayer : AbstractPlayerFragment() { player.handleEvent(CSPlayerEvent.PlayPauseToggle) } + skip_chapter_button?.setOnClickListener { + player.handleEvent(CSPlayerEvent.SkipCurrentChapter) + } + // init clicks player_resize_btt?.setOnClickListener { autoHide() 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 f466dd7e..180311f4 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 @@ -36,8 +36,8 @@ import com.lagradost.cloudstream3.ui.player.PlayerSubtitleHelper.Companion.toSub import com.lagradost.cloudstream3.ui.result.ResultEpisode import com.lagradost.cloudstream3.ui.result.ResultFragment import com.lagradost.cloudstream3.ui.result.SyncViewModel +import com.lagradost.cloudstream3.ui.result.setText import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings -import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment.Companion.getAutoSelectLanguageISO639_1 import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.Coroutines.ioSafe @@ -58,7 +58,6 @@ import kotlinx.android.synthetic.main.player_select_source_and_subs.* import kotlinx.android.synthetic.main.player_select_source_and_subs.subtitles_click_settings import kotlinx.android.synthetic.main.player_select_tracks.* import kotlinx.coroutines.Job -import kotlinx.coroutines.delay class GeneratorPlayer : FullScreenPlayer() { companion object { @@ -165,6 +164,7 @@ class GeneratorPlayer : FullScreenPlayer() { isActive = true setPlayerDimen(null) setTitle() + hasRequestedStamps = false loadExtractorJob(link.first) // load player @@ -878,7 +878,7 @@ class GeneratorPlayer : FullScreenPlayer() { } var maxEpisodeSet: Int? = null - + var hasRequestedStamps: Boolean = false override fun playerPositionChanged(posDur: Pair) { // Don't save livestream data if ((currentMeta as? ResultEpisode)?.tvType?.isLiveStream() == true) return @@ -888,10 +888,15 @@ class GeneratorPlayer : FullScreenPlayer() { val (position, duration) = posDur if (duration == 0L) return // idk how you achieved this, but div by zero crash + if (!hasRequestedStamps) { + hasRequestedStamps = true + viewModel.loadStamps(duration) + } viewModel.getId()?.let { DataStoreHelper.setViewPos(it, position, duration) } + val percentage = position * 100L / duration val nextEp = percentage >= NEXT_WATCH_EPISODE_PERCENTAGE @@ -1174,6 +1179,10 @@ class GeneratorPlayer : FullScreenPlayer() { return super.onCreateView(inflater, container, savedInstanceState) } + override fun onTimestamp(timestamp: EpisodeSkip.SkipStamp) { + skip_chapter_button.setText(timestamp.uiText) + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) var langFilterList = listOf() @@ -1203,7 +1212,7 @@ class GeneratorPlayer : FullScreenPlayer() { sync.updateUserData() - preferredAutoSelectSubtitles = SubtitlesFragment.getAutoSelectLanguageISO639_1() + preferredAutoSelectSubtitles = getAutoSelectLanguageISO639_1() if (currentSelectedLink == null) { viewModel.loadLinks() @@ -1218,6 +1227,10 @@ class GeneratorPlayer : FullScreenPlayer() { activity?.popCurrentPage() } + observe(viewModel.currentStamps) { stamps -> + player.addTimeStamps(stamps) + } + observe(viewModel.loadingLinks) { when (it) { is Resource.Loading -> { 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 473b3e65..2f71ef79 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 @@ -2,6 +2,7 @@ package com.lagradost.cloudstream3.ui.player import android.content.Context import com.lagradost.cloudstream3.ui.subtitles.SaveCaptionStyle +import com.lagradost.cloudstream3.utils.EpisodeSkip import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorUri @@ -12,9 +13,9 @@ enum class PlayerEventType(val value: Int) { SeekForward(2), SeekBack(3), - //SkipCurrentChapter(4), + SkipCurrentChapter(4), NextEpisode(5), - PrevEpisode(5), + PrevEpisode(6), PlayPauseToggle(7), ToggleMute(8), Lock(9), @@ -32,7 +33,7 @@ enum class CSPlayerEvent(val value: Int) { SeekForward(2), SeekBack(3), - //SkipCurrentChapter(4), + SkipCurrentChapter(4), NextEpisode(5), PrevEpisode(6), PlayPauseToggle(7), @@ -54,7 +55,8 @@ interface Track { **/ val id: String? val label: String? -// val isCurrentlyPlaying: Boolean + + // val isCurrentlyPlaying: Boolean val language: String? } @@ -124,6 +126,7 @@ interface IPlayer { subtitlesUpdates: (() -> Unit)? = null, // callback from player to inform that subtitles have updated in some way 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 ) fun releaseCallbacks() @@ -131,6 +134,8 @@ interface IPlayer { fun updateSubtitleStyle(style: SaveCaptionStyle) fun saveData() + fun addTimeStamps(timeStamps: List) + fun loadPlayer( context: Context, sameEpisode: Boolean, 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 0ed26b71..bbd82482 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 @@ -9,10 +9,12 @@ import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.launchSafe import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.mvvm.safeApiCall +import com.lagradost.cloudstream3.ui.result.ResultEpisode +import com.lagradost.cloudstream3.utils.Coroutines.ioSafe +import com.lagradost.cloudstream3.utils.EpisodeSkip import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorUri import kotlinx.coroutines.Job -import kotlinx.coroutines.launch class PlayerGeneratorViewModel : ViewModel() { companion object { @@ -30,6 +32,9 @@ class PlayerGeneratorViewModel : ViewModel() { private val _loadingLinks = MutableLiveData>() val loadingLinks: LiveData> = _loadingLinks + private val _currentStamps = MutableLiveData>(emptyList()) + val currentStamps: LiveData> = _currentStamps + fun getId(): Int? { return generator?.getCurrentId() } @@ -113,10 +118,25 @@ class PlayerGeneratorViewModel : ViewModel() { } private var currentJob: Job? = null + private var currentStampJob: Job? = null + + fun loadStamps(duration: Long) { + println("Starting loadStamps with duration = $duration") + //currentStampJob?.cancel() + currentStampJob = ioSafe { + val meta = generator?.getCurrent() + val page = (generator as? RepoLinkGenerator?)?.page + if (page != null && meta is ResultEpisode) { + _currentStamps.postValue(listOf()) + _currentStamps.postValue(EpisodeSkip.getStamps(page, meta, duration)) + } + } + } fun loadLinks(clearCache: Boolean = false, isCasting: Boolean = false) { Log.i(TAG, "loadLinks") currentJob?.cancel() + currentJob = viewModelScope.launchSafe { val currentLinks = mutableSetOf>() val currentSubs = mutableSetOf() @@ -142,5 +162,6 @@ class PlayerGeneratorViewModel : ViewModel() { _currentLinks.postValue(currentLinks) _currentSubs.postValue(currentSubs) } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/RepoLinkGenerator.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/RepoLinkGenerator.kt index d0ab245d..2ce53ea5 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/RepoLinkGenerator.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/RepoLinkGenerator.kt @@ -2,6 +2,7 @@ package com.lagradost.cloudstream3.ui.player import android.util.Log import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull +import com.lagradost.cloudstream3.LoadResponse import com.lagradost.cloudstream3.ui.APIRepository import com.lagradost.cloudstream3.ui.result.ResultEpisode import com.lagradost.cloudstream3.utils.ExtractorLink @@ -11,7 +12,8 @@ import kotlin.math.min class RepoLinkGenerator( private val episodes: List, - private var currentIndex: Int = 0 + private var currentIndex: Int = 0, + val page: LoadResponse? = null, ) : IGenerator { companion object { const val TAG = "RepoLink" diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt index 6b04ebf9..0c26f69c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt @@ -416,7 +416,7 @@ class ResultViewModel2 : ViewModel() { return this?.firstOrNull { it.season == season } } - fun updateWatchStatus(currentResponse : LoadResponse, status: WatchType) { + fun updateWatchStatus(currentResponse: LoadResponse, status: WatchType) { val currentId = currentResponse.getId() val resultPage = currentResponse @@ -793,7 +793,7 @@ class ResultViewModel2 : ViewModel() { fun updateWatchStatus(status: WatchType) { - updateWatchStatus(currentResponse ?: return,status) + updateWatchStatus(currentResponse ?: return, status) _watchStatus.postValue(status) } @@ -1681,10 +1681,10 @@ class ResultViewModel2 : ViewModel() { preferDubStatus = indexer.dubStatus generator = if (isMovie) { - getMovie()?.let { RepoLinkGenerator(listOf(it)) } + getMovie()?.let { RepoLinkGenerator(listOf(it), page = currentResponse) } } else { episodes?.let { list -> - RepoLinkGenerator(list) + RepoLinkGenerator(list, page = currentResponse) } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/AniSkip.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/AniSkip.kt new file mode 100644 index 00000000..35084131 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/AniSkip.kt @@ -0,0 +1,114 @@ +package com.lagradost.cloudstream3.utils + +import android.util.Log +import androidx.annotation.StringRes +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.LoadResponse.Companion.getMalId +import com.lagradost.cloudstream3.mvvm.logError +import com.lagradost.cloudstream3.ui.result.ResultEpisode +import com.lagradost.cloudstream3.ui.result.txt + +object EpisodeSkip { + data class SkipStamp( + @StringRes + private val name: Int, + val startMs: Long, + val endMs: Long, + ) { + val uiText = txt(R.string.skip_type_format, txt(name)) + } + + private val cachedStamps = HashMap>() + + suspend fun getStamps( + data: LoadResponse, + episode: ResultEpisode, + episodeDurationMs: Long + ): List { + cachedStamps[episode.id]?.let { list -> + return list + } + + val out = mutableListOf() + println("CALLING WITH : ${data.syncData} $episode $episodeDurationMs") + 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 + else -> null + } ?: return@mapNotNull null + SkipStamp( + name, + (stamp.interval.startTime * 1000.0).toLong(), + (stamp.interval.endTime * 1000.0).toLong() + ) + }?.let { list -> + out.addAll(list) + } + } + } + if (out.isNotEmpty()) + cachedStamps[episode.id] = out + return out + } +} + +// 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 { + 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") + + val a = app.get(url) + println("GOT RESPONSE:::.") + val res = a.parsed() + Log.i("AniSkip", "Response = $res") + if (res.found) res.results else null + } catch (t: Throwable) { + Log.i("AniSkip", "error = ${t.message}") + logError(t) + null + } + } + + + data class AniSkipResponse( + @JsonSerialize val found: Boolean, + @JsonSerialize val results: List?, + @JsonSerialize val message: String?, + @JsonSerialize val statusCode: Int + ) + + data class Stamp( + @JsonSerialize val interval: AniSkipInterval, + @JsonSerialize val skipType: String, + @JsonSerialize val skipId: String, + @JsonSerialize val episodeLength: Double + ) + + + //fun String.getType(): String { + // + // + // + // + // + // + // + // + //} + + data class AniSkipInterval( + @JsonSerialize val startTime: Double, + @JsonSerialize val endTime: Double + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/widget/TopCropImageView.kt b/app/src/main/java/com/lagradost/cloudstream3/widget/TopCropImageView.kt new file mode 100644 index 00000000..11ac0160 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/widget/TopCropImageView.kt @@ -0,0 +1,56 @@ +package com.lagradost.cloudstream3.widget + +import android.content.Context +import android.graphics.Matrix +import android.graphics.drawable.Drawable +import android.util.AttributeSet + +// taken from https://gist.github.com/arriolac/3843346 +class TopCropImageView : androidx.appcompat.widget.AppCompatImageView { + constructor(context: Context) : super(context) { + init() + } + + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { + init() + } + + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super( + context, + attrs, + defStyleAttr + ) { + init() + } + + override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { + super.onLayout(changed, left, top, right, bottom) + recomputeImgMatrix() + } + + override fun setFrame(l: Int, t: Int, r: Int, b: Int): Boolean { + recomputeImgMatrix() + return super.setFrame(l, t, r, b) + } + + private fun init() { + scaleType = ScaleType.MATRIX + } + + private fun recomputeImgMatrix() { + val drawable: Drawable = drawable ?: return + val matrix: Matrix = imageMatrix + val scale: Float + val viewWidth: Int = width - paddingLeft - paddingRight + val viewHeight: Int = height - paddingTop - paddingBottom + val drawableWidth: Int = drawable.intrinsicWidth + val drawableHeight: Int = drawable.intrinsicHeight + scale = if (drawableWidth * viewHeight > drawableHeight * viewWidth) { + viewHeight.toFloat() / drawableHeight.toFloat() + } else { + viewWidth.toFloat() / drawableWidth.toFloat() + } + matrix.setScale(scale, scale) + imageMatrix = matrix + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_player.xml b/app/src/main/res/layout/fragment_player.xml index 232dcaa0..412f7b5b 100644 --- a/app/src/main/res/layout/fragment_player.xml +++ b/app/src/main/res/layout/fragment_player.xml @@ -1,130 +1,130 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/player_background" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@android:color/black" + android:orientation="horizontal" + android:screenOrientation="sensorLandscape" + app:backgroundTint="@android:color/black" + app:surface_type="texture_view"> + android:id="@+id/player_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@android:color/black" + app:auto_show="true" + app:backgroundTint="@android:color/black" + app:controller_layout_id="@layout/player_custom_layout" + app:hide_on_touch="false" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:show_timeout="0" /> + android:id="@+id/player_loading_overlay" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@android:color/black" + android:backgroundTint="@android:color/black" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + android:text="@string/skip_loading" + android:textAllCaps="false" + android:textColor="?attr/textColor" + android:visibility="gone" + app:cornerRadius="4dp" + app:icon="@drawable/ic_baseline_skip_next_24" + app:iconTint="?attr/textColor" + app:rippleColor="?attr/colorPrimary" + tools:visibility="visible" /> + android:id="@+id/main_load" + android:layout_width="50dp" + android:layout_height="50dp" + android:layout_gravity="center" /> + android:id="@+id/video_go_back_holder_holder" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_margin="5dp" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + android:layout_width="30dp" + android:layout_height="30dp" + android:layout_gravity="center" + android:contentDescription="@string/go_back_img_des" + android:src="@drawable/ic_baseline_arrow_back_24" + app:tint="@android:color/white" /> + android:id="@+id/player_loading_go_back" + android:layout_width="70dp" + android:layout_height="70dp" + android:layout_gravity="center" + android:background="@drawable/video_tap_button_always_white" + android:clickable="true" + android:contentDescription="@string/go_back_img_des" + android:focusable="true" /> + + + android:layout_height="wrap_content" + android:layout_marginTop="15dp" + android:gravity="start" + android:textColor="@color/white" + android:textStyle="bold" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:text="78% at 18kb/s" /> - - + android:id="@+id/video_torrent_seeders" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="0dp" + android:gravity="start" + android:textColor="@color/white" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintTop_toBottomOf="@+id/player_video_title" + tools:text="17 seeders" /> \ No newline at end of file diff --git a/app/src/main/res/layout/player_custom_layout.xml b/app/src/main/res/layout/player_custom_layout.xml index 9bbded4e..65b24a17 100644 --- a/app/src/main/res/layout/player_custom_layout.xml +++ b/app/src/main/res/layout/player_custom_layout.xml @@ -318,6 +318,22 @@ + + #66000000 #C0121212 + #C0121212 #121212 #66B5B5B5 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5665ea5c..144d2477 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -638,4 +638,13 @@ Browser App not found All Languages + + Skip %s + Opening + Ending + Recap + Mixed ending + Mixed opening + Credits + Intro 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 2/7] 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 From a5cee365720c2405894185fb557cb2be0a34b2c7 Mon Sep 17 00:00:00 2001 From: reduplicated <110570621+reduplicated@users.noreply.github.com> Date: Fri, 4 Nov 2022 19:17:07 +0100 Subject: [PATCH 3/7] removed prints --- .../main/java/com/lagradost/cloudstream3/CommonActivity.kt | 2 +- .../java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt | 1 - .../com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt | 1 + .../cloudstream3/ui/player/PlayerGeneratorViewModel.kt | 1 - .../main/java/com/lagradost/cloudstream3/utils/AniSkip.kt | 5 +---- app/src/main/res/layout/player_custom_layout.xml | 2 +- 6 files changed, 4 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt index 63895cab..af04a1a2 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt @@ -338,7 +338,7 @@ object CommonActivity { PlayerEventType.SkipOp } KeyEvent.KEYCODE_V, KeyEvent.KEYCODE_NUMPAD_5, KeyEvent.KEYCODE_5 -> { - PlayerEventType.SkipOp + PlayerEventType.SkipCurrentChapter } KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, KeyEvent.KEYCODE_P, KeyEvent.KEYCODE_SPACE, KeyEvent.KEYCODE_NUMPAD_ENTER, KeyEvent.KEYCODE_ENTER -> { // space is not captured due to navigation PlayerEventType.PlayPauseToggle 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 dac384b1..d132406c 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 @@ -1039,7 +1039,6 @@ 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) 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 70dd53b3..450711fa 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 @@ -186,6 +186,7 @@ class GeneratorPlayer : FullScreenPlayer() { ), ) } + player.addTimeStamps(listOf()) // clear stamps } private fun sortLinks(useQualitySettings: Boolean = true): List> { 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 49fcde60..4f16e9f6 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 @@ -121,7 +121,6 @@ class PlayerGeneratorViewModel : ViewModel() { private var currentStampJob: Job? = null fun loadStamps(duration: Long) { - println("Starting loadStamps with duration = $duration") //currentStampJob?.cancel() currentStampJob = ioSafe { val meta = generator?.getCurrent() 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 4e3de770..07233871 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/AniSkip.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/AniSkip.kt @@ -37,7 +37,7 @@ object EpisodeSkip { private val cachedStamps = HashMap>() private fun shouldSkipToNextEpisode(endMs: Long, episodeDurationMs: Long): Boolean { - return (episodeDurationMs - endMs) < 12_000L + return episodeDurationMs - endMs < 12_000L } suspend fun getStamps( @@ -94,11 +94,8 @@ object AniSkip { Log.i(TAG, "Requesting $url") val a = app.get(url) - //println("GOT RESPONSE:::.") val res = a.parsed() 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(TAG, "error = ${t.message}") diff --git a/app/src/main/res/layout/player_custom_layout.xml b/app/src/main/res/layout/player_custom_layout.xml index d571d7ca..95bf3b3e 100644 --- a/app/src/main/res/layout/player_custom_layout.xml +++ b/app/src/main/res/layout/player_custom_layout.xml @@ -334,7 +334,7 @@ app:layout_constraintEnd_toEndOf="parent" app:strokeColor="@color/white" app:strokeWidth="1dp" - android:text="Skip Opening" /> + tools:text="Skip Opening" /> Date: Fri, 4 Nov 2022 19:57:12 +0100 Subject: [PATCH 4/7] bump nicehttp --- app/build.gradle.kts | 2 +- .../cloudstream3/network/RequestsHelper.kt | 29 ++-------- .../cloudstream3/ui/player/GeneratorPlayer.kt | 2 +- .../cloudstream3/widget/TopCropImageView.kt | 56 ------------------- .../main/res/layout/player_custom_layout.xml | 1 + 5 files changed, 7 insertions(+), 83 deletions(-) delete mode 100644 app/src/main/java/com/lagradost/cloudstream3/widget/TopCropImageView.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7c77de39..df50d843 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -190,7 +190,7 @@ dependencies { // Networking // implementation("com.squareup.okhttp3:okhttp:4.9.2") // implementation("com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.1") - implementation("com.github.Blatzar:NiceHttp:0.3.3") + implementation("com.github.Blatzar:NiceHttp:0.3.4") // Util to skip the URI file fuckery 🙏 implementation("com.github.tachiyomiorg:unifile:17bec43") 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 50389291..8bf1f91b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/network/RequestsHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/network/RequestsHelper.kt @@ -1,37 +1,16 @@ 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 okhttp3.* +import com.lagradost.nicehttp.ignoreAllSSLErrors +import okhttp3.Cache +import okhttp3.Headers import okhttp3.Headers.Companion.toHeaders +import okhttp3.OkHttpClient 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/GeneratorPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt index 450711fa..16c5c3ce 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 @@ -1155,7 +1155,7 @@ class GeneratorPlayer : FullScreenPlayer() { if(timestampShowState == show) return timestampShowState = show skip_chapter_button?.apply { - val showWidth = 190.toPx + val showWidth = 170.toPx val noShowWidth = 10.toPx //if((show && width == showWidth) || (!show && width == noShowWidth)) { // return diff --git a/app/src/main/java/com/lagradost/cloudstream3/widget/TopCropImageView.kt b/app/src/main/java/com/lagradost/cloudstream3/widget/TopCropImageView.kt deleted file mode 100644 index 11ac0160..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/widget/TopCropImageView.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.lagradost.cloudstream3.widget - -import android.content.Context -import android.graphics.Matrix -import android.graphics.drawable.Drawable -import android.util.AttributeSet - -// taken from https://gist.github.com/arriolac/3843346 -class TopCropImageView : androidx.appcompat.widget.AppCompatImageView { - constructor(context: Context) : super(context) { - init() - } - - constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { - init() - } - - constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super( - context, - attrs, - defStyleAttr - ) { - init() - } - - override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { - super.onLayout(changed, left, top, right, bottom) - recomputeImgMatrix() - } - - override fun setFrame(l: Int, t: Int, r: Int, b: Int): Boolean { - recomputeImgMatrix() - return super.setFrame(l, t, r, b) - } - - private fun init() { - scaleType = ScaleType.MATRIX - } - - private fun recomputeImgMatrix() { - val drawable: Drawable = drawable ?: return - val matrix: Matrix = imageMatrix - val scale: Float - val viewWidth: Int = width - paddingLeft - paddingRight - val viewHeight: Int = height - paddingTop - paddingBottom - val drawableWidth: Int = drawable.intrinsicWidth - val drawableHeight: Int = drawable.intrinsicHeight - scale = if (drawableWidth * viewHeight > drawableHeight * viewWidth) { - viewHeight.toFloat() / drawableHeight.toFloat() - } else { - viewWidth.toFloat() / drawableWidth.toFloat() - } - matrix.setScale(scale, scale) - imageMatrix = matrix - } -} \ No newline at end of file diff --git a/app/src/main/res/layout/player_custom_layout.xml b/app/src/main/res/layout/player_custom_layout.xml index 95bf3b3e..2cdcbf22 100644 --- a/app/src/main/res/layout/player_custom_layout.xml +++ b/app/src/main/res/layout/player_custom_layout.xml @@ -319,6 +319,7 @@ Date: Fri, 4 Nov 2022 20:07:14 +0100 Subject: [PATCH 5/7] small fix --- app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt | 1 - .../com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt | 2 -- 2 files changed, 3 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index b2395bea..ff74d6cc 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -182,7 +182,6 @@ var app = Requests(responseParser = object : ResponseParser { } }).apply { defaultHeaders = mapOf("user-agent" to USER_AGENT) - //baseClient = baseClient.newBuilder().connectionSpecs(listOf(ConnectionSpec.COMPATIBLE_TLS)).build() } class MainActivity : AppCompatActivity(), ColorPickerDialogListener { 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 16c5c3ce..25925871 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 @@ -1189,12 +1189,10 @@ class GeneratorPlayer : FullScreenPlayer() { } 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({ From e24dc692d0e757bba339d09852d711d130210525 Mon Sep 17 00:00:00 2001 From: reduplicated <110570621+reduplicated@users.noreply.github.com> Date: Sat, 5 Nov 2022 00:32:40 +0100 Subject: [PATCH 6/7] small fixes --- .../ui/player/AbstractPlayerFragment.kt | 2 +- .../cloudstream3/ui/player/CS3IPlayer.kt | 55 +++++++++++-------- .../cloudstream3/ui/player/GeneratorPlayer.kt | 33 +++++++---- .../cloudstream3/ui/player/IPlayer.kt | 2 +- .../lagradost/cloudstream3/utils/AniSkip.kt | 27 +++++++-- 5 files changed, 78 insertions(+), 41 deletions(-) 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 5037b37d..21047db3 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 @@ -104,7 +104,7 @@ abstract class AbstractPlayerFragment( throw NotImplementedError() } - open fun onTimestamp(timestamp: EpisodeSkip.SkipStamp) { + open fun onTimestamp(timestamp: EpisodeSkip.SkipStamp?) { } 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 d132406c..8eda6e30 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 @@ -117,7 +117,7 @@ class CS3IPlayer : IPlayer { private var playerUpdated: ((Any?) -> Unit)? = null private var embeddedSubtitlesFetched: ((List) -> 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 override fun releaseCallbacks() { @@ -150,7 +150,7 @@ class CS3IPlayer : IPlayer { subtitlesUpdates: (() -> Unit)?, embeddedSubtitlesFetched: ((List) -> Unit)?, onTracksInfoChanged: (() -> Unit)?, - onTimestampInvoked: ((EpisodeSkip.SkipStamp) -> Unit)?, + onTimestampInvoked: ((EpisodeSkip.SkipStamp?) -> Unit)?, onTimestampSkipped: ((EpisodeSkip.SkipStamp) -> Unit)?, ) { this.playerUpdated = playerUpdated @@ -731,7 +731,7 @@ class CS3IPlayer : IPlayer { source } - println("PLAYBACK POS $playbackPosition") + //println("PLAYBACK POS $playbackPosition") return exoPlayerBuilder.build().apply { setPlayWhenReady(playWhenReady) seekTo(currentWindow, playbackPosition) @@ -747,8 +747,22 @@ class CS3IPlayer : IPlayer { } } - fun updatedTime() { - val position = exoPlayer?.currentPosition + private fun getCurrentTimestamp(writePosition : Long? = null): EpisodeSkip.SkipStamp? { + 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 if (duration != null && position != null) { playerPositionChanged?.invoke(Pair(position, duration)) @@ -760,12 +774,12 @@ class CS3IPlayer : IPlayer { } override fun seekTo(time: Long) { - updatedTime() + updatedTime(time) exoPlayer?.seekTo(time) } private fun ExoPlayer.seekTime(time: Long) { - updatedTime() + updatedTime(currentPosition + time) seekTo(currentPosition + time) } @@ -803,18 +817,13 @@ class CS3IPlayer : IPlayer { CSPlayerEvent.PrevEpisode -> prevEpisode?.invoke() CSPlayerEvent.SkipCurrentChapter -> { //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) { - if (lastTimeStamp.skipToNextEpisode) { - handleEvent(CSPlayerEvent.NextEpisode) - } else { - seekTo(lastTimeStamp.endMs) - } - - onTimestampSkipped?.invoke(lastTimeStamp) - break + getCurrentTimestamp()?.let { lastTimeStamp -> + if (lastTimeStamp.skipToNextEpisode) { + handleEvent(CSPlayerEvent.NextEpisode) + } else { + seekTo(lastTimeStamp.endMs + 1L) } + onTimestampSkipped?.invoke(lastTimeStamp) } } } @@ -1039,16 +1048,18 @@ class CS3IPlayer : IPlayer { override fun addTimeStamps(timeStamps: List) { lastTimeStamps = timeStamps timeStamps.forEach { timestamp -> - exoPlayer?.createMessage { _, payload -> - if (payload is EpisodeSkip.SkipStamp) // this should always be true - onTimestampInvoked?.invoke(payload) + exoPlayer?.createMessage { _, _ -> + updatedTime() + //if (payload is EpisodeSkip.SkipStamp) // this should always be true + // onTimestampInvoked?.invoke(payload) } ?.setLooper(Looper.getMainLooper()) ?.setPosition(timestamp.startMs) - ?.setPayload(timestamp) + //?.setPayload(timestamp) ?.setDeleteAfterDelivery(false) ?.send() } + updatedTime() } fun onRenderFirst() { 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 25925871..e1f1d99d 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 @@ -646,8 +646,8 @@ class GeneratorPlayer : FullScreenPlayer() { true, {}) { settingsManager.edit().putString( - ctx.getString(R.string.subtitles_encoding_key), prefValues[it] - ).apply() + ctx.getString(R.string.subtitles_encoding_key), prefValues[it] + ).apply() updateForcedEncoding(ctx) dismiss() @@ -869,7 +869,7 @@ class GeneratorPlayer : FullScreenPlayer() { if ((currentMeta as? ResultEpisode)?.tvType == TvType.NSFW) return 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) { hasRequestedStamps = true viewModel.loadStamps(duration) @@ -1151,8 +1151,13 @@ class GeneratorPlayer : FullScreenPlayer() { var timestampShowState = false + var skipAnimator: ValueAnimator? = null + var skipIndex = 0 + private fun displayTimeStamp(show: Boolean) { - if(timestampShowState == show) return + if (timestampShowState == show) return + skipIndex++ + println("displayTimeStamp = $show") timestampShowState = show skip_chapter_button?.apply { val showWidth = 170.toPx @@ -1163,14 +1168,14 @@ class GeneratorPlayer : FullScreenPlayer() { val to = if (show) showWidth else noShowWidth val from = if (!show) showWidth else noShowWidth + skipAnimator?.cancel() isVisible = true // just in case val lay = layoutParams lay.width = from layoutParams = lay - - ValueAnimator.ofInt( + skipAnimator = ValueAnimator.ofInt( from, to ).apply { addListener(onEnd = { @@ -1192,12 +1197,18 @@ class GeneratorPlayer : FullScreenPlayer() { displayTimeStamp(false) } - override fun onTimestamp(timestamp: EpisodeSkip.SkipStamp) { - skip_chapter_button.setText(timestamp.uiText) - displayTimeStamp(true) - skip_chapter_button?.handler?.postDelayed({ + override fun onTimestamp(timestamp: EpisodeSkip.SkipStamp?) { + if (timestamp != null) { + skip_chapter_button.setText(timestamp.uiText) + displayTimeStamp(true) + val currentIndex = skipIndex + skip_chapter_button?.handler?.postDelayed({ + if (skipIndex == currentIndex) + displayTimeStamp(false) + }, 6000) + } else { displayTimeStamp(false) - }, 6000) + } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 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 6c1f1108..ba5a4a85 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 @@ -126,7 +126,7 @@ interface IPlayer { subtitlesUpdates: (() -> Unit)? = null, // callback from player to inform that subtitles have updated in some way 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 + 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) ) 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 07233871..e9b69c5b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/AniSkip.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/AniSkip.kt @@ -8,6 +8,7 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.getMalId import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.ui.result.ResultEpisode import com.lagradost.cloudstream3.ui.result.txt +import java.lang.Long.min object EpisodeSkip { private const val TAG = "EpisodeSkip" @@ -37,14 +38,14 @@ object EpisodeSkip { private val cachedStamps = HashMap>() 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( data: LoadResponse, episode: ResultEpisode, episodeDurationMs: Long, - hasNextEpisode : Boolean, + hasNextEpisode: Boolean, ): List { cachedStamps[episode.id]?.let { list -> return list @@ -55,7 +56,14 @@ object EpisodeSkip { 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 (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) { "op" -> SkipType.Opening "ed" -> SkipType.Ending @@ -68,7 +76,10 @@ object EpisodeSkip { val start = (stamp.interval.startTime * 1000.0).toLong() SkipStamp( type = skipType, - skipToNextEpisode = hasNextEpisode && shouldSkipToNextEpisode(end, episodeDurationMs), + skipToNextEpisode = hasNextEpisode && shouldSkipToNextEpisode( + end, + dur + ), startMs = start, endMs = end ) @@ -87,7 +98,11 @@ object EpisodeSkip { // 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? { + suspend fun getResult( + malId: Int, + episodeNumber: Int, + episodeLength: Long + ): Pair>? { 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}" @@ -96,7 +111,7 @@ object AniSkip { val a = app.get(url) val res = a.parsed() 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) { Log.i(TAG, "error = ${t.message}") logError(t) From 7f91ea5cc661eb214bd813d43eec45af7327da3b Mon Sep 17 00:00:00 2001 From: reduplicated <110570621+reduplicated@users.noreply.github.com> Date: Sat, 5 Nov 2022 00:35:51 +0100 Subject: [PATCH 7/7] bump --- app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index df50d843..4a1fcada 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -48,7 +48,7 @@ android { targetSdk = 30 versionCode = 54 - versionName = "3.2.1" + versionName = "3.2.2" resValue("string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}")