Fix SflixProvider.kt

This commit is contained in:
Blatzar 2022-09-16 16:00:26 +02:00
parent 7ecd74b98f
commit 8910da0fd0
3 changed files with 129 additions and 41 deletions

View file

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

View file

@ -23,7 +23,12 @@ import okhttp3.RequestBody.Companion.toRequestBody
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import java.net.URI
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
import java.util.*
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
import kotlin.system.measureTimeMillis
open class SflixProvider : MainAPI() {
@ -41,7 +46,7 @@ open class SflixProvider : MainAPI() {
)
override val vpnStatus = VPNStatus.None
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
val html = app.get("$mainUrl/home").text
val document = Jsoup.parse(html)
@ -290,10 +295,18 @@ open class SflixProvider : MainAPI() {
)
data class SourceObject(
@JsonProperty("sources") val sources: List<Sources?>?,
@JsonProperty("sources_1") val sources1: List<Sources?>?,
@JsonProperty("sources_2") val sources2: List<Sources?>?,
@JsonProperty("sourcesBackup") val sourcesBackup: List<Sources?>?,
@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("sources_1") val sources1: String?,
@JsonProperty("sources_2") val sources2: String?,
@JsonProperty("sourcesBackup") val sourcesBackup: String?,
@JsonProperty("tracks") val tracks: List<Tracks?>?
)
@ -305,6 +318,15 @@ open class SflixProvider : MainAPI() {
// @JsonProperty("title") val title: String? = null
)
open suspend fun getKey(): String? {
data class KeyObject(
@JsonProperty("key") val key: String? = null
)
return app.get("https://raw.githubusercontent.com/chenkaslowankiya/BruhGlow/main/keys.json")
.parsed<KeyObject>().key
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
@ -347,7 +369,13 @@ open class SflixProvider : MainAPI() {
if (iframeLink.contains("streamlare", ignoreCase = true)) {
loadExtractor(iframeLink, null, subtitleCallback, callback)
} else {
extractRabbitStream(iframeLink, subtitleCallback, callback, false) { it }
extractRabbitStream(
iframeLink,
subtitleCallback,
callback,
false,
decryptKey = getKey()
) { it }
}
}
}
@ -468,7 +496,7 @@ open class SflixProvider : MainAPI() {
if (!response.okhttpResponse.isSuccessful) {
return negotiateNewSid(baseUrl)?.let {
it to true
} ?: data to false
} ?: (data to false)
}
return data to false
}
@ -652,6 +680,49 @@ open class SflixProvider : MainAPI() {
}
}
private fun md5(input: ByteArray): ByteArray {
return MessageDigest.getInstance("MD5").digest(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 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)
}
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
)
}
suspend fun MainAPI.extractRabbitStream(
url: String,
subtitleCallback: (SubtitleFile) -> Unit,
@ -659,6 +730,7 @@ open class SflixProvider : MainAPI() {
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
@ -666,13 +738,13 @@ open class SflixProvider : MainAPI() {
url.substringBeforeLast("/")
val mainIframeId = url.substringAfterLast("/")
.substringBefore("?") // https://rapid-cloud.ru/embed-6/dcPOVRE57YOT?z= -> dcPOVRE57YOT
val iframe = app.get(url, referer = mainUrl)
val iframeKey =
iframe.document.select("script[src*=https://www.google.com/recaptcha/api.js?render=]")
.attr("src").substringAfter("render=")
val iframeToken = getCaptchaToken(url, iframeKey)
val number =
Regex("""recaptchaNumber = '(.*?)'""").find(iframe.text)?.groupValues?.get(1)
// val iframe = app.get(url, referer = mainUrl)
// val iframeKey =
// iframe.document.select("script[src*=https://www.google.com/recaptcha/api.js?render=]")
// .attr("src").substringAfter("render=")
// val iframeToken = getCaptchaToken(url, iframeKey)
// val number =
// Regex("""recaptchaNumber = '(.*?)'""").find(iframe.text)?.groupValues?.get(1)
var sid: String? = null
if (useSidAuthentication && extractorData != null) {
@ -691,42 +763,52 @@ open class SflixProvider : MainAPI() {
ioSafe { app.get("$extractorData&t=${generateTimeStamp()}&sid=${pollingData.sid}") }
}
}
val mapped = app.get(
"${
val getSourcesUrl = "${
mainIframeUrl.replace(
"/embed",
"/ajax/embed"
)
}/getSources?id=$mainIframeId&_token=$iframeToken&_number=$number${sid?.let { "$&sId=$it" } ?: ""}",
}/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",
// "Cache-Control" to "no-cache",
"Connection" to "keep-alive",
// "Sec-Fetch-Dest" to "empty",
// "Sec-Fetch-Mode" to "no-cors",
// "Sec-Fetch-Site" to "cross-site",
// "Pragma" to "no-cache",
// "Cache-Control" to "no-cache",
"TE" to "trailers"
)
).parsed<SourceObject>()
)
mapped.tracks?.forEach { track ->
val sourceObject = if (response.text.contains("encrypted") && decryptKey != null) {
val encryptedMap = response.parsedSafe<SourceObjectEncrypted>()
val sources =
encryptedMap?.sources ?: throw RuntimeException("NO SOURCES $encryptedMap")
val decrypted = decryptMapped<List<Sources>>(sources, decryptKey)
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(
mapped.sources to "source 1",
mapped.sources1 to "source 2",
mapped.sources2 to "source 3",
mapped.sourcesBackup to "source backup"
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(

View file

@ -322,6 +322,10 @@ class ZoroProvider : MainAPI() {
}
}
private suspend fun getKey(): String {
return app.get("https://raw.githubusercontent.com/consumet/rapidclown/main/key.txt").text
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
@ -349,21 +353,23 @@ class ZoroProvider : MainAPI() {
val extractorLink = app.get(
link,
).parsed<RapidCloudResponse>().link
val hasLoadedExtractorLink =
loadExtractor(extractorLink, "https://rapid-cloud.ru/", subtitleCallback, callback)
// val hasLoadedExtractorLink =
// loadExtractor(extractorLink, "https://rapid-cloud.ru/", subtitleCallback, callback)
if (!hasLoadedExtractorLink) {
// if (!hasLoadedExtractorLink) {
extractRabbitStream(
extractorLink,
subtitleCallback,
// Blacklist VidCloud for now
{ videoLink -> if (!videoLink.url.contains("betterstream")) callback(videoLink) },
true,
extractorData
false,
// extractorData,
decryptKey = getKey()
) { sourceName ->
sourceName + " - ${it.first}"
}
}
// }
}
return true