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

View File

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

View File

@ -5,7 +5,6 @@ import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.nicehttp.Requests
import com.lagradost.nicehttp.Session
import com.hexated.RabbitStream.extractRabbitStream
import com.lagradost.cloudstream3.extractors.Filesim
import com.lagradost.cloudstream3.extractors.StreamSB
import com.lagradost.cloudstream3.extractors.Voe
@ -15,7 +14,6 @@ import com.lagradost.nicehttp.RequestBodyTypes
import kotlinx.coroutines.delay
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.toRequestBody
import okio.ByteString.Companion.encode
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
@ -47,8 +45,9 @@ object SoraExtractor : SoraStream() {
val titleMedia = ele.select("h3.movie-name").text()
val titleSlug = title.createSlug()
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")
.substringBefore("/").trim().toIntOrNull()
val lastSeasonMedia =
ele.select("div.info-split > div:nth-child(2)").text().substringAfter("SS")
.substringBefore("/").trim().toIntOrNull()
(titleMedia.equals(title, true) || titleMedia.createSlug()
.equals(titleSlug) || url?.contains("$titleSlug-") == true) &&
(if (season == null) {
@ -74,7 +73,8 @@ object SoraExtractor : SoraStream() {
"$gokuAPI/ajax/movie/seasons/${
media.selectFirst("a.btn-wl")?.attr("data-id") ?: return
}", 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 =
app.get(
"$gokuAPI/ajax/movie/season/episodes/${seasonId ?: return}",
@ -93,15 +93,13 @@ object SoraExtractor : SoraStream() {
val iframe =
app.get("$gokuAPI/ajax/movie/episode/server/sources/$id", headers = headers)
.parsedSafe<GokuServer>()?.data?.link ?: return@apmap
extractRabbitStream(
loadCustomExtractor(
if (iframe.contains("rabbitstream")) "Vidcloud" else "Upcloud",
iframe,
"$gokuAPI/",
subtitleCallback,
callback,
false,
decryptKey = RabbitStream.getKey()
) { it }
)
}
}
@ -417,7 +415,7 @@ object SoraExtractor : SoraStream() {
} else {
"$idlixAPI/episode/$fixTitle-season-$season-episode-$episode"
}
invokeWpmovies(url,subtitleCallback, callback)
invokeWpmovies(url, subtitleCallback, callback)
}
suspend fun invokeMultimovies(
@ -433,7 +431,7 @@ object SoraExtractor : SoraStream() {
} else {
"$multimoviesAPI/episodes/$fixTitle-${season}x${episode}"
}
invokeWpmovies(url,subtitleCallback, callback,true)
invokeWpmovies(url, subtitleCallback, callback, true)
}
suspend fun invokeNetmovies(
@ -450,7 +448,7 @@ object SoraExtractor : SoraStream() {
} else {
"$netmoviesAPI/episodes/$fixTitle-${season}x${episode}"
}
invokeWpmovies(url,subtitleCallback, callback)
invokeWpmovies(url, subtitleCallback, callback)
}
private suspend fun invokeWpmovies(
@ -459,7 +457,7 @@ object SoraExtractor : SoraStream() {
callback: (ExtractorLink) -> Unit,
fixIframe: Boolean = false,
) {
val res = app.get(url ?: return)
val res = session.get(url ?: return)
val referer = getBaseUrl(res.url)
val document = res.document
document.select("ul#playeroptionsul > li").map {
@ -469,11 +467,14 @@ object SoraExtractor : SoraStream() {
it.attr("data-type")
)
}.apmap { (id, nume, type) ->
val source = app.post(
val json = session.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
).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")) {
loadExtractor(source, "$referer/", subtitleCallback, callback)
}
@ -560,9 +561,11 @@ object SoraExtractor : SoraStream() {
)
)
}
!source.contains("youtube") -> loadExtractor(
source, "$uniqueStreamAPI/", subtitleCallback, callback
)
else -> {
// pass
}
@ -912,7 +915,10 @@ object SoraExtractor : SoraStream() {
when {
season == null -> slugTitle?.equals(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
@ -1066,16 +1072,23 @@ object SoraExtractor : SoraStream() {
val headers = mapOf(
"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 ->
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 {
Jsoup.parse(it)
}?.select("div.ss-list a")?.find { it.attr("data-number") == "${episode ?: 1}" }
?.attr("data-id")
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) }
?.select("div.item.server-item")?.map {
Triple(
@ -1086,27 +1099,21 @@ object SoraExtractor : SoraStream() {
}
servers?.apmap servers@{ server ->
val iframe = app.get("$aniwatchAPI/ajax/v2/episode/sources?id=${server.second ?: return@servers}", headers = headers)
.parsedSafe<AniwatchResponses>()?.link ?: return@servers
val iframe = app.get(
"$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"
if (server.first.contains(Regex("Vidstreaming|MegaCloud|Vidcloud"))) {
extractRabbitStream(
"${server.first} [$audio]",
iframe,
"$aniwatchAPI/",
subtitleCallback,
callback,
false,
decryptKey = RabbitStream.getZoroKey()
) { it }
} else {
loadExtractor(iframe, "$aniwatchAPI/", subtitleCallback, callback)
}
loadCustomExtractor(
"${server.first} [$audio]",
iframe,
"$aniwatchAPI/",
subtitleCallback,
callback,
)
}
}
}
suspend fun invokeLing(
@ -1307,9 +1314,11 @@ object SoraExtractor : SoraStream() {
val gdBotLink = extractGdbot(link)
extractGdflix(gdBotLink ?: return@apmap)
}
link.contains("gdflix") -> {
extractGdflix(link)
}
else -> {
return@apmap
}
@ -1476,15 +1485,17 @@ object SoraExtractor : SoraStream() {
val gdBotLink = extractGdbot(fdLink ?: return@apmap null)
extractGdflix(gdBotLink ?: return@apmap null)
}
type.contains("oiya") -> {
val oiyaLink = extractOiya(fdLink ?: return@apmap null, qualities)
if(oiyaLink?.contains("gdtot") == true) {
if (oiyaLink?.contains("gdtot") == true) {
val gdBotLink = extractGdbot(oiyaLink)
extractGdflix(gdBotLink ?: return@apmap null)
} else {
oiyaLink
}
}
else -> {
return@apmap null
}
@ -1634,7 +1645,7 @@ object SoraExtractor : SoraStream() {
callback: (ExtractorLink) -> Unit
) {
val id = getCrunchyrollId("${aniId ?: return}")
?: getCrunchyrollIdFromMalSync("${malId ?: return}") ?: return
?: getCrunchyrollIdFromMalSync("${malId ?: return}")
val audioLocal = listOf(
"ja-JP",
"en-US",
@ -1946,24 +1957,33 @@ object SoraExtractor : SoraStream() {
it.attr("data-id") to it.text()
}.apmap {
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)
}
it.first.contains("/gtop") -> {
invokeSmashyGtop(it.second, it.first, callback)
}
it.first.contains("/dude_tv") -> {
invokeSmashyDude(it.second, it.first, callback)
}
it.first.contains("/rip") -> {
invokeSmashyRip(it.second, it.first, subtitleCallback, callback)
}
it.first.contains("/im.php") && !isAnime -> {
invokeSmashyIm(it.second, it.first, subtitleCallback, callback)
}
it.first.contains("/rw.php") && !isAnime -> {
invokeSmashyRw(it.second, it.first, subtitleCallback, callback)
}
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(
@ -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(
apiUrl: String,
api: String,
@ -2490,7 +2433,7 @@ object SoraExtractor : SoraStream() {
episode: Int? = null,
callback: (ExtractorLink) -> Unit,
) {
val (seasonSlug, episodeSlug) = getEpisodeSlug(season, episode)
val slug = getEpisodeSlug(season, episode)
val query = if (season == null) {
title
} else {
@ -2530,7 +2473,7 @@ object SoraExtractor : SoraStream() {
media.third,
gomoviesAPI
)
).document.selectFirst("div#g_MXOzFGouZrOAUioXjpddqkZK a:contains(Episode $episodeSlug)")
).document.selectFirst("div#g_MXOzFGouZrOAUioXjpddqkZK a:contains(Episode ${slug.second})")
?.attr("href")
} ?: return
@ -2576,7 +2519,6 @@ object SoraExtractor : SoraStream() {
year: Int? = null,
season: Int? = null,
episode: Int? = null,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit,
) {
val query = if (season == null) {
@ -2610,7 +2552,8 @@ object SoraExtractor : SoraStream() {
}
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 m3u8 = Regex("file:\\s*\"(.*?m3u8.*?)\"").find(unpacked)?.groupValues?.getOrNull(1)
M3u8Helper.generateM3u8(
@ -2748,7 +2691,8 @@ object SoraExtractor : SoraStream() {
"$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)
}
@ -2898,7 +2842,6 @@ object SoraExtractor : SoraStream() {
imdbId: String? = null,
title: String? = null,
year: Int? = null,
season: Int? = null,
episode: Int? = null,
callback: (ExtractorLink) -> Unit
) {
@ -2909,7 +2852,7 @@ object SoraExtractor : SoraStream() {
null,
title,
year,
season,
null,
episode,
false
)
@ -2919,7 +2862,11 @@ object SoraExtractor : SoraStream() {
val size = getIndexSize(stream.title)
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(
ExtractorLink(
@ -2943,7 +2890,8 @@ object SoraExtractor : SoraStream() {
) {
val referer = "https://2now.tv/"
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
callback.invoke(
ExtractorLink(
@ -3038,7 +2986,7 @@ object SoraExtractor : SoraStream() {
).text
M3u8Helper.generateM3u8(
if(host == navyAPI) "Navy" else "Moment",
if (host == navyAPI) "Navy" else "Moment",
path,
"${referer}/"
).forEach(callback)
@ -3081,7 +3029,10 @@ object SoraExtractor : SoraStream() {
)
).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 {
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 xtremeMoviesAPI = "https://kartik19.xtrememirror0.workers.dev/0:"
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 papaonMovies2API = "https://m.papaonwork.workers.dev/1:"
const val xMovieAPI = "https://xemovies.to"
@ -789,7 +788,6 @@ open class SoraStream : TmdbProvider() {
res.year,
res.season,
res.episode,
subtitleCallback,
callback
)
},
@ -819,7 +817,6 @@ open class SoraStream : TmdbProvider() {
res.imdbId,
res.title,
res.year,
res.season,
res.episode,
callback
)

View File

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

View File

@ -1,11 +1,9 @@
package com.hexated
import android.util.Base64
import com.fasterxml.jackson.annotation.JsonProperty
import com.hexated.DumpUtils.queryApi
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
@ -19,12 +17,9 @@ import com.hexated.SoraStream.Companion.watchOnlineAPI
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getCaptchaToken
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
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.tryParseJson
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.nicehttp.NiceResponse
import com.lagradost.nicehttp.RequestBodyTypes
import com.lagradost.nicehttp.requestCreator
@ -37,7 +32,6 @@ import okhttp3.RequestBody.Companion.toRequestBody
import org.jsoup.nodes.Document
import java.math.BigInteger
import java.net.*
import java.nio.charset.StandardCharsets
import java.security.*
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec
@ -52,12 +46,6 @@ import kotlin.math.min
val bflixChipperKey = base64DecodeAPI("Yjc=ejM=TzA=YTk=WHE=WnU=bXU=RFo=")
const val bflixKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
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(
"GamMovies",
"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? {
val seasons = arrayOf(
"Winter", "Winter", "Spring", "Spring", "Spring", "Summer",
@ -1182,7 +1197,6 @@ fun getPutlockerQuality(quality: String): Int {
}
}
fun getEpisodeSlug(
season: Int? = null,
episode: Int? = null,
@ -1264,23 +1278,6 @@ fun matchingIndex(
) == 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 {
val slug = json.reversed().substring(24)
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 {
private val deviceId = getDeviceId()