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
|
// use an integer for version numbers
|
||||||
version = 11
|
version = 12
|
||||||
|
|
||||||
|
|
||||||
cloudstream {
|
cloudstream {
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,7 +45,8 @@ 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 =
|
||||||
|
ele.select("div.info-split > div:nth-child(2)").text().substringAfter("SS")
|
||||||
.substringBefore("/").trim().toIntOrNull()
|
.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) &&
|
||||||
|
@ -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(
|
||||||
|
"$aniwatchAPI/ajax/v2/episode/sources?id=${server.second ?: return@servers}",
|
||||||
|
headers = headers
|
||||||
|
)
|
||||||
.parsedSafe<AniwatchResponses>()?.link ?: return@servers
|
.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]")
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
|
@ -276,7 +276,6 @@ class SoraStreamLite : SoraStream() {
|
||||||
res.year,
|
res.year,
|
||||||
res.season,
|
res.season,
|
||||||
res.episode,
|
res.episode,
|
||||||
subtitleCallback,
|
|
||||||
callback
|
callback
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in a new issue