fix idlix

This commit is contained in:
hexated 2023-08-05 16:50:35 +07:00
parent c510461e47
commit b04f604e74
7 changed files with 119 additions and 477 deletions

View File

@ -1,5 +1,5 @@
// use an integer for version numbers // use an integer for version numbers
version = 11 version = 12
cloudstream { cloudstream {

View File

@ -6,6 +6,9 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.mvvm.safeApiCall import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.nicehttp.Requests
import com.lagradost.nicehttp.Session
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import java.net.URI import java.net.URI
@ -16,6 +19,7 @@ class IdlixProvider : MainAPI() {
override val hasMainPage = true override val hasMainPage = true
override var lang = "id" override var lang = "id"
override val hasDownloadSupport = true override val hasDownloadSupport = true
private val session = Session(Requests().baseClient)
override val supportedTypes = setOf( override val supportedTypes = setOf(
TvType.Movie, TvType.Movie,
TvType.TvSeries, TvType.TvSeries,
@ -47,9 +51,9 @@ class IdlixProvider : MainAPI() {
val url = request.data.split("?") val url = request.data.split("?")
val nonPaged = request.name == "Featured" && page <= 1 val nonPaged = request.name == "Featured" && page <= 1
val req = if (nonPaged) { val req = if (nonPaged) {
app.get(request.data) session.get(request.data)
} else { } else {
app.get("${url.first()}$page/?${url.lastOrNull()}") session.get("${url.first()}$page/?${url.lastOrNull()}")
} }
mainUrl = getBaseUrl(req.url) mainUrl = getBaseUrl(req.url)
val document = req.document val document = req.document
@ -94,7 +98,7 @@ class IdlixProvider : MainAPI() {
} }
override suspend fun search(query: String): List<SearchResponse> { override suspend fun search(query: String): List<SearchResponse> {
val req = app.get("$mainUrl/search/$query") val req = session.get("$mainUrl/search/$query")
mainUrl = getBaseUrl(req.url) mainUrl = getBaseUrl(req.url)
val document = req.document val document = req.document
return document.select("div.result-item").map { return document.select("div.result-item").map {
@ -109,7 +113,7 @@ class IdlixProvider : MainAPI() {
} }
override suspend fun load(url: String): LoadResponse { override suspend fun load(url: String): LoadResponse {
val request = app.get(url) val request = session.get(url)
directUrl = getBaseUrl(request.url) directUrl = getBaseUrl(request.url)
val document = request.document val document = request.document
val title = val title =
@ -189,7 +193,7 @@ class IdlixProvider : MainAPI() {
callback: (ExtractorLink) -> Unit callback: (ExtractorLink) -> Unit
): Boolean { ): Boolean {
val document = app.get(data).document val document = session.get(data).document
val id = document.select("meta#dooplay-ajax-counter").attr("data-postid") val id = document.select("meta#dooplay-ajax-counter").attr("data-postid")
val type = if (data.contains("/movie/")) "movie" else "tv" val type = if (data.contains("/movie/")) "movie" else "tv"
@ -197,7 +201,7 @@ class IdlixProvider : MainAPI() {
it.attr("data-nume") it.attr("data-nume")
}.apmap { nume -> }.apmap { nume ->
safeApiCall { safeApiCall {
var source = app.post( var source = session.post(
url = "$directUrl/wp-admin/admin-ajax.php", url = "$directUrl/wp-admin/admin-ajax.php",
data = mapOf( data = mapOf(
"action" to "doo_player_ajax", "action" to "doo_player_ajax",
@ -207,7 +211,7 @@ class IdlixProvider : MainAPI() {
), ),
headers = mapOf("X-Requested-With" to "XMLHttpRequest"), headers = mapOf("X-Requested-With" to "XMLHttpRequest"),
referer = data referer = data
).parsed<ResponseHash>().embed_url ).let { tryParseJson<ResponseHash>(it.text) }?.embed_url ?: return@safeApiCall
if (source.startsWith("https://uservideo.xyz")) { if (source.startsWith("https://uservideo.xyz")) {
source = app.get(source).document.select("iframe").attr("src") source = app.get(source).document.select("iframe").attr("src")

View File

@ -1,7 +1,7 @@
import org.jetbrains.kotlin.konan.properties.Properties import org.jetbrains.kotlin.konan.properties.Properties
// use an integer for version numbers // use an integer for version numbers
version = 153 version = 154
android { android {
defaultConfig { defaultConfig {

View File

@ -5,7 +5,6 @@ import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.nicehttp.Requests import com.lagradost.nicehttp.Requests
import com.lagradost.nicehttp.Session import com.lagradost.nicehttp.Session
import com.hexated.RabbitStream.extractRabbitStream
import com.lagradost.cloudstream3.extractors.Filesim import com.lagradost.cloudstream3.extractors.Filesim
import com.lagradost.cloudstream3.extractors.StreamSB import com.lagradost.cloudstream3.extractors.StreamSB
import com.lagradost.cloudstream3.extractors.Voe import com.lagradost.cloudstream3.extractors.Voe
@ -15,7 +14,6 @@ import com.lagradost.nicehttp.RequestBodyTypes
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import okio.ByteString.Companion.encode
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
@ -47,8 +45,9 @@ object SoraExtractor : SoraStream() {
val titleMedia = ele.select("h3.movie-name").text() val titleMedia = ele.select("h3.movie-name").text()
val titleSlug = title.createSlug() val titleSlug = title.createSlug()
val yearMedia = ele.select("div.info-split > div:first-child").text().toIntOrNull() val yearMedia = ele.select("div.info-split > div:first-child").text().toIntOrNull()
val lastSeasonMedia = ele.select("div.info-split > div:nth-child(2)").text().substringAfter("SS") val lastSeasonMedia =
.substringBefore("/").trim().toIntOrNull() ele.select("div.info-split > div:nth-child(2)").text().substringAfter("SS")
.substringBefore("/").trim().toIntOrNull()
(titleMedia.equals(title, true) || titleMedia.createSlug() (titleMedia.equals(title, true) || titleMedia.createSlug()
.equals(titleSlug) || url?.contains("$titleSlug-") == true) && .equals(titleSlug) || url?.contains("$titleSlug-") == true) &&
(if (season == null) { (if (season == null) {
@ -74,7 +73,8 @@ object SoraExtractor : SoraStream() {
"$gokuAPI/ajax/movie/seasons/${ "$gokuAPI/ajax/movie/seasons/${
media.selectFirst("a.btn-wl")?.attr("data-id") ?: return media.selectFirst("a.btn-wl")?.attr("data-id") ?: return
}", headers = headers }", headers = headers
).document.select("a.ss-item").find { it.ownText().equals("Season $season", true) }?.attr("data-id") ).document.select("a.ss-item").find { it.ownText().equals("Season $season", true) }
?.attr("data-id")
val episodeId = val episodeId =
app.get( app.get(
"$gokuAPI/ajax/movie/season/episodes/${seasonId ?: return}", "$gokuAPI/ajax/movie/season/episodes/${seasonId ?: return}",
@ -93,15 +93,13 @@ object SoraExtractor : SoraStream() {
val iframe = val iframe =
app.get("$gokuAPI/ajax/movie/episode/server/sources/$id", headers = headers) app.get("$gokuAPI/ajax/movie/episode/server/sources/$id", headers = headers)
.parsedSafe<GokuServer>()?.data?.link ?: return@apmap .parsedSafe<GokuServer>()?.data?.link ?: return@apmap
extractRabbitStream( loadCustomExtractor(
if (iframe.contains("rabbitstream")) "Vidcloud" else "Upcloud", if (iframe.contains("rabbitstream")) "Vidcloud" else "Upcloud",
iframe, iframe,
"$gokuAPI/", "$gokuAPI/",
subtitleCallback, subtitleCallback,
callback, callback,
false, )
decryptKey = RabbitStream.getKey()
) { it }
} }
} }
@ -417,7 +415,7 @@ object SoraExtractor : SoraStream() {
} else { } else {
"$idlixAPI/episode/$fixTitle-season-$season-episode-$episode" "$idlixAPI/episode/$fixTitle-season-$season-episode-$episode"
} }
invokeWpmovies(url,subtitleCallback, callback) invokeWpmovies(url, subtitleCallback, callback)
} }
suspend fun invokeMultimovies( suspend fun invokeMultimovies(
@ -433,7 +431,7 @@ object SoraExtractor : SoraStream() {
} else { } else {
"$multimoviesAPI/episodes/$fixTitle-${season}x${episode}" "$multimoviesAPI/episodes/$fixTitle-${season}x${episode}"
} }
invokeWpmovies(url,subtitleCallback, callback,true) invokeWpmovies(url, subtitleCallback, callback, true)
} }
suspend fun invokeNetmovies( suspend fun invokeNetmovies(
@ -450,7 +448,7 @@ object SoraExtractor : SoraStream() {
} else { } else {
"$netmoviesAPI/episodes/$fixTitle-${season}x${episode}" "$netmoviesAPI/episodes/$fixTitle-${season}x${episode}"
} }
invokeWpmovies(url,subtitleCallback, callback) invokeWpmovies(url, subtitleCallback, callback)
} }
private suspend fun invokeWpmovies( private suspend fun invokeWpmovies(
@ -459,7 +457,7 @@ object SoraExtractor : SoraStream() {
callback: (ExtractorLink) -> Unit, callback: (ExtractorLink) -> Unit,
fixIframe: Boolean = false, fixIframe: Boolean = false,
) { ) {
val res = app.get(url ?: return) val res = session.get(url ?: return)
val referer = getBaseUrl(res.url) val referer = getBaseUrl(res.url)
val document = res.document val document = res.document
document.select("ul#playeroptionsul > li").map { document.select("ul#playeroptionsul > li").map {
@ -469,11 +467,14 @@ object SoraExtractor : SoraStream() {
it.attr("data-type") it.attr("data-type")
) )
}.apmap { (id, nume, type) -> }.apmap { (id, nume, type) ->
val source = app.post( val json = session.post(
url = "$referer/wp-admin/admin-ajax.php", data = mapOf( url = "$referer/wp-admin/admin-ajax.php", data = mapOf(
"action" to "doo_player_ajax", "post" to id, "nume" to nume, "type" to type "action" to "doo_player_ajax", "post" to id, "nume" to nume, "type" to type
), headers = mapOf("X-Requested-With" to "XMLHttpRequest"), referer = url ), headers = mapOf("X-Requested-With" to "XMLHttpRequest"), referer = url
).parsed<ResponseHash>().embed_url.let { if(fixIframe) Jsoup.parse(it).select("IFRAME").attr("SRC") else it } )
val source = tryParseJson<ResponseHash>(json.text)?.embed_url?.let {
if (fixIframe) Jsoup.parse(it).select("IFRAME").attr("SRC") else it
} ?: return@apmap
if (!source.contains("youtube")) { if (!source.contains("youtube")) {
loadExtractor(source, "$referer/", subtitleCallback, callback) loadExtractor(source, "$referer/", subtitleCallback, callback)
} }
@ -560,9 +561,11 @@ object SoraExtractor : SoraStream() {
) )
) )
} }
!source.contains("youtube") -> loadExtractor( !source.contains("youtube") -> loadExtractor(
source, "$uniqueStreamAPI/", subtitleCallback, callback source, "$uniqueStreamAPI/", subtitleCallback, callback
) )
else -> { else -> {
// pass // pass
} }
@ -912,7 +915,10 @@ object SoraExtractor : SoraStream() {
when { when {
season == null -> slugTitle?.equals(slug) == true season == null -> slugTitle?.equals(slug) == true
lastSeason == 1 -> slugTitle?.contains(slug) == true lastSeason == 1 -> slugTitle?.contains(slug) == true
else -> slugTitle?.contains(slug) == true && it.title?.contains("Season $season", true) == true else -> slugTitle?.contains(slug) == true && it.title?.contains(
"Season $season",
true
) == true
} }
} }
data?.id to data?.title data?.id to data?.title
@ -1066,16 +1072,23 @@ object SoraExtractor : SoraStream() {
val headers = mapOf( val headers = mapOf(
"X-Requested-With" to "XMLHttpRequest", "X-Requested-With" to "XMLHttpRequest",
) )
val animeId = app.get("$malsyncAPI/mal/anime/${malId ?: return}").parsedSafe<MALSyncResponses>()?.sites?.zoro?.keys?.map { it } val animeId = app.get("$malsyncAPI/mal/anime/${malId ?: return}")
.parsedSafe<MALSyncResponses>()?.sites?.zoro?.keys?.map { it }
animeId?.apmap { id -> animeId?.apmap { id ->
val episodeId = app.get("$aniwatchAPI/ajax/v2/episode/list/${id ?: return@apmap}", headers = headers) val episodeId = app.get(
"$aniwatchAPI/ajax/v2/episode/list/${id ?: return@apmap}",
headers = headers
)
.parsedSafe<AniwatchResponses>()?.html?.let { .parsedSafe<AniwatchResponses>()?.html?.let {
Jsoup.parse(it) Jsoup.parse(it)
}?.select("div.ss-list a")?.find { it.attr("data-number") == "${episode ?: 1}" } }?.select("div.ss-list a")?.find { it.attr("data-number") == "${episode ?: 1}" }
?.attr("data-id") ?.attr("data-id")
val servers = val servers =
app.get("$aniwatchAPI/ajax/v2/episode/servers?episodeId=${episodeId ?: return@apmap}", headers = headers) app.get(
"$aniwatchAPI/ajax/v2/episode/servers?episodeId=${episodeId ?: return@apmap}",
headers = headers
)
.parsedSafe<AniwatchResponses>()?.html?.let { Jsoup.parse(it) } .parsedSafe<AniwatchResponses>()?.html?.let { Jsoup.parse(it) }
?.select("div.item.server-item")?.map { ?.select("div.item.server-item")?.map {
Triple( Triple(
@ -1086,27 +1099,21 @@ object SoraExtractor : SoraStream() {
} }
servers?.apmap servers@{ server -> servers?.apmap servers@{ server ->
val iframe = app.get("$aniwatchAPI/ajax/v2/episode/sources?id=${server.second ?: return@servers}", headers = headers) val iframe = app.get(
.parsedSafe<AniwatchResponses>()?.link ?: return@servers "$aniwatchAPI/ajax/v2/episode/sources?id=${server.second ?: return@servers}",
headers = headers
)
.parsedSafe<AniwatchResponses>()?.link ?: return@servers
val audio = if (server.third == "sub") "Raw" else "English Dub" val audio = if (server.third == "sub") "Raw" else "English Dub"
if (server.first.contains(Regex("Vidstreaming|MegaCloud|Vidcloud"))) { loadCustomExtractor(
extractRabbitStream( "${server.first} [$audio]",
"${server.first} [$audio]", iframe,
iframe, "$aniwatchAPI/",
"$aniwatchAPI/", subtitleCallback,
subtitleCallback, callback,
callback, )
false,
decryptKey = RabbitStream.getZoroKey()
) { it }
} else {
loadExtractor(iframe, "$aniwatchAPI/", subtitleCallback, callback)
}
} }
} }
} }
suspend fun invokeLing( suspend fun invokeLing(
@ -1307,9 +1314,11 @@ object SoraExtractor : SoraStream() {
val gdBotLink = extractGdbot(link) val gdBotLink = extractGdbot(link)
extractGdflix(gdBotLink ?: return@apmap) extractGdflix(gdBotLink ?: return@apmap)
} }
link.contains("gdflix") -> { link.contains("gdflix") -> {
extractGdflix(link) extractGdflix(link)
} }
else -> { else -> {
return@apmap return@apmap
} }
@ -1476,15 +1485,17 @@ object SoraExtractor : SoraStream() {
val gdBotLink = extractGdbot(fdLink ?: return@apmap null) val gdBotLink = extractGdbot(fdLink ?: return@apmap null)
extractGdflix(gdBotLink ?: return@apmap null) extractGdflix(gdBotLink ?: return@apmap null)
} }
type.contains("oiya") -> { type.contains("oiya") -> {
val oiyaLink = extractOiya(fdLink ?: return@apmap null, qualities) val oiyaLink = extractOiya(fdLink ?: return@apmap null, qualities)
if(oiyaLink?.contains("gdtot") == true) { if (oiyaLink?.contains("gdtot") == true) {
val gdBotLink = extractGdbot(oiyaLink) val gdBotLink = extractGdbot(oiyaLink)
extractGdflix(gdBotLink ?: return@apmap null) extractGdflix(gdBotLink ?: return@apmap null)
} else { } else {
oiyaLink oiyaLink
} }
} }
else -> { else -> {
return@apmap null return@apmap null
} }
@ -1634,7 +1645,7 @@ object SoraExtractor : SoraStream() {
callback: (ExtractorLink) -> Unit callback: (ExtractorLink) -> Unit
) { ) {
val id = getCrunchyrollId("${aniId ?: return}") val id = getCrunchyrollId("${aniId ?: return}")
?: getCrunchyrollIdFromMalSync("${malId ?: return}") ?: return ?: getCrunchyrollIdFromMalSync("${malId ?: return}")
val audioLocal = listOf( val audioLocal = listOf(
"ja-JP", "ja-JP",
"en-US", "en-US",
@ -1946,24 +1957,33 @@ object SoraExtractor : SoraStream() {
it.attr("data-id") to it.text() it.attr("data-id") to it.text()
}.apmap { }.apmap {
when { when {
(it.second.equals("Player F", true) || it.second.equals("Player N", true)) && !isAnime -> { (it.second.equals("Player F", true) || it.second.equals(
"Player N",
true
)) && !isAnime -> {
invokeSmashyFfix(it.second, it.first, url, callback) invokeSmashyFfix(it.second, it.first, url, callback)
} }
it.first.contains("/gtop") -> { it.first.contains("/gtop") -> {
invokeSmashyGtop(it.second, it.first, callback) invokeSmashyGtop(it.second, it.first, callback)
} }
it.first.contains("/dude_tv") -> { it.first.contains("/dude_tv") -> {
invokeSmashyDude(it.second, it.first, callback) invokeSmashyDude(it.second, it.first, callback)
} }
it.first.contains("/rip") -> { it.first.contains("/rip") -> {
invokeSmashyRip(it.second, it.first, subtitleCallback, callback) invokeSmashyRip(it.second, it.first, subtitleCallback, callback)
} }
it.first.contains("/im.php") && !isAnime -> { it.first.contains("/im.php") && !isAnime -> {
invokeSmashyIm(it.second, it.first, subtitleCallback, callback) invokeSmashyIm(it.second, it.first, subtitleCallback, callback)
} }
it.first.contains("/rw.php") && !isAnime -> { it.first.contains("/rw.php") && !isAnime -> {
invokeSmashyRw(it.second, it.first, subtitleCallback, callback) invokeSmashyRw(it.second, it.first, subtitleCallback, callback)
} }
else -> return@apmap else -> return@apmap
} }
} }
@ -2015,63 +2035,6 @@ object SoraExtractor : SoraStream() {
} }
}
suspend fun invokeBaymovies(
title: String? = null,
year: Int? = null,
season: Int? = null,
episode: Int? = null,
callback: (ExtractorLink) -> Unit,
) {
val api = "https://thebayindexpublicgroupapi.zindex.eu.org"
val key = base64DecodeAPI("ZW0=c3Q=c3k=b28=YWQ=Ymg=")
val headers = mapOf(
"Referer" to "$baymoviesAPI/",
"Origin" to baymoviesAPI,
"cf_cache_token" to "UKsVpQqBMxB56gBfhYKbfCVkRIXMh42pk6G4DdkXXoVh7j4BjV"
)
val query = getIndexQuery(title, year, season, episode)
val search = app.get(
"$api/0:search?q=$query&page_token=&page_index=0",
headers = headers
).text
val media = searchIndex(title, season, episode, year, search) ?: return
media.apmap { file ->
val expiry = (System.currentTimeMillis() + 345600000).toString()
val hmacSign = "${file.id}@$expiry".encode()
.hmacSha256(key.encode()).base64().replace("+", "-")
val encryptedId =
base64Encode(CryptoAES.encrypt(key, file.id ?: return@apmap null).toByteArray())
val encryptedExpiry = base64Encode(CryptoAES.encrypt(key, expiry).toByteArray())
val worker = getConfig().workers.randomOrNull() ?: return@apmap null
val link =
"https://api.$worker.workers.dev/download.aspx?file=$encryptedId&expiry=$encryptedExpiry&mac=$hmacSign"
val size = file.size?.toDouble() ?: return@apmap null
val sizeFile = "%.2f GB".format(bytesToGigaBytes(size))
val tags = Regex("\\d{3,4}[pP]\\.?(.*?)\\.(mkv|mp4)").find(
file.name ?: return@apmap null
)?.groupValues?.getOrNull(1)?.replace(".", " ")?.trim()
?: ""
val quality =
Regex("(\\d{3,4})[pP]").find(file.name)?.groupValues?.getOrNull(1)?.toIntOrNull()
?: Qualities.P1080.value
callback.invoke(
ExtractorLink(
"Baymovies",
"Baymovies $tags [$sizeFile]",
link,
"$baymoviesAPI/",
quality,
)
)
}
} }
suspend fun invokeBlackmovies( suspend fun invokeBlackmovies(
@ -2094,26 +2057,6 @@ object SoraExtractor : SoraStream() {
) )
} }
suspend fun invokeRinzrymovies(
apiUrl: String,
api: String,
title: String? = null,
year: Int? = null,
season: Int? = null,
episode: Int? = null,
callback: (ExtractorLink) -> Unit,
) {
invokeIndex(
apiUrl,
api,
title,
year,
season,
episode,
callback,
)
}
suspend fun invokeCodexmovies( suspend fun invokeCodexmovies(
apiUrl: String, apiUrl: String,
api: String, api: String,
@ -2490,7 +2433,7 @@ object SoraExtractor : SoraStream() {
episode: Int? = null, episode: Int? = null,
callback: (ExtractorLink) -> Unit, callback: (ExtractorLink) -> Unit,
) { ) {
val (seasonSlug, episodeSlug) = getEpisodeSlug(season, episode) val slug = getEpisodeSlug(season, episode)
val query = if (season == null) { val query = if (season == null) {
title title
} else { } else {
@ -2530,7 +2473,7 @@ object SoraExtractor : SoraStream() {
media.third, media.third,
gomoviesAPI gomoviesAPI
) )
).document.selectFirst("div#g_MXOzFGouZrOAUioXjpddqkZK a:contains(Episode $episodeSlug)") ).document.selectFirst("div#g_MXOzFGouZrOAUioXjpddqkZK a:contains(Episode ${slug.second})")
?.attr("href") ?.attr("href")
} ?: return } ?: return
@ -2576,7 +2519,6 @@ object SoraExtractor : SoraStream() {
year: Int? = null, year: Int? = null,
season: Int? = null, season: Int? = null,
episode: Int? = null, episode: Int? = null,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit, callback: (ExtractorLink) -> Unit,
) { ) {
val query = if (season == null) { val query = if (season == null) {
@ -2610,7 +2552,8 @@ object SoraExtractor : SoraStream() {
} }
val iframeDoc = app.get(iframe, referer = "$ask4MoviesAPI/").text val iframeDoc = app.get(iframe, referer = "$ask4MoviesAPI/").text
val script = Regex("""eval\(function\(p,a,c,k,e,.*\)\)""").findAll(iframeDoc).lastOrNull()?.value val script =
Regex("""eval\(function\(p,a,c,k,e,.*\)\)""").findAll(iframeDoc).lastOrNull()?.value
val unpacked = getAndUnpack(script ?: return) val unpacked = getAndUnpack(script ?: return)
val m3u8 = Regex("file:\\s*\"(.*?m3u8.*?)\"").find(unpacked)?.groupValues?.getOrNull(1) val m3u8 = Regex("file:\\s*\"(.*?m3u8.*?)\"").find(unpacked)?.groupValues?.getOrNull(1)
M3u8Helper.generateM3u8( M3u8Helper.generateM3u8(
@ -2748,7 +2691,8 @@ object SoraExtractor : SoraStream() {
"$nineTvAPI/tv/$tmdbId-$season-$episode" "$nineTvAPI/tv/$tmdbId-$season-$episode"
} }
val iframe = app.get(url, referer = "https://pressplay.top/").document.selectFirst("iframe")?.attr("src") ?: return val iframe = app.get(url, referer = "https://pressplay.top/").document.selectFirst("iframe")
?.attr("src") ?: return
loadExtractor(iframe, "$nineTvAPI/", subtitleCallback, callback) loadExtractor(iframe, "$nineTvAPI/", subtitleCallback, callback)
} }
@ -2898,7 +2842,6 @@ object SoraExtractor : SoraStream() {
imdbId: String? = null, imdbId: String? = null,
title: String? = null, title: String? = null,
year: Int? = null, year: Int? = null,
season: Int? = null,
episode: Int? = null, episode: Int? = null,
callback: (ExtractorLink) -> Unit callback: (ExtractorLink) -> Unit
) { ) {
@ -2909,7 +2852,7 @@ object SoraExtractor : SoraStream() {
null, null,
title, title,
year, year,
season, null,
episode, episode,
false false
) )
@ -2919,7 +2862,11 @@ object SoraExtractor : SoraStream() {
val size = getIndexSize(stream.title) val size = getIndexSize(stream.title)
val headers = stream.behaviorHints?.proxyHeaders?.request ?: mapOf() val headers = stream.behaviorHints?.proxyHeaders?.request ?: mapOf()
if(!app.get(stream.url ?: return@apmap, headers = headers).isSuccessful) return@apmap if (!app.get(
stream.url ?: return@apmap,
headers = headers
).isSuccessful
) return@apmap
callback.invoke( callback.invoke(
ExtractorLink( ExtractorLink(
@ -2943,7 +2890,8 @@ object SoraExtractor : SoraStream() {
) { ) {
val referer = "https://2now.tv/" val referer = "https://2now.tv/"
val (seasonSlug, episodeSlug) = getEpisodeSlug(season, episode) val (seasonSlug, episodeSlug) = getEpisodeSlug(season, episode)
val url = if(season == null) "$nowTvAPI/$tmdbId.mp4" else "$nowTvAPI/tv/$tmdbId/s${season}e${episodeSlug}.mp4" val url =
if (season == null) "$nowTvAPI/$tmdbId.mp4" else "$nowTvAPI/tv/$tmdbId/s${season}e${episodeSlug}.mp4"
if (!app.get(url, referer = referer).isSuccessful) return if (!app.get(url, referer = referer).isSuccessful) return
callback.invoke( callback.invoke(
ExtractorLink( ExtractorLink(
@ -3038,7 +2986,7 @@ object SoraExtractor : SoraStream() {
).text ).text
M3u8Helper.generateM3u8( M3u8Helper.generateM3u8(
if(host == navyAPI) "Navy" else "Moment", if (host == navyAPI) "Navy" else "Moment",
path, path,
"${referer}/" "${referer}/"
).forEach(callback) ).forEach(callback)
@ -3081,7 +3029,10 @@ object SoraExtractor : SoraStream() {
) )
).parsedSafe<EMovieServer>()?.value ).parsedSafe<EMovieServer>()?.value
val script = app.get(server ?: return, referer = "$emoviesAPI/").document.selectFirst("script:containsData(sources:)")?.data() ?: return val script = app.get(
server ?: return,
referer = "$emoviesAPI/"
).document.selectFirst("script:containsData(sources:)")?.data() ?: return
val sources = Regex("sources:\\s*\\[(.*)],").find(script)?.groupValues?.get(1)?.let { val sources = Regex("sources:\\s*\\[(.*)],").find(script)?.groupValues?.get(1)?.let {
tryParseJson<List<EMovieSources>>("[$it]") tryParseJson<List<EMovieSources>>("[$it]")
} }

View File

@ -159,7 +159,6 @@ open class SoraStream : TmdbProvider() {
const val jsMoviesAPI = "https://jsupload.jnsbot.workers.dev/0:" const val jsMoviesAPI = "https://jsupload.jnsbot.workers.dev/0:"
const val xtremeMoviesAPI = "https://kartik19.xtrememirror0.workers.dev/0:" const val xtremeMoviesAPI = "https://kartik19.xtrememirror0.workers.dev/0:"
const val tgarMovieAPI = "https://tgarchive.eu.org" const val tgarMovieAPI = "https://tgarchive.eu.org"
const val baymoviesAPI = "https://opengatewayindex.pages.dev"
const val papaonMovies1API = "https://m.papaonwork.workers.dev/0:" const val papaonMovies1API = "https://m.papaonwork.workers.dev/0:"
const val papaonMovies2API = "https://m.papaonwork.workers.dev/1:" const val papaonMovies2API = "https://m.papaonwork.workers.dev/1:"
const val xMovieAPI = "https://xemovies.to" const val xMovieAPI = "https://xemovies.to"
@ -789,7 +788,6 @@ open class SoraStream : TmdbProvider() {
res.year, res.year,
res.season, res.season,
res.episode, res.episode,
subtitleCallback,
callback callback
) )
}, },
@ -819,7 +817,6 @@ open class SoraStream : TmdbProvider() {
res.imdbId, res.imdbId,
res.title, res.title,
res.year, res.year,
res.season,
res.episode, res.episode,
callback callback
) )

View File

@ -276,7 +276,6 @@ class SoraStreamLite : SoraStream() {
res.year, res.year,
res.season, res.season,
res.episode, res.episode,
subtitleCallback,
callback callback
) )
}, },

View File

@ -1,11 +1,9 @@
package com.hexated package com.hexated
import android.util.Base64 import android.util.Base64
import com.fasterxml.jackson.annotation.JsonProperty
import com.hexated.DumpUtils.queryApi import com.hexated.DumpUtils.queryApi
import com.hexated.SoraStream.Companion.anilistAPI import com.hexated.SoraStream.Companion.anilistAPI
import com.hexated.SoraStream.Companion.base64DecodeAPI import com.hexated.SoraStream.Companion.base64DecodeAPI
import com.hexated.SoraStream.Companion.baymoviesAPI
import com.hexated.SoraStream.Companion.consumetHelper import com.hexated.SoraStream.Companion.consumetHelper
import com.hexated.SoraStream.Companion.crunchyrollAPI import com.hexated.SoraStream.Companion.crunchyrollAPI
import com.hexated.SoraStream.Companion.filmxyAPI import com.hexated.SoraStream.Companion.filmxyAPI
@ -19,12 +17,9 @@ import com.hexated.SoraStream.Companion.watchOnlineAPI
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getCaptchaToken import com.lagradost.cloudstream3.APIHolder.getCaptchaToken
import com.lagradost.cloudstream3.APIHolder.unixTimeMS import com.lagradost.cloudstream3.APIHolder.unixTimeMS
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.AppUtils.toJson import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.nicehttp.NiceResponse import com.lagradost.nicehttp.NiceResponse
import com.lagradost.nicehttp.RequestBodyTypes import com.lagradost.nicehttp.RequestBodyTypes
import com.lagradost.nicehttp.requestCreator import com.lagradost.nicehttp.requestCreator
@ -37,7 +32,6 @@ import okhttp3.RequestBody.Companion.toRequestBody
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import java.math.BigInteger import java.math.BigInteger
import java.net.* import java.net.*
import java.nio.charset.StandardCharsets
import java.security.* import java.security.*
import java.security.spec.PKCS8EncodedKeySpec import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec import java.security.spec.X509EncodedKeySpec
@ -52,12 +46,6 @@ import kotlin.math.min
val bflixChipperKey = base64DecodeAPI("Yjc=ejM=TzA=YTk=WHE=WnU=bXU=RFo=") val bflixChipperKey = base64DecodeAPI("Yjc=ejM=TzA=YTk=WHE=WnU=bXU=RFo=")
const val bflixKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" const val bflixKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
const val otakuzBaseUrl = "https://otakuz.live/" const val otakuzBaseUrl = "https://otakuz.live/"
val soraHeaders = mapOf(
"lang" to "en",
"versioncode" to "33",
"clienttype" to "android_Official",
"deviceid" to getDeviceId(),
)
val encodedIndex = arrayOf( val encodedIndex = arrayOf(
"GamMovies", "GamMovies",
"JSMovies", "JSMovies",
@ -1165,6 +1153,33 @@ suspend fun tmdbToAnimeId(title: String?, year: Int?, season: String?, type: TvT
} }
suspend fun loadCustomExtractor(
name: String,
url: String,
referer: String? = null,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit,
quality: Int? = null,
) {
loadExtractor(url, referer, subtitleCallback) { link ->
callback.invoke(
ExtractorLink(
name,
name,
link.url,
link.referer,
when {
link.isM3u8 -> link.quality
else -> quality ?: link.quality
},
link.isM3u8,
link.headers,
link.extractorData
)
)
}
}
fun getSeason(month: Int?): String? { fun getSeason(month: Int?): String? {
val seasons = arrayOf( val seasons = arrayOf(
"Winter", "Winter", "Spring", "Spring", "Spring", "Summer", "Winter", "Winter", "Spring", "Spring", "Spring", "Summer",
@ -1182,7 +1197,6 @@ fun getPutlockerQuality(quality: String): Int {
} }
} }
fun getEpisodeSlug( fun getEpisodeSlug(
season: Int? = null, season: Int? = null,
episode: Int? = null, episode: Int? = null,
@ -1264,23 +1278,6 @@ fun matchingIndex(
) == true && ((mediaMimeType in mimeType) || mediaName.contains(Regex("\\.mkv|\\.mp4|\\.avi"))) ) == true && ((mediaMimeType in mimeType) || mediaName.contains(Regex("\\.mkv|\\.mp4|\\.avi")))
} }
suspend fun getConfig(): BaymoviesConfig {
val regex = """const country = "(.*?)";
const downloadtime = "(.*?)";
var arrayofworkers = (.*)""".toRegex()
val js = app.get(
"https://geolocation.zindex.eu.org/api.js",
referer = "$baymoviesAPI/",
).text
val match = regex.find(js) ?: throw ErrorLoadingException()
val country = match.groupValues[1]
val downloadTime = match.groupValues[2]
val workers = tryParseJson<List<String>>(match.groupValues[3])
?: throw ErrorLoadingException()
return BaymoviesConfig(country, downloadTime, workers)
}
fun decodeIndexJson(json: String): String { fun decodeIndexJson(json: String): String {
val slug = json.reversed().substring(24) val slug = json.reversed().substring(24)
return base64Decode(slug.substring(0, slug.length - 20)) return base64Decode(slug.substring(0, slug.length - 20))
@ -1791,312 +1788,6 @@ object CryptoAES {
} }
object RabbitStream {
suspend fun MainAPI.extractRabbitStream(
server: String,
url: String,
ref: String,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit,
useSidAuthentication: Boolean,
/** Used for extractorLink name, input: Source name */
extractorData: String? = null,
decryptKey: String? = null,
nameTransformer: (String) -> String,
) = suspendSafeApiCall {
// https://rapid-cloud.ru/embed-6/dcPOVRE57YOT?z= -> https://rapid-cloud.ru/embed-6
val mainIframeUrl =
url.substringBeforeLast("/")
val mainIframeId = url.substringAfterLast("/")
.substringBefore("?") // https://rapid-cloud.ru/embed-6/dcPOVRE57YOT?z= -> dcPOVRE57YOT
var sid: String? = null
if (useSidAuthentication && extractorData != null) {
negotiateNewSid(extractorData)?.also { pollingData ->
app.post(
"$extractorData&t=${generateTimeStamp()}&sid=${pollingData.sid}",
requestBody = "40".toRequestBody(),
timeout = 60
)
val text = app.get(
"$extractorData&t=${generateTimeStamp()}&sid=${pollingData.sid}",
timeout = 60
).text.replaceBefore("{", "")
sid = AppUtils.parseJson<PollingData>(text).sid
ioSafe { app.get("$extractorData&t=${generateTimeStamp()}&sid=${pollingData.sid}") }
}
}
val mainIframeAjax = mainIframeUrl.let {
if(it.contains("/embed-2/e-1")) it.replace(
"/embed-2/e-1",
"/embed-2/ajax/e-1"
) else it.replace(
"/embed",
"/ajax/embed"
)
}
val getSourcesUrl = "$mainIframeAjax/getSources?id=$mainIframeId${sid?.let { "$&sId=$it" } ?: ""}"
val response = app.get(
getSourcesUrl,
referer = mainUrl,
headers = mapOf(
"X-Requested-With" to "XMLHttpRequest",
"Accept" to "*/*",
"Accept-Language" to "en-US,en;q=0.5",
"Connection" to "keep-alive",
"TE" to "trailers"
)
)
val sourceObject = if (decryptKey != null) {
val encryptedMap = response.parsedSafe<SourceObjectEncrypted>()
val sources = encryptedMap?.sources
if (sources == null || encryptedMap.encrypted == false) {
response.parsedSafe()
} else {
val (realKey, encData) = extractRealKey(sources, decryptKey)
val decrypted = decryptMapped<List<Sources>>(encData, realKey)
SourceObject(
sources = decrypted,
tracks = encryptedMap.tracks
)
}
} else {
response.parsedSafe()
} ?: return@suspendSafeApiCall
sourceObject.tracks?.forEach { track ->
track?.toSubtitleFile()?.let { subtitleFile ->
subtitleCallback.invoke(subtitleFile)
}
}
val list = listOf(
sourceObject.sources to "source 1",
sourceObject.sources1 to "source 2",
sourceObject.sources2 to "source 3",
sourceObject.sourcesBackup to "source backup"
)
list.forEach { subList ->
subList.first?.forEach { source ->
source?.toExtractorLink(
server,
ref,
extractorData,
)
?.forEach {
// Sets Zoro SID used for video loading
// (this as? ZoroProvider)?.sid?.set(it.url.hashCode(), sid)
callback(it)
}
}
}
}
private suspend fun Sources.toExtractorLink(
name: String,
referer: String,
extractorData: String? = null,
): List<ExtractorLink>? {
return this.file?.let { file ->
//println("FILE::: $file")
val isM3u8 = URI(this.file).path.endsWith(".m3u8") || this.type.equals(
"hls",
ignoreCase = true
)
return if (isM3u8) {
suspendSafeApiCall {
M3u8Helper().m3u8Generation(
M3u8Helper.M3u8Stream(
this.file,
null,
mapOf("Referer" to "https://mzzcloud.life/")
), false
)
.map { stream ->
ExtractorLink(
name,
name,
stream.streamUrl,
referer,
getQualityFromName(stream.quality?.toString()),
true,
extractorData = extractorData
)
}
}.takeIf { !it.isNullOrEmpty() } ?: listOf(
// Fallback if m3u8 extractor fails
ExtractorLink(
name,
name,
this.file,
referer,
getQualityFromName(this.label),
isM3u8,
extractorData = extractorData
)
)
} else {
listOf(
ExtractorLink(
name,
name,
file,
referer,
getQualityFromName(this.label),
false,
extractorData = extractorData
)
)
}
}
}
private fun Tracks.toSubtitleFile(): SubtitleFile? {
return this.file?.let {
SubtitleFile(
this.label ?: "Unknown",
it
)
}
}
/**
* Generates a session
* 1 Get request.
* */
private suspend fun negotiateNewSid(baseUrl: String): PollingData? {
// Tries multiple times
for (i in 1..5) {
val jsonText =
app.get("$baseUrl&t=${generateTimeStamp()}").text.replaceBefore(
"{",
""
)
// println("Negotiated sid $jsonText")
AppUtils.parseJson<PollingData?>(jsonText)?.let { return it }
delay(1000L * i)
}
return null
}
private fun generateTimeStamp(): String {
val chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_"
var code = ""
var time = APIHolder.unixTimeMS
while (time > 0) {
code += chars[(time % (chars.length)).toInt()]
time /= chars.length
}
return code.reversed()
}
suspend fun getKey(): String {
return app.get("https://raw.githubusercontent.com/enimax-anime/key/e4/key.txt")
.text
}
suspend fun getZoroKey(): String {
return app.get("https://raw.githubusercontent.com/enimax-anime/key/e6/key.txt").text
}
private fun extractRealKey(originalString: String?, stops: String) : Pair<String,String> {
val table = parseJson<List<List<Int>>>(stops)
val decryptedKey = StringBuilder()
var offset = 0
var encryptedString = originalString
table.forEach { (start, end) ->
decryptedKey.append(encryptedString?.substring(start - offset, end - offset))
encryptedString = encryptedString?.substring(0, start - offset) + encryptedString?.substring(end - offset)
offset += end - start
}
return decryptedKey.toString() to encryptedString.toString()
}
private inline fun <reified T> decryptMapped(input: String, key: String): T? {
return tryParseJson(decrypt(input, key))
}
private fun decrypt(input: String, key: String): String {
return decryptSourceUrl(
generateKey(
base64DecodeArray(input).copyOfRange(8, 16),
key.toByteArray()
), input
)
}
private fun generateKey(salt: ByteArray, secret: ByteArray): ByteArray {
var key = md5(secret + salt)
var currentKey = key
while (currentKey.size < 48) {
key = md5(key + secret + salt)
currentKey += key
}
return currentKey
}
private fun md5(input: ByteArray): ByteArray {
return MessageDigest.getInstance("MD5").digest(input)
}
private fun decryptSourceUrl(decryptionKey: ByteArray, sourceUrl: String): String {
val cipherData = base64DecodeArray(sourceUrl)
val encrypted = cipherData.copyOfRange(16, cipherData.size)
val aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding")
Objects.requireNonNull(aesCBC).init(
Cipher.DECRYPT_MODE, SecretKeySpec(
decryptionKey.copyOfRange(0, 32),
"AES"
),
IvParameterSpec(decryptionKey.copyOfRange(32, decryptionKey.size))
)
val decryptedData = aesCBC!!.doFinal(encrypted)
return String(decryptedData, StandardCharsets.UTF_8)
}
data class PollingData(
@JsonProperty("sid") val sid: String? = null,
@JsonProperty("upgrades") val upgrades: ArrayList<String> = arrayListOf(),
@JsonProperty("pingInterval") val pingInterval: Int? = null,
@JsonProperty("pingTimeout") val pingTimeout: Int? = null
)
data class Tracks(
@JsonProperty("file") val file: String?,
@JsonProperty("label") val label: String?,
@JsonProperty("kind") val kind: String?
)
data class Sources(
@JsonProperty("file") val file: String?,
@JsonProperty("type") val type: String?,
@JsonProperty("label") val label: String?
)
data class SourceObject(
@JsonProperty("sources") val sources: List<Sources?>? = null,
@JsonProperty("sources_1") val sources1: List<Sources?>? = null,
@JsonProperty("sources_2") val sources2: List<Sources?>? = null,
@JsonProperty("sourcesBackup") val sourcesBackup: List<Sources?>? = null,
@JsonProperty("tracks") val tracks: List<Tracks?>? = null
)
data class SourceObjectEncrypted(
@JsonProperty("sources") val sources: String?,
@JsonProperty("encrypted") val encrypted: Boolean?,
@JsonProperty("sources_1") val sources1: String?,
@JsonProperty("sources_2") val sources2: String?,
@JsonProperty("sourcesBackup") val sourcesBackup: String?,
@JsonProperty("tracks") val tracks: List<Tracks?>?
)
}
object DumpUtils { object DumpUtils {
private val deviceId = getDeviceId() private val deviceId = getDeviceId()