From b6ffffa34b4e3dfbe6bc926282f7098520c2ed1a Mon Sep 17 00:00:00 2001 From: hexated Date: Thu, 22 Dec 2022 04:49:12 +0700 Subject: [PATCH] [Sora] fixed missing anime in zoro and crunchy --- SoraStream/build.gradle.kts | 2 +- .../main/kotlin/com/hexated/SoraExtractor.kt | 284 +++++++----------- .../src/main/kotlin/com/hexated/SoraStream.kt | 16 +- .../src/main/kotlin/com/hexated/SoraUtils.kt | 2 +- 4 files changed, 119 insertions(+), 185 deletions(-) diff --git a/SoraStream/build.gradle.kts b/SoraStream/build.gradle.kts index 66f45b7c..06eaf774 100644 --- a/SoraStream/build.gradle.kts +++ b/SoraStream/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 60 +version = 61 cloudstream { diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt index bb582cea..0229843d 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt @@ -80,8 +80,7 @@ object SoraExtractor : SoraStream() { document.select(".dropdown-menu a[data-id]").map { it.attr("data-id") }.apmap { serverID -> val token = APIHolder.getCaptchaToken(url, captchaKey) app.get( - "$twoEmbedAPI/ajax/embed/play?id=$serverID&_token=$token", - referer = url + "$twoEmbedAPI/ajax/embed/play?id=$serverID&_token=$token", referer = url ).parsedSafe()?.let { source -> val link = source.link ?: return@let if (link.contains("rabbitstream")) { @@ -92,17 +91,13 @@ object SoraExtractor : SoraStream() { ).parsedSafe()?.tracks?.map { sub -> subtitleCallback.invoke( SubtitleFile( - sub.label.toString(), - sub.file ?: return@map null + sub.label.toString(), sub.file ?: return@map null ) ) } } else { loadExtractor( - link, - twoEmbedAPI, - subtitleCallback, - callback + link, twoEmbedAPI, subtitleCallback, callback ) } } @@ -144,8 +139,7 @@ object SoraExtractor : SoraStream() { episode: Int? = null, callback: (ExtractorLink) -> Unit ) { - val url = - "$olgplyAPI/${id}${season?.let { "/$it" } ?: ""}${episode?.let { "/$it" } ?: ""}" + val url = "$olgplyAPI/${id}${season?.let { "/$it" } ?: ""}${episode?.let { "/$it" } ?: ""}" loadLinksWithWebView(url, callback) } @@ -167,13 +161,11 @@ object SoraExtractor : SoraStream() { val doc = app.get("$dbgoAPI/tv-imdb.php?id=$id&s=$season").document iframeDbgo = doc.select("div.myvideo iframe").attr("src") val token = app.get( - iframeDbgo, - referer = "$dbgoAPI/" + iframeDbgo, referer = "$dbgoAPI/" ).document.selectFirst("select#translator-name option")?.attr("data-token") app.get("https://voidboost.net/serial/$token/iframe?s=$season&e=$episode&h=dbgo.fun").document.select( "script" - ) - .find { it.data().contains("CDNplayerConfig =") }?.data() + ).find { it.data().contains("CDNplayerConfig =") }?.data() } val source = @@ -189,34 +181,30 @@ object SoraExtractor : SoraStream() { decryptStreamUrl(source).split(",").map { links -> val quality = Regex("\\[([0-9]*p.*?)]").find(links)?.groupValues?.getOrNull(1).toString().trim() - links.replace("[$quality]", "").split(" or ").map { it.trim() } - .map { link -> - val name = if (link.contains(".m3u8")) "Dbgo (Main)" else "Dbgo (Backup)" - callback.invoke( - ExtractorLink( - name, - name, - link, - "$ref/", - getQuality(quality), - isM3u8 = link.contains(".m3u8"), - headers = mapOf( - "Origin" to ref - ) + links.replace("[$quality]", "").split(" or ").map { it.trim() }.map { link -> + val name = if (link.contains(".m3u8")) "Dbgo (Main)" else "Dbgo (Backup)" + callback.invoke( + ExtractorLink( + name, + name, + link, + "$ref/", + getQuality(quality), + isM3u8 = link.contains(".m3u8"), + headers = mapOf( + "Origin" to ref ) ) - } + ) + } } subtitle?.split(",")?.map { sub -> - val language = - Regex("\\[(.*)]").find(sub)?.groupValues?.getOrNull(1) - .toString() + val language = Regex("\\[(.*)]").find(sub)?.groupValues?.getOrNull(1).toString() val link = sub.replace("[$language]", "").trim() subtitleCallback.invoke( SubtitleFile( - language, - link + language, link ) ) } @@ -250,10 +238,7 @@ object SoraExtractor : SoraStream() { } }.apmap { link -> loadExtractor( - link, - "https://123moviesjr.cc/", - subtitleCallback, - callback + link, "https://123moviesjr.cc/", subtitleCallback, callback ) } } @@ -282,10 +267,7 @@ object SoraExtractor : SoraStream() { headers = mapOf("X-Requested-With" to "XMLHttpRequest") ).parsedSafe()?.data?.let { res -> loadExtractor( - res.link ?: return@let null, - movieHabAPI, - subtitleCallback, - callback + res.link ?: return@let null, movieHabAPI, subtitleCallback, callback ) } } @@ -387,8 +369,7 @@ object SoraExtractor : SoraStream() { ?.attr("src") val script = app.get( - iframe ?: return, - referer = "$hdMovieBoxAPI/" + iframe ?: return, referer = "$hdMovieBoxAPI/" ).document.selectFirst("script:containsData(var vhash =)")?.data() ?.substringAfter("vhash, {")?.substringBefore("}, false") @@ -399,8 +380,7 @@ object SoraExtractor : SoraStream() { base64Encode(source.videoDisk.toString().toByteArray()) } val link = getBaseUrl(iframe) + source?.videoUrl?.replace( - "\\", - "" + "\\", "" ) + "?s=${source?.videoServer}&d=$disk" callback.invoke( ExtractorLink( @@ -485,15 +465,9 @@ object SoraExtractor : SoraStream() { it.attr("data-nume") }.apmap { nume -> val source = app.post( - url = "$referer/wp-admin/admin-ajax.php", - data = mapOf( - "action" to "doo_player_ajax", - "post" to id, - "nume" to nume, - "type" to type - ), - headers = mapOf("X-Requested-With" to "XMLHttpRequest"), - referer = url + url = "$referer/wp-admin/admin-ajax.php", data = mapOf( + "action" to "doo_player_ajax", "post" to id, "nume" to nume, "type" to type + ), headers = mapOf("X-Requested-With" to "XMLHttpRequest"), referer = url ).parsed().embed_url if (!source.contains("youtube")) { @@ -525,15 +499,9 @@ object SoraExtractor : SoraStream() { val id = el.attr("data-post") val nume = el.attr("data-nume") val source = app.post( - url = "$uniqueStreamAPI/wp-admin/admin-ajax.php", - data = mapOf( - "action" to "doo_player_ajax", - "post" to id, - "nume" to nume, - "type" to type - ), - headers = mapOf("X-Requested-With" to "XMLHttpRequest"), - referer = url + url = "$uniqueStreamAPI/wp-admin/admin-ajax.php", data = mapOf( + "action" to "doo_player_ajax", "post" to id, "nume" to nume, "type" to type + ), headers = mapOf("X-Requested-With" to "XMLHttpRequest"), referer = url ).parsed().embed_url.let { fixUrl(it) } when { @@ -569,10 +537,7 @@ object SoraExtractor : SoraStream() { ) } !source.contains("youtube") -> loadExtractor( - source, - "$uniqueStreamAPI/", - subtitleCallback, - callback + source, "$uniqueStreamAPI/", subtitleCallback, callback ) else -> { // pass @@ -602,8 +567,7 @@ object SoraExtractor : SoraStream() { } } else { doc.select("table.table-striped tbody tr") - .find { it.text().contains("Episode $episode") } - ?.select("td")?.map { + .find { it.text().contains("Episode $episode") }?.select("td")?.map { it.select("a").attr("href") to it.select("a").text() } } ?: return @@ -771,9 +735,7 @@ object SoraExtractor : SoraStream() { 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=") @@ -786,8 +748,7 @@ object SoraExtractor : SoraStream() { 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("div.desc")?.text()?.substringBefore(".")?.toIntOrNull(), it.selectFirst("a")?.attr("href")?.split("/") ) } @@ -799,20 +760,17 @@ object SoraExtractor : SoraStream() { when (season) { null -> { it.first.equals( - title, - true + title, true ) && it.second == year } 1 -> { it.first.contains( - "$title", - true + "$title", true ) && (it.second == year || it.first.contains("Season $season", true)) } else -> { it.first.contains( - "$title", - true + "$title", true ) && it.second == year && it.first.contains("Season $season", true) } } @@ -823,8 +781,7 @@ object SoraExtractor : SoraStream() { 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 @@ -836,8 +793,7 @@ object SoraExtractor : SoraStream() { json?.subtitlingList?.map { sub -> subtitleCallback.invoke( SubtitleFile( - sub.language ?: "", - sub.subtitlingUrl ?: return@map + sub.language ?: "", sub.subtitlingUrl ?: return@map ) ) } @@ -908,18 +864,16 @@ object SoraExtractor : SoraStream() { callback: (ExtractorLink) -> Unit ) { val fixTitle = title?.replace("–", "-") - val id = - app.get("$consumetFlixhqAPI/$title") - .parsedSafe()?.results?.find { - if (season == null) { - it.title?.equals( - "$fixTitle", - true - ) == true && it.releaseDate?.equals("$year") == true && it.type == "Movie" - } else { - it.title?.equals("$fixTitle", true) == true && it.type == "TV Series" - } - }?.id ?: return + val id = app.get("$consumetFlixhqAPI/$title") + .parsedSafe()?.results?.find { + if (season == null) { + it.title?.equals( + "$fixTitle", true + ) == true && it.releaseDate?.equals("$year") == true && it.type == "Movie" + } else { + it.title?.equals("$fixTitle", true) == true && it.type == "TV Series" + } + }?.id ?: return val episodeId = app.get("$consumetFlixhqAPI/info?id=$id").parsedSafe()?.let { @@ -931,16 +885,12 @@ object SoraExtractor : SoraStream() { } ?: return listOf( - "vidcloud", - "upcloud" + "vidcloud", "upcloud" ).apmap { server -> - delay(1000) - val sources = - app.get( - "$consumetFlixhqAPI/watch?episodeId=$episodeId&mediaId=$id&server=$server", - timeout = 120L - ) - .parsedSafe() + val sources = app.get( + "$consumetFlixhqAPI/watch?episodeId=$episodeId&mediaId=$id&server=$server", + timeout = 120L + ).parsedSafe() val name = fixTitle(server) sources?.sources?.map { callback.invoke( @@ -958,8 +908,7 @@ object SoraExtractor : SoraStream() { sources?.subtitles?.map { subtitleCallback.invoke( SubtitleFile( - it.lang ?: "", - it.url ?: return@map null + it.lang ?: "", it.url ?: return@map null ) ) } @@ -978,8 +927,7 @@ object SoraExtractor : SoraStream() { ) { val fixTitle = title?.replace("–", "-") val res = app.get( - "$kissKhAPI/api/DramaList/Search?q=$title&type=0", - referer = "$kissKhAPI/" + "$kissKhAPI/api/DramaList/Search?q=$title&type=0", referer = "$kissKhAPI/" ).text.let { tryParseJson>(it) } ?: return @@ -993,8 +941,7 @@ object SoraExtractor : SoraStream() { } else { val data = res.find { it.title?.contains( - "$fixTitle", - true + "$fixTitle", true ) == true && it.title.contains("Season $season", true) } data?.id to data?.title @@ -1002,8 +949,7 @@ object SoraExtractor : SoraStream() { } val resDetail = app.get( - "$kissKhAPI/api/DramaList/Drama/$id?isq=false", - referer = "$kissKhAPI/Drama/${ + "$kissKhAPI/api/DramaList/Drama/$id?isq=false", referer = "$kissKhAPI/Drama/${ getKisskhTitle(contentTitle) }?id=$id" ).parsedSafe() ?: return @@ -1042,8 +988,7 @@ object SoraExtractor : SoraStream() { tryParseJson>(resSub)?.map { sub -> subtitleCallback.invoke( SubtitleFile( - getLanguage(sub.label ?: return@map), - sub.src ?: return@map + getLanguage(sub.label ?: return@map), sub.src ?: return@map ) ) } @@ -1060,27 +1005,17 @@ object SoraExtractor : SoraStream() { callback: (ExtractorLink) -> Unit ) { val malId = app.get("$tmdb2mal/?id=$id&s=$season").text.trim() - val (engTitle, japTitle) = app.get("https://animixplay.to/assets/mal/$malId.json") - .parsedSafe()?.let { - it.title_english to it.title - } ?: return + val anilistId = app.post( + "https://graphql.anilist.co/", data = mapOf( + "query" to "{Media(idMal:$malId,type:ANIME){id}}", + ) + ).parsedSafe()?.data?.media?.id - val animeId = - app.get("$consumetZoroAPI/$japTitle") - .parsedSafe()?.results?.find { - val title = it.title ?: return - (title.equals(engTitle, true) || title.equals( - japTitle, - true - )) && it.type == "TV" - }?.id ?: return - - val episodeId = app.get("$consumetZoroAPI/info?id=$animeId") + val episodeId = app.get("$consumetAnilistAPI/info/$anilistId?provider=zoro") .parsedSafe()?.episodes?.find { it.number == episode }?.id ?: return - delay(1000) val sources = app.get("$consumetZoroAPI/watch?episodeId=$episodeId", timeout = 120L) .parsedSafe() ?: return @@ -1137,8 +1072,7 @@ object SoraExtractor : SoraStream() { } else { scriptData.find { it.first?.contains( - "$fixTitle", - true + "$fixTitle", true ) == true && it.second?.contains("$year") == true } } @@ -1155,12 +1089,7 @@ object SoraExtractor : SoraStream() { val link = Regex("((https:|http:)//.*\\.mp4)").find(source.text)?.value ?: return callback.invoke( ExtractorLink( - "Ling", - "Ling", - link, - "$lingAPI/", - Qualities.Unknown.value, - headers = mapOf( + "Ling", "Ling", link, "$lingAPI/", Qualities.Unknown.value, headers = mapOf( "Range" to "bytes=0-" ) ) @@ -1203,8 +1132,7 @@ object SoraExtractor : SoraStream() { val detailDoc = app.get(script?.first ?: return).document val iframeList = detailDoc.select("div.entry-content p").map { it } - .filter { it.text().filterIframe(season, lastSeason, year) } - .mapNotNull { + .filter { it.text().filterIframe(season, lastSeason, year) }.mapNotNull { if (season == null) { it.text() to it.nextElementSibling()?.select("a")?.attr("href") } else { @@ -1215,8 +1143,7 @@ object SoraExtractor : SoraStream() { val iframe = if (iframeList.any { it.first.contains("2160p", true) }) iframeList.filter { it.first.contains( - "2160p", - true + "2160p", true ) } else iframeList.filter { it.first.contains("1080p", true) } @@ -1270,14 +1197,13 @@ object SoraExtractor : SoraStream() { ) { val request = app.get("$fwatayakoAPI/IAF0wWTdNYZm?imdb_id=$imdbId") if (!request.isSuccessful) return - val files = request.document.selectFirst("input#files")?.attr("value") - .let { - if (season == null) { - it?.replace("\"381\"", "\"movie\"") - } else { - it?.replace("\"381\"", "\"tv\"") - } - }.let { tryParseJson(it) } ?: return + val files = request.document.selectFirst("input#files")?.attr("value").let { + if (season == null) { + it?.replace("\"381\"", "\"movie\"") + } else { + it?.replace("\"381\"", "\"tv\"") + } + }.let { tryParseJson(it) } ?: return val sourcesLink = if (season == null) { files.sourcesMovie @@ -1332,8 +1258,7 @@ object SoraExtractor : SoraStream() { } })?.filter { it.first.contains("gdtot") && (it.second.contains( - "1080p", - true + "1080p", true ) || it.second.contains("4k", true)) } ?: return @@ -1380,8 +1305,7 @@ object SoraExtractor : SoraStream() { ) }.filter { (it.quality.contains("1080p", true) || it.quality.contains( - "4k", - true + "4k", true )) && (it.type.contains("gdtot") || it.type.contains("oiya")) } Log.i("hexated", "$iframe") @@ -1436,8 +1360,7 @@ object SoraExtractor : SoraStream() { } else { scriptData.find { it.first?.contains( - "Watch Free ${title?.replace(":", "")}", - true + "Watch Free ${title?.replace(":", "")}", true ) == true && (it.first?.contains("$year") == true || it.second?.contains( "$year" ) == true) @@ -1464,16 +1387,11 @@ object SoraExtractor : SoraStream() { ?: return val idepisode = episodeData.select("button").attr("idepisode") ?: return val requestEmbed = app.post( - "$m4uhdAPI/ajaxtv", - data = mapOf( - "idepisode" to idepisode, - "_token" to "$token" - ), - referer = link, - headers = mapOf( + "$m4uhdAPI/ajaxtv", data = mapOf( + "idepisode" to idepisode, "_token" to "$token" + ), referer = link, headers = mapOf( "X-Requested-With" to "XMLHttpRequest", - ), - cookies = mapOf( + ), cookies = mapOf( "laravel_session" to "$session", "XSRF-TOKEN" to "$xsrf", ) @@ -1491,8 +1409,7 @@ object SoraExtractor : SoraStream() { val iframe = app.post( "$m4uhdAPI/ajax", data = mapOf( - "m4u" to m4uData, - "_token" to "$token" + "m4u" to m4uData, "_token" to "$token" ), referer = link, headers = mapOf( @@ -1525,7 +1442,8 @@ object SoraExtractor : SoraStream() { val server = getTvMoviesServer(url, season, episode) ?: return val videoData = extractCovyn(server.second ?: return) - val quality = Regex("([0-9]{3,4})p").find(server.first)?.groupValues?.getOrNull(1)?.toIntOrNull() + val quality = + Regex("([0-9]{3,4})p").find(server.first)?.groupValues?.getOrNull(1)?.toIntOrNull() callback.invoke( ExtractorLink( @@ -1543,24 +1461,30 @@ object SoraExtractor : SoraStream() { suspend fun invokeCrunchyroll( title: String? = null, epsTitle: String? = null, + season: Int? = null, + episode: Int? = null, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ) { val id = app.get("$consumetCrunchyrollAPI/$title") .parsedSafe()?.results?.find { it.title.equals( - title, - true + 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") + (if (season == 1) { + it.title.equals( + epsTitle, true + ) || it.number == episode + } else { + 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&format=srt") .parsedSafe() @@ -1758,4 +1682,16 @@ data class PreviewResponse( data class PreviewVideos( @JsonProperty("mediaUrl") val mediaUrl: String? = null, @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, ) \ No newline at end of file diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt index ff201016..e1ae6536 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt @@ -54,6 +54,7 @@ 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" private val mainAPI = base64DecodeAPI("cHA=LmE=ZWw=cmM=dmU=aC4=dGM=d2E=eHA=Ly8=czo=dHA=aHQ=") @@ -79,7 +80,7 @@ open class SoraStream : TmdbProvider() { 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" + const val uhdmoviesAPI = "https://uhdmovies.org.in" const val fwatayakoAPI = "https://5100.svetacdn.in" const val gMoviesAPI = "https://gdrivemovies.xyz" const val fdMoviesAPI = "https://freedrivemovie.com" @@ -162,18 +163,13 @@ open class SoraStream : TmdbProvider() { } } - override suspend fun search(query: String): List { - val searchResponse = mutableListOf() - - val mainResponse = app.get( + override suspend fun search(query: String): List? { + return app.get( "$tmdbAPI/search/multi?api_key=$apiKey&language=en-US&query=$query&page=1&include_adult=${settingsForProvider.enableAdult}", referer = "$mainAPI/" ).parsedSafe()?.results?.mapNotNull { media -> media.toSearchResponse() } - if (mainResponse?.isNotEmpty() == true) searchResponse.addAll(mainResponse) - - return searchResponse } override suspend fun load(url: String): LoadResponse? { @@ -246,7 +242,7 @@ open class SoraStream : TmdbProvider() { newTvSeriesLoadResponse( title, url, - TvType.TvSeries, + if(isAnime) TvType.Anime else TvType.TvSeries, episodes ) { this.posterUrl = poster @@ -355,6 +351,8 @@ open class SoraStream : TmdbProvider() { if (res.season != null && res.isAnime) invokeCrunchyroll( res.title, res.epsTitle, + res.season, + res.episode, subtitleCallback, callback ) diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt b/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt index e03acef6..03611b30 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt @@ -174,7 +174,7 @@ suspend fun extractGdflix(url: String): String? { ?.attr("href") ?: return null val base = getBaseUrl(iframeGdflix) - val gdfDoc = app.get(iframeGdflix).document.selectFirst("script")?.data()?.substringAfter("replace(\"") + val gdfDoc = app.get(iframeGdflix).document.selectFirst("script:containsData(replace)")?.data()?.substringAfter("replace(\"") ?.substringBefore("\")")?.let { app.get(fixUrl(it, base)).document }