diff --git a/SoraStream/build.gradle.kts b/SoraStream/build.gradle.kts index fa53bfc6..714db815 100644 --- a/SoraStream/build.gradle.kts +++ b/SoraStream/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 70 +version = 71 cloudstream { diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt index 47aa53f8..c75bd903 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt @@ -520,28 +520,17 @@ object SoraExtractor : SoraStream() { "Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8" ) ).document - val srcm3u8 = - resDoc.selectFirst("script:containsData(let url =)")?.data()?.let { + val srcm3u8 = resDoc.selectFirst("script:containsData(let url =)")?.data()?.let { Regex("['|\"](.*?.m3u8)['|\"]").find(it)?.groupValues?.getOrNull(1) - } ?: return@apmap null - val quality = app.get( - srcm3u8, referer = source, headers = mapOf( - "Accept" to "*/*", - ) - ).text.let { quality -> - if (quality.contains("RESOLUTION=1920")) Qualities.P1080.value else Qualities.P720.value - } + } callback.invoke( ExtractorLink( "UniqueStream", "UniqueStream", - srcm3u8, + srcm3u8 ?: return@apmap null, source, - quality, + Qualities.P1080.value, true, - headers = mapOf( - "Accept" to "*/*", - ) ) ) } @@ -759,67 +748,71 @@ object SoraExtractor : SoraStream() { } } - suspend fun invokeSoraVIP( - title: String? = null, - year: Int? = null, - season: Int? = null, + private suspend fun invokeNetMovies( + id: String? = null, + type: String? = null, + episode: Int? = null, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val jsonResponse = app.get( + "$netMoviesAPI/detail?category=$type&id=$id", + ) + + if (!jsonResponse.isSuccessful) return + val epsId = jsonResponse.parsedSafe()?.data?.episodeVo?.find { + it.seriesNo == (episode ?: 0) + }?.id + + val sources = app.get("$netMoviesAPI/episode?category=$type&id=$id&episode=$epsId") + .parsedSafe()?.data ?: return + + sources.subtitles?.map { sub -> + subtitleCallback.invoke( + SubtitleFile( + sub.language ?: "", + sub.url ?: return@map null + ) + ) + } + + sources.qualities?.map { source -> + callback.invoke( + ExtractorLink( + "NetMovies", + "NetMovies", + source.url ?: return@map null, + "", + source.quality?.toIntOrNull() ?: Qualities.Unknown.value, + isM3u8 = true, + ) + ) + } + + } + + private suspend fun invokeLoklok( + id: String? = null, + type: String? = null, episode: Int? = null, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ) { val headers = mapOf( - "lang" to "en", "versioncode" to "11", "clienttype" to "ios_jike_default" + "lang" to "en", + "versioncode" to "11", + "clienttype" to "ios_jike_default" ) val vipAPI = base64DecodeAPI("cA==YXA=cy8=Y20=di8=LnQ=b2s=a2w=bG8=aS4=YXA=ZS0=aWw=b2I=LW0=Z2E=Ly8=czo=dHA=aHQ=") - val vipUrl = base64DecodeAPI("b20=LmM=b2s=a2w=bG8=Ly8=czo=dHA=aHQ=") - - val doc = app.get( - "$vipUrl/search?keyword=$title", - ).document - - val scriptData = doc.select("div.search-list div.search-video-card").map { - Triple( - it.selectFirst("h2.title")?.text().toString(), - it.selectFirst("div.desc")?.text()?.substringBefore(".")?.toIntOrNull(), - it.selectFirst("a")?.attr("href")?.split("/") - ) - } - - val script = if (scriptData.size == 1) { - scriptData.firstOrNull() - } else { - scriptData.find { - when (season) { - null -> { - it.first.equals( - title, true - ) && it.second == year - } - 1 -> { - it.first.contains( - "$title", true - ) && (it.second == year || it.first.contains("Season $season", true)) - } - else -> { - it.first.contains( - "$title", true - ) && it.second == year && it.first.contains("Season $season", true) - } - } - } - } - - val id = script?.third?.last() ?: return - val type = script.third?.get(2) ?: return val jsonResponse = app.get( - "$vipAPI/movieDrama/get?id=${id}&category=${type}", headers = headers + "$vipAPI/movieDrama/get?id=${id}&category=${type}", + headers = headers ) if (!jsonResponse.isSuccessful) return - - val json = jsonResponse.parsedSafe()?.data?.episodeVo?.first { + val json = jsonResponse.parsedSafe()?.data?.episodeVo?.find { it.seriesNo == (episode ?: 0) } @@ -855,6 +848,69 @@ object SoraExtractor : SoraStream() { } } + suspend fun invokeSoraStream( + title: String? = null, + year: Int? = null, + season: Int? = null, + episode: Int? = null, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val searchUrl = base64DecodeAPI("b20=LmM=b2s=a2w=bG8=Ly8=czo=dHA=aHQ=") + val doc = app.get( + "$searchUrl/search?keyword=$title", + ).document + + val scriptData = doc.select("div.search-list div.search-video-card").map { + Triple( + it.selectFirst("h2.title")?.text().toString(), + it.selectFirst("div.desc")?.text() + ?.substringBefore(".")?.toIntOrNull(), + it.selectFirst("a")?.attr("href")?.split("/") + ) + } + + val script = if (scriptData.size == 1) { + scriptData.firstOrNull() + } else { + scriptData.find { + when (season) { + null -> { + it.first.equals( + title, + true + ) && it.second == year + } + 1 -> { + it.first.contains( + "$title", + true + ) && (it.second == year || it.first.contains("Season $season", true)) + } + else -> { + it.first.contains( + "$title", + true + ) && it.second == year && it.first.contains("Season $season", true) + } + } + } + } + + val id = script?.third?.last() ?: return + val type = script.third?.get(2) ?: return + + argamap( + { + invokeLoklok(id, type, episode, subtitleCallback, callback) + }, + { + invokeNetMovies(id, type, episode, subtitleCallback, callback) + } + ) + + } + suspend fun invokeXmovies( title: String? = null, year: Int? = null, @@ -1060,13 +1116,7 @@ object SoraExtractor : SoraStream() { callback: (ExtractorLink) -> Unit ) { val malId = app.get("$tmdb2mal/?id=$id&s=$season").text.trim() - val anilistId = app.post( - "https://graphql.anilist.co/", data = mapOf( - "query" to "{Media(idMal:$malId,type:ANIME){id}}", - ) - ).parsedSafe()?.data?.media?.id - - val episodeId = app.get("$consumetAnilistAPI/info/$anilistId?provider=zoro") + val episodeId = app.get("$consumetMalAPI/info/$malId?provider=zoro") .parsedSafe()?.episodes?.find { it.number == episode }?.id?.substringBeforeLast("$") ?: return @@ -1204,14 +1254,31 @@ object SoraExtractor : SoraStream() { } }.filter { it.second?.contains(Regex("(https:)|(http:)")) == true } - val iframe = if (iframeList.any { it.first.contains("2160p", true) }) iframeList.filter { - it.first.contains( - "2160p", true - ) - } else iframeList.filter { it.first.contains("1080p", true) } + val sources = mutableListOf>() + if (iframeList.any { + it.first.contains( + "2160p", + true + ) + }) { + sources.addAll(iframeList.filter { + it.first.contains( + "2160p", + true + ) + }) + sources.add(iframeList.first { + it.first.contains( + "1080p", + true + ) + }) + } else { + sources.addAll(iframeList.filter { it.first.contains("1080p", true) }) + } val base = "https://drivebit.in" - iframe.apmap { (quality, link) -> + sources.apmap { (quality, link) -> delay(2000) val driveLink = bypassHrefli(link ?: return@apmap null) val res = app.get(driveLink ?: return@apmap null).document @@ -1721,7 +1788,7 @@ object SoraExtractor : SoraStream() { if(!app.get(url).isSuccessful) return - delay(5000) + delay(4000) callback.invoke( ExtractorLink( "RStream", @@ -1812,6 +1879,26 @@ data class Load( @JsonProperty("data") val data: MediaDetail? = null, ) +data class NetMoviesSubtitles( + @JsonProperty("lang") val lang: String? = null, + @JsonProperty("language") val language: String? = null, + @JsonProperty("url") val url: String? = null, +) + +data class NetMoviesQualities( + @JsonProperty("quality") val quality: String? = null, + @JsonProperty("url") val url: String? = null, +) + +data class NetMoviesData( + @JsonProperty("subtitles") val subtitles: ArrayList? = arrayListOf(), + @JsonProperty("qualities") val qualities: ArrayList? = arrayListOf(), +) + +data class NetMoviesSources( + @JsonProperty("data") val data: NetMoviesData? = null, +) + data class ConsumetHeaders( @JsonProperty("Referer") val referer: String? = null, ) @@ -1912,18 +1999,6 @@ data class PreviewVideos( @JsonProperty("currentDefinition") val currentDefinition: String? = null, ) -data class IdAni( - @JsonProperty("id") val id: String? = null, -) - -data class MediaAni( - @JsonProperty("Media") val media: IdAni? = null, -) - -data class DataAni( - @JsonProperty("data") val data: MediaAni? = null, -) - data class Safelink( @JsonProperty("safelink") val safelink: String? = null, ) diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt index 43c6469e..8e3ea651 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt @@ -12,7 +12,6 @@ import com.hexated.SoraExtractor.invokeKimcartoon import com.hexated.SoraExtractor.invokeMovieHab import com.hexated.SoraExtractor.invokeNoverse import com.hexated.SoraExtractor.invokeSeries9 -import com.hexated.SoraExtractor.invokeSoraVIP import com.hexated.SoraExtractor.invokeTwoEmbed import com.hexated.SoraExtractor.invokeUniqueStream import com.hexated.SoraExtractor.invokeVidSrc @@ -30,6 +29,7 @@ import com.hexated.SoraExtractor.invokeM4uhd import com.hexated.SoraExtractor.invokeMoviesbay import com.hexated.SoraExtractor.invokeMoviezAdd import com.hexated.SoraExtractor.invokeRStream +import com.hexated.SoraExtractor.invokeSoraStream import com.hexated.SoraExtractor.invokeTvMovies import com.hexated.SoraExtractor.invokeUhdmovies import com.hexated.SoraExtractor.invokeZoro @@ -58,12 +58,13 @@ open class SoraStream : TmdbProvider() { 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" - const val consumetAnilistAPI = "https://api.consumet.org/meta/anilist" + const val consumetMalAPI = "https://api.consumet.org/meta/mal" 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=") + var netMoviesAPI = base64DecodeAPI("aQ==YXA=cC8=YXA=bC4=Y2U=ZXI=LnY=bG0=Zmk=dC0=bmU=Ly8=czo=dHA=aHQ=") const val twoEmbedAPI = "https://www.2embed.to" const val vidSrcAPI = "https://v2.vidsrc.me" const val dbgoAPI = "https://dbgo.fun" @@ -75,7 +76,7 @@ open class SoraStream : TmdbProvider() { const val idlixAPI = "https://idlixian.com" const val noverseAPI = "https://www.nollyverse.com" const val olgplyAPI = "https://olgply.xyz" - const val uniqueStreamAPI = "https://uniquestream.net" + const val uniqueStreamAPI = "https://uniquestreaming.net" const val filmxyAPI = "https://www.filmxy.vip" const val kimcartoonAPI = "https://kimcartoon.li" const val xMovieAPI = "https://xemovies.to" @@ -305,7 +306,7 @@ open class SoraStream : TmdbProvider() { argamap( { - invokeSoraVIP( + invokeSoraStream( res.title, res.year, res.season,