From f6a6bc6997232a21553a3a43ced5b941a5cd6d4f Mon Sep 17 00:00:00 2001 From: Phisher98 Date: Thu, 2 Apr 2026 19:34:56 +0530 Subject: [PATCH 1/6] Adding Intro-DB --- .../ui/player/PlayerGeneratorViewModel.kt | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) 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 d8c5e777cfa..a1eed3b948d 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 @@ -16,6 +16,7 @@ import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.EpisodeSkip import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLinkType +import com.lagradost.cloudstream3.utils.IntroDbSkip import kotlinx.coroutines.Job import kotlinx.coroutines.launch @@ -179,14 +180,23 @@ class PlayerGeneratorViewModel : ViewModel() { val meta = generator?.getCurrent() val page = (generator as? RepoLinkGenerator?)?.page if (page != null && meta is ResultEpisode) { + val hasNext = hasNextEpisode() ?: false _currentStamps.postValue(listOf()) + val aniSkipStamps = EpisodeSkip.getStamps( + page, + meta, + duration, + hasNext + ) _currentStamps.postValue( - EpisodeSkip.getStamps( - page, - meta, - duration, - hasNextEpisode() ?: false - ) + aniSkipStamps.ifEmpty { + IntroDbSkip.getStamps( + page, + meta, + duration, + hasNext + ) + } ) } } @@ -242,4 +252,4 @@ class PlayerGeneratorViewModel : ViewModel() { } } -} \ No newline at end of file +} From 12137ae0b072d302e96fad338263e9385d1be927 Mon Sep 17 00:00:00 2001 From: Phisher98 Date: Thu, 2 Apr 2026 19:35:03 +0530 Subject: [PATCH 2/6] Adding Intro-DB --- .../cloudstream3/utils/IntroDbSkip.kt | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/utils/IntroDbSkip.kt diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/IntroDbSkip.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/IntroDbSkip.kt new file mode 100644 index 00000000000..d4e3a0ce32d --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/IntroDbSkip.kt @@ -0,0 +1,108 @@ +package com.lagradost.cloudstream3.utils + +import android.util.Log +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.lagradost.cloudstream3.LoadResponse +import com.lagradost.cloudstream3.LoadResponse.Companion.getImdbId +import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.mvvm.logError +import com.lagradost.cloudstream3.ui.result.ResultEpisode + +object IntroDbSkip { + private const val TAG = "IntroDb" + + private fun shouldSkipToNextEpisode(endMs: Long, episodeDurationMs: Long): Boolean { + return episodeDurationMs - endMs < 20_000L + } + + suspend fun getResult( + imdbId: String, + season: Int, + episode: Int, + ): IntroDbResponse? { + return try { + val url = + "https://api.introdb.app/segments?imdb_id=$imdbId&season=$season&episode=$episode&segment_type=intro" + Log.i(TAG, "Requesting $url") + app.get(url).parsed() + } catch (t: Throwable) { + Log.i(TAG, "error = ${t.message}") + logError(t) + null + } + } + + private fun Segment?.toSkipStamp( + type: EpisodeSkip.SkipType, + episodeDurationMs: Long, + hasNextEpisode: Boolean, + ): EpisodeSkip.SkipStamp? { + val segment = this ?: return null + val startMs = segment.startMs ?: return null + val endMs = segment.endMs ?: return null + return EpisodeSkip.SkipStamp( + type = type, + skipToNextEpisode = hasNextEpisode && shouldSkipToNextEpisode( + endMs, + episodeDurationMs + ), + startMs = startMs, + endMs = endMs + ) + } + + suspend fun getStamps( + data: LoadResponse, + episode: ResultEpisode, + episodeDurationMs: Long, + hasNextEpisode: Boolean, + ): List { + val season = episode.season ?: return emptyList() + val episodeNumber = episode.episode ?: return emptyList() + val imdbId = data.getImdbId() ?: return emptyList() + + val result = getResult( + imdbId = imdbId, + season = season, + episode = episodeNumber + ) ?: return emptyList() + + return listOfNotNull( + result.intro.toSkipStamp( + EpisodeSkip.SkipType.Opening, + episodeDurationMs, + hasNextEpisode + ), + result.recap.toSkipStamp( + EpisodeSkip.SkipType.Recap, + episodeDurationMs, + hasNextEpisode + ), + result.outro.toSkipStamp( + EpisodeSkip.SkipType.Credits, + episodeDurationMs, + hasNextEpisode + ) + ) + } + + data class IntroDbResponse( + @JsonProperty("imdb_id") @JsonSerialize val imdbId: String?, + @JsonSerialize val season: Int?, + @JsonSerialize val episode: Int?, + @JsonSerialize val intro: Segment?, + @JsonSerialize val recap: Segment?, + @JsonSerialize val outro: Segment?, + ) + + data class Segment( + @JsonProperty("start_sec") @JsonSerialize val startSec: Double?, + @JsonProperty("end_sec") @JsonSerialize val endSec: Double?, + @JsonProperty("start_ms") @JsonSerialize val startMs: Long?, + @JsonProperty("end_ms") @JsonSerialize val endMs: Long?, + @JsonSerialize val confidence: Double?, + @JsonProperty("submission_count") @JsonSerialize val submissionCount: Int?, + @JsonProperty("updated_at") @JsonSerialize val updatedAt: String?, + ) +} From d8cbb9a36e8154fcc2fdc05cee095515f96b98f9 Mon Sep 17 00:00:00 2001 From: Phisher98 Date: Thu, 2 Apr 2026 19:43:15 +0530 Subject: [PATCH 3/6] Log import changed. --- .../java/com/lagradost/cloudstream3/utils/IntroDbSkip.kt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/IntroDbSkip.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/IntroDbSkip.kt index d4e3a0ce32d..a523fccb092 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/IntroDbSkip.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/IntroDbSkip.kt @@ -1,8 +1,8 @@ package com.lagradost.cloudstream3.utils -import android.util.Log import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.lagradost.api.Log import com.lagradost.cloudstream3.LoadResponse import com.lagradost.cloudstream3.LoadResponse.Companion.getImdbId import com.lagradost.cloudstream3.app @@ -22,9 +22,7 @@ object IntroDbSkip { episode: Int, ): IntroDbResponse? { return try { - val url = - "https://api.introdb.app/segments?imdb_id=$imdbId&season=$season&episode=$episode&segment_type=intro" - Log.i(TAG, "Requesting $url") + val url = "https://api.introdb.app/segments?imdb_id=$imdbId&season=$season&episode=$episode&segment_type=intro" app.get(url).parsed() } catch (t: Throwable) { Log.i(TAG, "error = ${t.message}") @@ -59,7 +57,7 @@ object IntroDbSkip { hasNextEpisode: Boolean, ): List { val season = episode.season ?: return emptyList() - val episodeNumber = episode.episode ?: return emptyList() + val episodeNumber = episode.episode val imdbId = data.getImdbId() ?: return emptyList() val result = getResult( From 1b3c5c53dd26f45dfbbeb86c40c10d3fdd963602 Mon Sep 17 00:00:00 2001 From: Phisher98 Date: Thu, 2 Apr 2026 23:39:25 +0530 Subject: [PATCH 4/6] Fixes --- .../ui/player/PlayerGeneratorViewModel.kt | 22 +--- .../lagradost/cloudstream3/utils/AniSkip.kt | 91 +++++++++++++++ .../cloudstream3/utils/IntroDbSkip.kt | 106 ------------------ 3 files changed, 97 insertions(+), 122 deletions(-) delete mode 100644 app/src/main/java/com/lagradost/cloudstream3/utils/IntroDbSkip.kt 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 a1eed3b948d..632a6fd0f17 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 @@ -16,7 +16,6 @@ import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.EpisodeSkip import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLinkType -import com.lagradost.cloudstream3.utils.IntroDbSkip import kotlinx.coroutines.Job import kotlinx.coroutines.launch @@ -180,23 +179,14 @@ class PlayerGeneratorViewModel : ViewModel() { val meta = generator?.getCurrent() val page = (generator as? RepoLinkGenerator?)?.page if (page != null && meta is ResultEpisode) { - val hasNext = hasNextEpisode() ?: false _currentStamps.postValue(listOf()) - val aniSkipStamps = EpisodeSkip.getStamps( - page, - meta, - duration, - hasNext - ) _currentStamps.postValue( - aniSkipStamps.ifEmpty { - IntroDbSkip.getStamps( - page, - meta, - duration, - hasNext - ) - } + 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 820a01f9f40..ff25833dcf4 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/AniSkip.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/AniSkip.kt @@ -2,8 +2,10 @@ package com.lagradost.cloudstream3.utils import android.util.Log import androidx.annotation.StringRes +import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.databind.annotation.JsonSerialize import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.LoadResponse.Companion.getImdbId import com.lagradost.cloudstream3.LoadResponse.Companion.getMalId import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.ui.result.ResultEpisode @@ -86,7 +88,57 @@ object EpisodeSkip { out.addAll(list) } } + } else if (data.type == TvType.TvSeries) { + val season = episode.season + val imdbId = data.getImdbId() + + if (season != null && imdbId != null) { + val result = IntroDbSkip.getResult( + imdbId, + season, + episode.episode + ) + + result?.let { res -> + listOfNotNull( + res.intro?.let { + val start = it.startMs ?: return@let null + val end = it.endMs ?: return@let null + SkipStamp( + type = SkipType.Opening, + skipToNextEpisode = hasNextEpisode && + shouldSkipToNextEpisode(end, episodeDurationMs), + startMs = start, + endMs = end + ) + }, + res.recap?.let { + val start = it.startMs ?: return@let null + val end = it.endMs ?: return@let null + SkipStamp( + type = SkipType.Recap, + skipToNextEpisode = hasNextEpisode && + shouldSkipToNextEpisode(end, episodeDurationMs), + startMs = start, + endMs = end + ) + }, + res.outro?.let { + val start = it.startMs ?: return@let null + val end = it.endMs ?: return@let null + SkipStamp( + type = SkipType.Credits, + skipToNextEpisode = hasNextEpisode && + shouldSkipToNextEpisode(end, episodeDurationMs), + startMs = start, + endMs = end + ) + } + ).let { out.addAll(it) } + } + } } + if (out.isNotEmpty()) cachedStamps[episode.id] = out return out @@ -136,4 +188,43 @@ object AniSkip { @JsonSerialize val startTime: Double, @JsonSerialize val endTime: Double ) +} + +object IntroDbSkip { + private const val TAG = "IntroDb" + + suspend fun getResult( + imdbId: String, + season: Int, + episode: Int, + ): IntroDbResponse? { + return try { + val url = + "https://api.introdb.app/segments?imdb_id=$imdbId&season=$season&episode=$episode" + app.get(url).parsed() + } catch (t: Throwable) { + Log.i(TAG, "error = ${t.message}") + logError(t) + null + } + } + + data class IntroDbResponse( + @JsonProperty("imdb_id") val imdbId: String?, + val season: Int?, + val episode: Int?, + val intro: Segment?, + val recap: Segment?, + val outro: Segment?, + ) + + data class Segment( + @JsonProperty("start_sec") val startSec: Double?, + @JsonProperty("end_sec") val endSec: Double?, + @JsonProperty("start_ms") val startMs: Long?, + @JsonProperty("end_ms") val endMs: Long?, + val confidence: Double?, + @JsonProperty("submission_count") val submissionCount: Int?, + @JsonProperty("updated_at") val updatedAt: String?, + ) } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/IntroDbSkip.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/IntroDbSkip.kt deleted file mode 100644 index a523fccb092..00000000000 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/IntroDbSkip.kt +++ /dev/null @@ -1,106 +0,0 @@ -package com.lagradost.cloudstream3.utils - -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.databind.annotation.JsonSerialize -import com.lagradost.api.Log -import com.lagradost.cloudstream3.LoadResponse -import com.lagradost.cloudstream3.LoadResponse.Companion.getImdbId -import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.mvvm.logError -import com.lagradost.cloudstream3.ui.result.ResultEpisode - -object IntroDbSkip { - private const val TAG = "IntroDb" - - private fun shouldSkipToNextEpisode(endMs: Long, episodeDurationMs: Long): Boolean { - return episodeDurationMs - endMs < 20_000L - } - - suspend fun getResult( - imdbId: String, - season: Int, - episode: Int, - ): IntroDbResponse? { - return try { - val url = "https://api.introdb.app/segments?imdb_id=$imdbId&season=$season&episode=$episode&segment_type=intro" - app.get(url).parsed() - } catch (t: Throwable) { - Log.i(TAG, "error = ${t.message}") - logError(t) - null - } - } - - private fun Segment?.toSkipStamp( - type: EpisodeSkip.SkipType, - episodeDurationMs: Long, - hasNextEpisode: Boolean, - ): EpisodeSkip.SkipStamp? { - val segment = this ?: return null - val startMs = segment.startMs ?: return null - val endMs = segment.endMs ?: return null - return EpisodeSkip.SkipStamp( - type = type, - skipToNextEpisode = hasNextEpisode && shouldSkipToNextEpisode( - endMs, - episodeDurationMs - ), - startMs = startMs, - endMs = endMs - ) - } - - suspend fun getStamps( - data: LoadResponse, - episode: ResultEpisode, - episodeDurationMs: Long, - hasNextEpisode: Boolean, - ): List { - val season = episode.season ?: return emptyList() - val episodeNumber = episode.episode - val imdbId = data.getImdbId() ?: return emptyList() - - val result = getResult( - imdbId = imdbId, - season = season, - episode = episodeNumber - ) ?: return emptyList() - - return listOfNotNull( - result.intro.toSkipStamp( - EpisodeSkip.SkipType.Opening, - episodeDurationMs, - hasNextEpisode - ), - result.recap.toSkipStamp( - EpisodeSkip.SkipType.Recap, - episodeDurationMs, - hasNextEpisode - ), - result.outro.toSkipStamp( - EpisodeSkip.SkipType.Credits, - episodeDurationMs, - hasNextEpisode - ) - ) - } - - data class IntroDbResponse( - @JsonProperty("imdb_id") @JsonSerialize val imdbId: String?, - @JsonSerialize val season: Int?, - @JsonSerialize val episode: Int?, - @JsonSerialize val intro: Segment?, - @JsonSerialize val recap: Segment?, - @JsonSerialize val outro: Segment?, - ) - - data class Segment( - @JsonProperty("start_sec") @JsonSerialize val startSec: Double?, - @JsonProperty("end_sec") @JsonSerialize val endSec: Double?, - @JsonProperty("start_ms") @JsonSerialize val startMs: Long?, - @JsonProperty("end_ms") @JsonSerialize val endMs: Long?, - @JsonSerialize val confidence: Double?, - @JsonProperty("submission_count") @JsonSerialize val submissionCount: Int?, - @JsonProperty("updated_at") @JsonSerialize val updatedAt: String?, - ) -} From 992d7d1576a14ee7e90c708b2ef481d4cf29bfcd Mon Sep 17 00:00:00 2001 From: Phisher98 Date: Thu, 2 Apr 2026 23:42:47 +0530 Subject: [PATCH 5/6] Fixes --- .../cloudstream3/ui/player/PlayerGeneratorViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 632a6fd0f17..d8c5e777cfa 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 @@ -242,4 +242,4 @@ class PlayerGeneratorViewModel : ViewModel() { } } -} +} \ No newline at end of file From 57688c9f8de553be7e42c34a327bb53c67fbd9a4 Mon Sep 17 00:00:00 2001 From: Phisher98 Date: Thu, 2 Apr 2026 23:55:33 +0530 Subject: [PATCH 6/6] Fixes --- app/src/main/java/com/lagradost/cloudstream3/utils/AniSkip.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ff25833dcf4..bbdadbf3f5a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/AniSkip.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/AniSkip.kt @@ -88,7 +88,7 @@ object EpisodeSkip { out.addAll(list) } } - } else if (data.type == TvType.TvSeries) { + } else if (data.type == TvType.TvSeries || data.type == TvType.AsianDrama) { val season = episode.season val imdbId = data.getImdbId()