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 // use an integer for version numbers
version = 2 version = 3
cloudstream { cloudstream {

View file

@ -23,7 +23,12 @@ import okhttp3.RequestBody.Companion.toRequestBody
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import java.net.URI import java.net.URI
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
import java.util.* import java.util.*
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
open class SflixProvider : MainAPI() { open class SflixProvider : MainAPI() {
@ -41,7 +46,7 @@ open class SflixProvider : MainAPI() {
) )
override val vpnStatus = VPNStatus.None 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 html = app.get("$mainUrl/home").text
val document = Jsoup.parse(html) val document = Jsoup.parse(html)
@ -290,10 +295,18 @@ open class SflixProvider : MainAPI() {
) )
data class SourceObject( data class SourceObject(
@JsonProperty("sources") val sources: List<Sources?>?, @JsonProperty("sources") val sources: List<Sources?>? = null,
@JsonProperty("sources_1") val sources1: List<Sources?>?, @JsonProperty("sources_1") val sources1: List<Sources?>? = null,
@JsonProperty("sources_2") val sources2: List<Sources?>?, @JsonProperty("sources_2") val sources2: List<Sources?>? = null,
@JsonProperty("sourcesBackup") val sourcesBackup: List<Sources?>?, @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?>? @JsonProperty("tracks") val tracks: List<Tracks?>?
) )
@ -305,6 +318,15 @@ open class SflixProvider : MainAPI() {
// @JsonProperty("title") val title: String? = null // @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( override suspend fun loadLinks(
data: String, data: String,
isCasting: Boolean, isCasting: Boolean,
@ -347,7 +369,13 @@ open class SflixProvider : MainAPI() {
if (iframeLink.contains("streamlare", ignoreCase = true)) { if (iframeLink.contains("streamlare", ignoreCase = true)) {
loadExtractor(iframeLink, null, subtitleCallback, callback) loadExtractor(iframeLink, null, subtitleCallback, callback)
} else { } 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) { if (!response.okhttpResponse.isSuccessful) {
return negotiateNewSid(baseUrl)?.let { return negotiateNewSid(baseUrl)?.let {
it to true it to true
} ?: data to false } ?: (data to false)
} }
return 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( suspend fun MainAPI.extractRabbitStream(
url: String, url: String,
subtitleCallback: (SubtitleFile) -> Unit, subtitleCallback: (SubtitleFile) -> Unit,
@ -659,6 +730,7 @@ open class SflixProvider : MainAPI() {
useSidAuthentication: Boolean, useSidAuthentication: Boolean,
/** Used for extractorLink name, input: Source name */ /** Used for extractorLink name, input: Source name */
extractorData: String? = null, extractorData: String? = null,
decryptKey: String? = null,
nameTransformer: (String) -> String, nameTransformer: (String) -> String,
) = suspendSafeApiCall { ) = suspendSafeApiCall {
// https://rapid-cloud.ru/embed-6/dcPOVRE57YOT?z= -> https://rapid-cloud.ru/embed-6 // https://rapid-cloud.ru/embed-6/dcPOVRE57YOT?z= -> https://rapid-cloud.ru/embed-6
@ -666,13 +738,13 @@ open class SflixProvider : MainAPI() {
url.substringBeforeLast("/") url.substringBeforeLast("/")
val mainIframeId = url.substringAfterLast("/") val mainIframeId = url.substringAfterLast("/")
.substringBefore("?") // https://rapid-cloud.ru/embed-6/dcPOVRE57YOT?z= -> dcPOVRE57YOT .substringBefore("?") // https://rapid-cloud.ru/embed-6/dcPOVRE57YOT?z= -> dcPOVRE57YOT
val iframe = app.get(url, referer = mainUrl) // val iframe = app.get(url, referer = mainUrl)
val iframeKey = // val iframeKey =
iframe.document.select("script[src*=https://www.google.com/recaptcha/api.js?render=]") // iframe.document.select("script[src*=https://www.google.com/recaptcha/api.js?render=]")
.attr("src").substringAfter("render=") // .attr("src").substringAfter("render=")
val iframeToken = getCaptchaToken(url, iframeKey) // val iframeToken = getCaptchaToken(url, iframeKey)
val number = // val number =
Regex("""recaptchaNumber = '(.*?)'""").find(iframe.text)?.groupValues?.get(1) // Regex("""recaptchaNumber = '(.*?)'""").find(iframe.text)?.groupValues?.get(1)
var sid: String? = null var sid: String? = null
if (useSidAuthentication && extractorData != null) { if (useSidAuthentication && extractorData != null) {
@ -691,42 +763,52 @@ open class SflixProvider : MainAPI() {
ioSafe { app.get("$extractorData&t=${generateTimeStamp()}&sid=${pollingData.sid}") } ioSafe { app.get("$extractorData&t=${generateTimeStamp()}&sid=${pollingData.sid}") }
} }
} }
val getSourcesUrl = "${
val mapped = app.get( mainIframeUrl.replace(
"${ "/embed",
mainIframeUrl.replace( "/ajax/embed"
"/embed", )
"/ajax/embed" }/getSources?id=$mainIframeId${sid?.let { "$&sId=$it" } ?: ""}"
) val response = app.get(
}/getSources?id=$mainIframeId&_token=$iframeToken&_number=$number${sid?.let { "$&sId=$it" } ?: ""}", getSourcesUrl,
referer = mainUrl, referer = mainUrl,
headers = mapOf( headers = mapOf(
"X-Requested-With" to "XMLHttpRequest", "X-Requested-With" to "XMLHttpRequest",
"Accept" to "*/*", "Accept" to "*/*",
"Accept-Language" to "en-US,en;q=0.5", "Accept-Language" to "en-US,en;q=0.5",
// "Cache-Control" to "no-cache",
"Connection" to "keep-alive", "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" "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 -> track?.toSubtitleFile()?.let { subtitleFile ->
subtitleCallback.invoke(subtitleFile) subtitleCallback.invoke(subtitleFile)
} }
} }
val list = listOf( val list = listOf(
mapped.sources to "source 1", sourceObject.sources to "source 1",
mapped.sources1 to "source 2", sourceObject.sources1 to "source 2",
mapped.sources2 to "source 3", sourceObject.sources2 to "source 3",
mapped.sourcesBackup to "source backup" sourceObject.sourcesBackup to "source backup"
) )
list.forEach { subList -> list.forEach { subList ->
subList.first?.forEach { source -> subList.first?.forEach { source ->
source?.toExtractorLink( 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( override suspend fun loadLinks(
data: String, data: String,
isCasting: Boolean, isCasting: Boolean,
@ -349,21 +353,23 @@ class ZoroProvider : MainAPI() {
val extractorLink = app.get( val extractorLink = app.get(
link, link,
).parsed<RapidCloudResponse>().link ).parsed<RapidCloudResponse>().link
val hasLoadedExtractorLink = // val hasLoadedExtractorLink =
loadExtractor(extractorLink, "https://rapid-cloud.ru/", subtitleCallback, callback) // loadExtractor(extractorLink, "https://rapid-cloud.ru/", subtitleCallback, callback)
if (!hasLoadedExtractorLink) { // if (!hasLoadedExtractorLink) {
extractRabbitStream( extractRabbitStream(
extractorLink, extractorLink,
subtitleCallback, subtitleCallback,
// Blacklist VidCloud for now // Blacklist VidCloud for now
{ videoLink -> if (!videoLink.url.contains("betterstream")) callback(videoLink) }, { videoLink -> if (!videoLink.url.contains("betterstream")) callback(videoLink) },
true, false,
extractorData // extractorData,
decryptKey = getKey()
) { sourceName -> ) { sourceName ->
sourceName + " - ${it.first}" sourceName + " - ${it.first}"
} }
} // }
} }
return true return true