From 56632d69224fee4f42e766678d2c7a209ff021d2 Mon Sep 17 00:00:00 2001 From: hexated Date: Wed, 14 Dec 2022 12:05:18 +0700 Subject: [PATCH] [Sora] added Crunchyroll --- SoraStream/build.gradle.kts | 2 +- .../main/kotlin/com/hexated/SoraExtractor.kt | 47 ++++++++++++++++ .../src/main/kotlin/com/hexated/SoraStream.kt | 53 +++++++++++++++---- 3 files changed, 92 insertions(+), 10 deletions(-) diff --git a/SoraStream/build.gradle.kts b/SoraStream/build.gradle.kts index 5b68dd52..f608a32d 100644 --- a/SoraStream/build.gradle.kts +++ b/SoraStream/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 54 +version = 55 cloudstream { diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt index 58e548ef..58c83e54 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt @@ -1562,6 +1562,51 @@ object SoraExtractor : SoraStream() { } + suspend fun invoCrunchyroll( + title: String? = null, + epsTitle: String? = null, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val id = app.get("$consumetCrunchyrollAPI/$title") + .parsedSafe()?.results?.find { + it.title.equals( + title, + true + ) && it.type.equals("series") + } ?: return + + val detail = app.get("$consumetCrunchyrollAPI/info?id=${id.id}&mediaType=series").text + val episodeId = tryParseJson(detail)?.episodes?.filter { + it.title.equals(epsTitle, true) && (it.type == "Subbed" || it.type == "English Dub") + }?.map { it.id to it.type } ?: return + + episodeId.apmap { (id, type) -> + delay(1000) + val json = app.get("$consumetCrunchyrollAPI/watch?episodeId=$id") + .parsedSafe() + + json?.sources?.map source@{ source -> + M3u8Helper.generateM3u8( + "Crunchyroll [$type]", + source.url ?: return@source null, + "", + ).forEach(callback) + } + + json?.subtitles?.map subtitle@{ sub -> + subtitleCallback.invoke( + SubtitleFile( + sub.lang?.replace(Regex("\\[\\S+]"), "")?.trim() ?: "", + sub.url ?: return@subtitle null + ) + ) + } + + } + + } + } class StreamM4u: XStreamCdn() { @@ -1662,6 +1707,8 @@ data class ConsumetSourcesResponse( data class ConsumetEpisodes( @JsonProperty("id") val id: String? = null, + @JsonProperty("type") val type: String? = null, + @JsonProperty("title") val title: String? = null, @JsonProperty("number") val number: Int? = null, @JsonProperty("season") val season: Int? = null, ) diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt index 8e7c8e68..3b43b474 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt @@ -1,6 +1,7 @@ package com.hexated import com.fasterxml.jackson.annotation.JsonProperty +import com.hexated.SoraExtractor.invoCrunchyroll import com.hexated.SoraExtractor.invoKisskh import com.hexated.SoraExtractor.invoke123Movie import com.hexated.SoraExtractor.invokeDbgo @@ -49,12 +50,15 @@ open class SoraStream : TmdbProvider() { /** AUTHOR : Hexated & Sora */ companion object { private const val tmdbAPI = "https://api.themoviedb.org/3" - private val apiKey = base64DecodeAPI("ZTM=NTg=MjM=MjM=ODc=MzI=OGQ=MmE=Nzk=Nzk=ZjI=NTA=NDY=NDA=MzA=YjA=") // PLEASE DON'T STEAL + private val apiKey = + base64DecodeAPI("ZTM=NTg=MjM=MjM=ODc=MzI=OGQ=MmE=Nzk=Nzk=ZjI=NTA=NDY=NDA=MzA=YjA=") // PLEASE DON'T STEAL const val tmdb2mal = "https://tmdb2mal.slidemovies.org" const val gdbot = "https://gdbot.xyz" - private val mainAPI = base64DecodeAPI("cHA=LmE=ZWw=cmM=dmU=aC4=dGM=d2E=eHA=Ly8=czo=dHA=aHQ=") -// private var mainServerAPI = base64DecodeAPI("cA==YXA=bC4=Y2U=ZXI=LnY=aWU=b3Y=LW0=cmE=c28=Ly8=czo=dHA=aHQ=") + private val mainAPI = + base64DecodeAPI("cHA=LmE=ZWw=cmM=dmU=aC4=dGM=d2E=eHA=Ly8=czo=dHA=aHQ=") + + // private var mainServerAPI = base64DecodeAPI("cA==YXA=bC4=Y2U=ZXI=LnY=aWU=b3Y=LW0=cmE=c28=Ly8=czo=dHA=aHQ=") const val twoEmbedAPI = "https://www.2embed.to" const val vidSrcAPI = "https://v2.vidsrc.me" const val dbgoAPI = "https://dbgo.fun" @@ -72,6 +76,7 @@ open class SoraStream : TmdbProvider() { const val xMovieAPI = "https://xemovies.to" const val consumetFlixhqAPI = "https://api.consumet.org/movies/flixhq" const val consumetZoroAPI = "https://api.consumet.org/anime/zoro" + const val consumetCrunchyrollAPI = "https://api.consumet.org/anime/crunchyroll" const val kissKhAPI = "https://kisskh.me" const val lingAPI = "https://ling-online.net" const val uhdmoviesAPI = "https://uhdmovies.site" @@ -221,7 +226,8 @@ open class SoraStream : TmdbProvider() { orgTitle = orgTitle, isAnime = isAnime, airedYear = year, - lastSeason = lastSeason + lastSeason = lastSeason, + epsTitle = eps.name, ).toJson(), name = eps.name, season = eps.seasonNumber, @@ -343,7 +349,15 @@ open class SoraStream : TmdbProvider() { ) }, { - if(!res.isAnime) invokeHDMovieBox(res.title, res.season, res.episode, callback) + if (res.season != null && res.isAnime) invoCrunchyroll( + res.title, + res.epsTitle, + subtitleCallback, + callback + ) + }, + { + if (!res.isAnime) invokeHDMovieBox(res.title, res.season, res.episode, callback) }, { invokeSeries9(res.title, res.season, res.episode, subtitleCallback, callback) @@ -411,7 +425,7 @@ open class SoraStream : TmdbProvider() { ) }, { - if(!res.isAnime) invokeUhdmovies( + if (!res.isAnime) invokeUhdmovies( res.title, res.year, res.season, @@ -425,13 +439,33 @@ open class SoraStream : TmdbProvider() { invokeFwatayako(res.imdbId, res.season, res.episode, subtitleCallback, callback) }, { - if(!res.isAnime) invokeGMovies(res.title, res.year, res.season, res.episode, subtitleCallback, callback) + if (!res.isAnime) invokeGMovies( + res.title, + res.year, + res.season, + res.episode, + subtitleCallback, + callback + ) }, { - if(!res.isAnime) invokeFDMovies(res.title, res.season, res.episode, subtitleCallback, callback) + if (!res.isAnime) invokeFDMovies( + res.title, + res.season, + res.episode, + subtitleCallback, + callback + ) }, { - invokeM4uhd(res.title, res.year, res.season, res.episode, subtitleCallback, callback) + invokeM4uhd( + res.title, + res.year, + res.season, + res.episode, + subtitleCallback, + callback + ) }, { invokeTvMovies(res.title, res.season, res.episode, subtitleCallback, callback) @@ -455,6 +489,7 @@ open class SoraStream : TmdbProvider() { val isAnime: Boolean = false, val airedYear: Int? = null, val lastSeason: Int? = null, + val epsTitle: String? = null, ) data class Data(