diff --git a/SoraStream/build.gradle.kts b/SoraStream/build.gradle.kts index 08bea30d..32c90989 100644 --- a/SoraStream/build.gradle.kts +++ b/SoraStream/build.gradle.kts @@ -1,7 +1,7 @@ import org.jetbrains.kotlin.konan.properties.Properties // use an integer for version numbers -version = 134 +version = 135 android { defaultConfig { diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt index fe0ea9bb..60a5615c 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt @@ -730,14 +730,14 @@ object SoraExtractor : SoraStream() { ) { val query = title?.replace(Regex("[^\\w-\\s]"), "") val html = - app.get("$fmoviesAPI/ajax/film/search?vrf=${encodeVrf("$query")}&keyword=$query") - .parsedSafe()?.html + app.get("$fmoviesAPI/ajax/film/search?keyword=$query") + .parsedSafe()?.result?.html val mediaId = Jsoup.parse(html ?: return).select("a.item").map { Triple( it.attr("href"), - it.select("div.title").text(), - it.selectFirst("i.dot")?.nextSibling().toString().trim(), + it.select("div.name").text(), + it.select("span.dot")[1].text(), ) }.find { if (season == null) { @@ -746,38 +746,35 @@ object SoraExtractor : SoraStream() { it.first.contains("/series/") } && (it.second.equals(title, true) || it.second.createSlug() .equals(title.createSlug())) && it.third.toInt() == year - }?.first?.substringAfterLast("-") ?: return + }?.first - val episodeId = if (season == null) { - "1-full" - } else { - "$season-$episode" - } + val watchId = + app.get(fixUrl(mediaId ?: return, fmoviesAPI)).document.selectFirst("div.watch") + ?.attr("data-id") - val serversKname = - app.get("$fmoviesAPI/ajax/film/servers?id=$mediaId&vrf=${encodeVrf(mediaId)}") - .parsedSafe()?.html?.let { Jsoup.parse(it) } - ?.selectFirst("a[data-kname=$episodeId]")?.attr("data-ep") + val episodeId = app.get( + "$fmoviesAPI/ajax/episode/list/${watchId ?: return}?vrf=${ + comsumetEncodeVrf(watchId) + }" + ).parsedSafe()?.result?.let { Jsoup.parse(it) } + ?.selectFirst("ul[data-season=${season ?: 1}] li a[data-num=${episode ?: 1}]") + ?.attr("data-id") - val servers = tryParseJson>(serversKname) + val servers = + app.get("$fmoviesAPI/ajax/server/list/${episodeId ?: return}?vrf=${comsumetEncodeVrf(episodeId)}") + .parsedSafe()?.result?.let { Jsoup.parse(it) } + ?.select("ul li")?.map { it.attr("data-id") to it.attr("data-link-id") } - val sub = app.get("$fmoviesAPI/ajax/episode/subtitles/${servers?.get("28")}").text - tryParseJson>(sub)?.map { - subtitleCallback.invoke( - SubtitleFile( - it.label ?: "", - it.file ?: return@map - ) - ) - } - - servers?.apmap { server -> - val decryptServer = app.get("$fmoviesAPI/ajax/episode/info?id=${server.value}") - .parsedSafe()?.url?.let { decodeVrf(it) } ?: return@apmap - if (server.key == "41") { - invokeVizcloud(decryptServer, callback) + servers?.filter { + it.first == "41" || it.first == "45" + }?.apmap { (serverid, linkId) -> + delay(2000) + val decryptServer = app.get("$fmoviesAPI/ajax/server/$linkId?vrf=${comsumetEncodeVrf(linkId)}") + .parsedSafe()?.result?.url?.let { comsumetDecodeVrf(it) } + if (serverid == "41") { + invokeVizcloud(serverid, decryptServer ?: return@apmap, subtitleCallback, callback) } else { - loadExtractor(decryptServer, fmoviesAPI, subtitleCallback, callback) + loadExtractor(decryptServer ?: return@apmap, fmoviesAPI, subtitleCallback, callback) } } } @@ -3236,7 +3233,12 @@ data class DudetvSources( ) data class FmoviesResponses( + @JsonProperty("result") val result: FmoviesResult? = null, +) + +data class FmoviesResult( @JsonProperty("html") val html: String? = null, + @JsonProperty("result") val result: String? = null, @JsonProperty("url") val url: String? = null, ) diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt index d61c3ad1..558ffd60 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt @@ -78,6 +78,7 @@ open class SoraStream : TmdbProvider() { const val gdbot = "https://gdtot.pro" const val anilistAPI = "https://graphql.anilist.co" const val malsyncAPI = "https://api.malsync.moe" + const val consumetHelper = "https://api.consumet.org/anime/9anime/helper" private val apiKey = base64DecodeAPI("ZTM=NTg=MjM=MjM=ODc=MzI=OGQ=MmE=Nzk=Nzk=ZjI=NTA=NDY=NDA=MzA=YjA=") // PLEASE DON'T STEAL @@ -106,8 +107,8 @@ open class SoraStream : TmdbProvider() { const val fdMoviesAPI = "https://freedrivemovie.lol" const val m4uhdAPI = "https://m4uhd.tv" const val tvMoviesAPI = "https://www.tvseriesnmovies.com" - const val moviezAddAPI = "https://ww1.moviezaddiction.click" - const val bollyMazaAPI = "https://ww1.bollymaza.click" + const val moviezAddAPI = "https://ww2.moviezaddiction.click" + const val bollyMazaAPI = "https://m.bollymaza.click" const val moviesbayAPI = "https://moviesbay.live" const val rStreamAPI = "https://remotestre.am" const val flixonAPI = "https://flixon.ru" @@ -117,7 +118,7 @@ open class SoraStream : TmdbProvider() { const val watchSomuchAPI = "https://watchsomuch.tv" // sub only val gomoviesAPI = base64DecodeAPI("bQ==Y28=ZS4=aW4=bmw=LW8=ZXM=dmk=bW8=Z28=Ly8=czo=dHA=aHQ=") - const val ask4MoviesAPI = "https://ask4movie.mx" + const val ask4MoviesAPI = "https://ask4movie.net" const val biliBiliAPI = "https://api-vn.kaguya.app/server" const val watchOnlineAPI = "https://watchonline.ag" const val nineTvAPI = "https://api.9animetv.live" @@ -140,7 +141,6 @@ open class SoraStream : TmdbProvider() { base64DecodeAPI("ZXY=LmQ=cnM=a2U=b3I=Lnc=ZXI=ZGQ=bGE=cy0=b2I=YWM=Lmo=YWw=aW4=LWY=cm4=Ym8=cmU=Ly8=czo=dHA=aHQ=") // DEAD SITE - const val consumetCrunchyrollAPI = "https://cronchy.consumet.stream" // dead const val chillmovies0API = "https://chill.aicirou.workers.dev/0:" // dead const val chillmovies1API = "https://chill.aicirou.workers.dev/1:" // dead const val gamMoviesAPI = "https://drive.gamick.workers.dev/0:" // dead @@ -503,7 +503,7 @@ open class SoraStream : TmdbProvider() { ) }, { - invokeFmovies( + if (!res.isAnime) invokeFmovies( res.title, res.airedYear ?: res.year, res.season, @@ -610,7 +610,14 @@ open class SoraStream : TmdbProvider() { invokeMovie123Net(res.title, res.season, res.episode, subtitleCallback, callback) }, { - invokeSmashyStream(res.imdbId, res.season, res.episode, res.isAnime, subtitleCallback, callback) + invokeSmashyStream( + res.imdbId, + res.season, + res.episode, + res.isAnime, + subtitleCallback, + callback + ) }, { invokeWatchsomuch( diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt index 12e49e63..079aa124 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt @@ -145,7 +145,14 @@ class SoraStreamLite : SoraStream() { ) }, { - invokeSeries9(res.title, res.year, res.season, res.episode, subtitleCallback, callback) + invokeSeries9( + res.title, + res.year, + res.season, + res.episode, + subtitleCallback, + callback + ) }, { invokeIdlix( @@ -168,13 +175,26 @@ class SoraStreamLite : SoraStream() { // ) // }, { - if (!res.isAnime) invokeFilmxy(res.imdbId, res.season, res.episode, subtitleCallback, callback) + if (!res.isAnime) invokeFilmxy( + res.imdbId, + res.season, + res.episode, + subtitleCallback, + callback + ) }, { invokeKimcartoon(res.title, res.season, res.episode, subtitleCallback, callback) }, { - invokeSmashyStream(res.imdbId, res.season, res.episode, res.isAnime, subtitleCallback, callback) + invokeSmashyStream( + res.imdbId, + res.season, + res.episode, + res.isAnime, + subtitleCallback, + callback + ) }, { invokeXmovies( @@ -187,7 +207,7 @@ class SoraStreamLite : SoraStream() { ) }, { - invokeFmovies( + if (!res.isAnime) invokeFmovies( res.title, res.airedYear ?: res.year, res.season, diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt b/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt index 663829d4..cf1ad159 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt @@ -5,8 +5,10 @@ import com.fasterxml.jackson.annotation.JsonProperty import com.hexated.SoraStream.Companion.anilistAPI import com.hexated.SoraStream.Companion.base64DecodeAPI import com.hexated.SoraStream.Companion.baymoviesAPI +import com.hexated.SoraStream.Companion.consumetHelper import com.hexated.SoraStream.Companion.crunchyrollAPI import com.hexated.SoraStream.Companion.filmxyAPI +import com.hexated.SoraStream.Companion.fmoviesAPI import com.hexated.SoraStream.Companion.gdbot import com.hexated.SoraStream.Companion.malsyncAPI import com.hexated.SoraStream.Companion.putlockerAPI @@ -400,11 +402,13 @@ suspend fun getDirectGdrive(url: String): String { } suspend fun invokeVizcloud( + serverid: String, url: String, + subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit, ) { - val id = Regex("(?:embed-|/e/)([^?]*)").find(url)?.groupValues?.getOrNull(1) - app.get("https://api.consumet.org/anime/9anime/helper?query=${id ?: return}&action=vizcloud") + val id = Regex("(?:/embed[-/]|/e/)([^?/]*)").find(url)?.groupValues?.getOrNull(1) + app.get("$consumetHelper?query=${id ?: return}&action=vizcloud") .parsedSafe()?.data?.media?.sources?.map { M3u8Helper.generateM3u8( "Vizcloud", @@ -412,6 +416,16 @@ suspend fun invokeVizcloud( "${getBaseUrl(url)}/" ).forEach(callback) } + + val sub = app.get("${fmoviesAPI}/ajax/episode/subtitles/$serverid") + tryParseJson>(sub.text)?.map { + subtitleCallback.invoke( + SubtitleFile( + it.label ?: "", + it.file ?: return@map + ) + ) + } } suspend fun invokeSmashyFfix( @@ -1357,6 +1371,16 @@ fun getDeviceId(length: Int = 16): String { .joinToString("") } +suspend fun comsumetEncodeVrf(query: String): String? { + return app.get("$consumetHelper?query=$query&action=fmovies-vrf") + .parsedSafe>()?.get("url") +} + +suspend fun comsumetDecodeVrf(query: String): String? { + val res = app.get("$consumetHelper?query=$query&action=fmovies-decrypt") + return tryParseJson>(res.text)?.get("url") +} + fun encodeVrf(query: String): String { return encode( encryptVrf(