mirror of
https://github.com/hexated/cloudstream-extensions-hexated.git
synced 2024-08-15 00:03:22 +00:00
fix idlix
This commit is contained in:
parent
c510461e47
commit
b04f604e74
7 changed files with 119 additions and 477 deletions
|
@ -1,5 +1,5 @@
|
|||
// use an integer for version numbers
|
||||
version = 11
|
||||
version = 12
|
||||
|
||||
|
||||
cloudstream {
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import org.jetbrains.kotlin.konan.properties.Properties
|
||||
|
||||
// use an integer for version numbers
|
||||
version = 153
|
||||
version = 154
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
|
|
|
@ -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,7 +45,8 @@ 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")
|
||||
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) &&
|
||||
|
@ -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)
|
||||
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(
|
||||
loadCustomExtractor(
|
||||
"${server.first} [$audio]",
|
||||
iframe,
|
||||
"$aniwatchAPI/",
|
||||
subtitleCallback,
|
||||
callback,
|
||||
false,
|
||||
decryptKey = RabbitStream.getZoroKey()
|
||||
) { it }
|
||||
} else {
|
||||
loadExtractor(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]")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -276,7 +276,6 @@ class SoraStreamLite : SoraStream() {
|
|||
res.year,
|
||||
res.season,
|
||||
res.episode,
|
||||
subtitleCallback,
|
||||
callback
|
||||
)
|
||||
},
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue