Fix SflixProvider
This commit is contained in:
parent
3fdb1849d5
commit
2bc61ed11a
|
@ -1,12 +1,12 @@
|
||||||
// use an integer for version numbers
|
// use an integer for version numbers
|
||||||
version = 7
|
version = 8
|
||||||
|
|
||||||
|
|
||||||
cloudstream {
|
cloudstream {
|
||||||
language = "en"
|
language = "en"
|
||||||
// All of these properties are optional, you can safely remove them
|
// All of these properties are optional, you can safely remove them
|
||||||
|
|
||||||
description = "Also includes Dopebox, Solarmovie, Zoro and 2embed"
|
description = "Also includes Dopebox, Solarmovie, Zoro, HDToday and 2embed"
|
||||||
// authors = listOf("Cloudburst")
|
// authors = listOf("Cloudburst")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,6 +20,8 @@ cloudstream {
|
||||||
tvTypes = listOf(
|
tvTypes = listOf(
|
||||||
"TvSeries",
|
"TvSeries",
|
||||||
"Movie",
|
"Movie",
|
||||||
|
"Anime",
|
||||||
|
"AnimeMovie",
|
||||||
)
|
)
|
||||||
|
|
||||||
iconUrl = "https://www.google.com/s2/favicons?domain=www.2embed.to&sz=%size%"
|
iconUrl = "https://www.google.com/s2/favicons?domain=www.2embed.to&sz=%size%"
|
||||||
|
|
|
@ -3,23 +3,22 @@ package com.lagradost
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.APIHolder.getCaptchaToken
|
|
||||||
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
|
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
|
||||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
|
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
|
||||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addDuration
|
import com.lagradost.cloudstream3.LoadResponse.Companion.addDuration
|
||||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||||
//import com.lagradost.cloudstream3.animeproviders.ZoroProvider
|
import com.lagradost.cloudstream3.mvvm.Resource
|
||||||
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
|
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
import com.lagradost.cloudstream3.utils.M3u8Helper
|
import com.lagradost.cloudstream3.utils.M3u8Helper
|
||||||
import com.lagradost.cloudstream3.utils.getQualityFromName
|
import com.lagradost.cloudstream3.utils.getQualityFromName
|
||||||
import com.lagradost.cloudstream3.utils.loadExtractor
|
import com.lagradost.cloudstream3.utils.loadExtractor
|
||||||
import com.lagradost.nicehttp.NiceResponse
|
import com.lagradost.nicehttp.requestCreator
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import okhttp3.RequestBody.Companion.toRequestBody
|
import okhttp3.WebSocket
|
||||||
|
import okhttp3.WebSocketListener
|
||||||
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
|
||||||
|
@ -29,7 +28,6 @@ import java.util.*
|
||||||
import javax.crypto.Cipher
|
import javax.crypto.Cipher
|
||||||
import javax.crypto.spec.IvParameterSpec
|
import javax.crypto.spec.IvParameterSpec
|
||||||
import javax.crypto.spec.SecretKeySpec
|
import javax.crypto.spec.SecretKeySpec
|
||||||
import kotlin.system.measureTimeMillis
|
|
||||||
|
|
||||||
open class SflixProvider : MainAPI() {
|
open class SflixProvider : MainAPI() {
|
||||||
override var mainUrl = "https://sflix.to"
|
override var mainUrl = "https://sflix.to"
|
||||||
|
@ -355,18 +353,15 @@ open class SflixProvider : MainAPI() {
|
||||||
?: return@suspendSafeApiCall
|
?: return@suspendSafeApiCall
|
||||||
|
|
||||||
// Some smarter ws11 or w10 selection might be required in the future.
|
// Some smarter ws11 or w10 selection might be required in the future.
|
||||||
val extractorData =
|
// val extractorData =
|
||||||
"https://ws11.rabbitstream.net/socket.io/?EIO=4&transport=polling"
|
// "https://ws11.rabbitstream.net/socket.io/?EIO=4&transport=polling"
|
||||||
|
|
||||||
if (iframeLink.contains("streamlare", ignoreCase = true)) {
|
val hasLoadedExtractor = loadExtractor(iframeLink, null, subtitleCallback, callback)
|
||||||
loadExtractor(iframeLink, null, subtitleCallback, callback)
|
if (!hasLoadedExtractor) {
|
||||||
} else {
|
|
||||||
extractRabbitStream(
|
extractRabbitStream(
|
||||||
iframeLink,
|
iframeLink,
|
||||||
subtitleCallback,
|
subtitleCallback,
|
||||||
callback,
|
callback,
|
||||||
false,
|
|
||||||
decryptKey = getKey()
|
|
||||||
) { it }
|
) { it }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -375,10 +370,6 @@ open class SflixProvider : MainAPI() {
|
||||||
return !urls.isNullOrEmpty()
|
return !urls.isNullOrEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun extractorVerifierJob(extractorData: String?) {
|
|
||||||
runSflixExtractorVerifierJob(this, extractorData, "https://rabbitstream.net/")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Element.toSearchResult(): SearchResponse {
|
private fun Element.toSearchResult(): SearchResponse {
|
||||||
val inner = this.selectFirst("div.film-poster")
|
val inner = this.selectFirst("div.film-poster")
|
||||||
val img = inner!!.select("img")
|
val img = inner!!.select("img")
|
||||||
|
@ -459,149 +450,69 @@ open class SflixProvider : MainAPI() {
|
||||||
return code.reversed()
|
return code.reversed()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getKey(): String? {
|
fun getSourceObject(responseJson: String?, decryptKey: String?): SourceObject? {
|
||||||
data class KeyObject(
|
if (responseJson == null) return null
|
||||||
@JsonProperty("key") val key: String? = null
|
return if (decryptKey != null) {
|
||||||
)
|
val encryptedMap = tryParseJson<SourceObjectEncrypted>(responseJson)
|
||||||
return app.get("https://raw.githubusercontent.com/BlipBlob/blabflow/main/keys.json")
|
val sources = encryptedMap?.sources
|
||||||
.parsed<KeyObject>().key
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
if (sources == null || encryptedMap.encrypted == false) {
|
||||||
* Generates a session
|
tryParseJson(responseJson)
|
||||||
* 1 Get request.
|
} else {
|
||||||
* */
|
val decrypted = decryptMapped<List<Sources>>(sources, decryptKey)
|
||||||
private suspend fun negotiateNewSid(baseUrl: String): PollingData? {
|
SourceObject(
|
||||||
// Tries multiple times
|
sources = decrypted,
|
||||||
for (i in 1..5) {
|
tracks = encryptedMap.tracks
|
||||||
val jsonText =
|
|
||||||
app.get("$baseUrl&t=${generateTimeStamp()}").text.replaceBefore("{", "")
|
|
||||||
// println("Negotiated sid $jsonText")
|
|
||||||
parseJson<PollingData?>(jsonText)?.let { return it }
|
|
||||||
delay(1000L * i)
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a new session if the request fails
|
|
||||||
* @return the data and if it is new.
|
|
||||||
* */
|
|
||||||
private suspend fun getUpdatedData(
|
|
||||||
response: NiceResponse,
|
|
||||||
data: PollingData,
|
|
||||||
baseUrl: String
|
|
||||||
): Pair<PollingData, Boolean> {
|
|
||||||
if (!response.okhttpResponse.isSuccessful) {
|
|
||||||
return negotiateNewSid(baseUrl)?.let {
|
|
||||||
it to true
|
|
||||||
} ?: (data to false)
|
|
||||||
}
|
|
||||||
return data to false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private suspend fun initPolling(
|
|
||||||
extractorData: String,
|
|
||||||
referer: String
|
|
||||||
): Pair<PollingData?, String?> {
|
|
||||||
val headers = mapOf(
|
|
||||||
"Referer" to referer // "https://rabbitstream.net/"
|
|
||||||
)
|
|
||||||
|
|
||||||
val data = negotiateNewSid(extractorData) ?: return null to null
|
|
||||||
app.post(
|
|
||||||
"$extractorData&t=${generateTimeStamp()}&sid=${data.sid}",
|
|
||||||
requestBody = "40".toRequestBody(),
|
|
||||||
headers = headers
|
|
||||||
)
|
|
||||||
|
|
||||||
// This makes the second get request work, and re-connect work.
|
|
||||||
val reconnectSid =
|
|
||||||
parseJson<PollingData>(
|
|
||||||
app.get(
|
|
||||||
"$extractorData&t=${generateTimeStamp()}&sid=${data.sid}",
|
|
||||||
headers = headers
|
|
||||||
)
|
)
|
||||||
// .also { println("First get ${it.text}") }
|
}
|
||||||
.text.replaceBefore("{", "")
|
} else {
|
||||||
).sid
|
tryParseJson(responseJson)
|
||||||
|
}
|
||||||
// This response is used in the post requests. Same contents in all it seems.
|
|
||||||
val authInt =
|
|
||||||
app.get(
|
|
||||||
"$extractorData&t=${generateTimeStamp()}&sid=${data.sid}",
|
|
||||||
timeout = 60,
|
|
||||||
headers = headers
|
|
||||||
).text
|
|
||||||
//.also { println("Second get ${it}") }
|
|
||||||
// Dunno if it's actually generated like this, just guessing.
|
|
||||||
.toIntOrNull()?.plus(1) ?: 3
|
|
||||||
|
|
||||||
return data to reconnectSid
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun runSflixExtractorVerifierJob(
|
private fun getSources(
|
||||||
api: MainAPI,
|
socketUrl: String,
|
||||||
extractorData: String?,
|
id: String,
|
||||||
referer: String
|
callback: suspend (Resource<SourceObject>) -> Unit
|
||||||
) {
|
) {
|
||||||
if (extractorData == null) return
|
app.baseClient.newWebSocket(
|
||||||
val headers = mapOf(
|
requestCreator("GET", socketUrl),
|
||||||
"Referer" to referer // "https://rabbitstream.net/"
|
object : WebSocketListener() {
|
||||||
)
|
val sidRegex = Regex("""sid.*"(.*?)"""")
|
||||||
|
val sourceRegex = Regex("""\{.*\}""")
|
||||||
|
val codeRegex = Regex("""^\d*""")
|
||||||
|
|
||||||
lateinit var data: PollingData
|
var key: String? = null
|
||||||
var reconnectSid = ""
|
|
||||||
|
|
||||||
initPolling(extractorData, referer)
|
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
|
||||||
.also {
|
ioSafe {
|
||||||
data = it.first ?: throw RuntimeException("Data Null")
|
callback(Resource.Failure(false, code, null, reason))
|
||||||
reconnectSid = it.second ?: throw RuntimeException("ReconnectSid Null")
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Prevents them from fucking us over with doing a while(true){} loop
|
|
||||||
val interval = maxOf(data.pingInterval?.toLong()?.plus(2000) ?: return, 10000L)
|
|
||||||
var reconnect = false
|
|
||||||
var newAuth = false
|
|
||||||
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
val authData =
|
|
||||||
when {
|
|
||||||
newAuth -> "40"
|
|
||||||
reconnect -> """42["_reconnect", "$reconnectSid"]"""
|
|
||||||
else -> "3"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val url = "${extractorData}&t=${generateTimeStamp()}&sid=${data.sid}"
|
override fun onMessage(webSocket: WebSocket, text: String) {
|
||||||
|
Log.d("getSources", "onMessage $text")
|
||||||
|
val code = codeRegex.find(text)?.value?.toIntOrNull() ?: return
|
||||||
|
|
||||||
getUpdatedData(
|
when (code) {
|
||||||
app.post(url, json = authData, headers = headers),
|
0 -> webSocket.send("40")
|
||||||
data,
|
40 -> {
|
||||||
extractorData
|
key = sidRegex.find(text)?.groupValues?.get(1)
|
||||||
).also {
|
webSocket.send("""42["getSources",{"id":"$id"}]""")
|
||||||
newAuth = it.second
|
}
|
||||||
data = it.first
|
42 -> {
|
||||||
|
val response = sourceRegex.find(text)?.value
|
||||||
|
val sourceObject = getSourceObject(response, key)
|
||||||
|
val resource = if (sourceObject == null)
|
||||||
|
Resource.Failure(false, null, null, response ?: "")
|
||||||
|
else Resource.Success(sourceObject)
|
||||||
|
ioSafe { callback(resource) }
|
||||||
|
webSocket.close(1005, "41")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
)
|
||||||
//.also { println("Sflix post job ${it.text}") }
|
|
||||||
Log.d(api.name, "Running ${api.name} job $url")
|
|
||||||
|
|
||||||
val time = measureTimeMillis {
|
|
||||||
// This acts as a timeout
|
|
||||||
val getResponse = app.get(
|
|
||||||
url,
|
|
||||||
timeout = interval / 1000,
|
|
||||||
headers = headers
|
|
||||||
)
|
|
||||||
// .also { println("Sflix get job ${it.text}") }
|
|
||||||
reconnect = getResponse.text.contains("sid")
|
|
||||||
}
|
|
||||||
// Always waits even if the get response is instant, to prevent a while true loop.
|
|
||||||
if (time < interval - 4000)
|
|
||||||
delay(4000)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only scrape servers with these names
|
// Only scrape servers with these names
|
||||||
|
@ -611,7 +522,8 @@ open class SflixProvider : MainAPI() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// For re-use in Zoro
|
// For re-use in Zoro
|
||||||
private suspend fun Sources.toExtractorLink(
|
private suspend
|
||||||
|
fun Sources.toExtractorLink(
|
||||||
caller: MainAPI,
|
caller: MainAPI,
|
||||||
name: String,
|
name: String,
|
||||||
extractorData: String? = null,
|
extractorData: String? = null,
|
||||||
|
@ -693,7 +605,10 @@ open class SflixProvider : MainAPI() {
|
||||||
return currentKey
|
return currentKey
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun decryptSourceUrl(decryptionKey: ByteArray, sourceUrl: String): String {
|
private fun decryptSourceUrl(
|
||||||
|
decryptionKey: ByteArray,
|
||||||
|
sourceUrl: String
|
||||||
|
): String {
|
||||||
val cipherData = base64DecodeArray(sourceUrl)
|
val cipherData = base64DecodeArray(sourceUrl)
|
||||||
val encrypted = cipherData.copyOfRange(16, cipherData.size)
|
val encrypted = cipherData.copyOfRange(16, cipherData.size)
|
||||||
val aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
val aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
||||||
|
@ -709,7 +624,8 @@ open class SflixProvider : MainAPI() {
|
||||||
return String(decryptedData, StandardCharsets.UTF_8)
|
return String(decryptedData, StandardCharsets.UTF_8)
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun <reified T> decryptMapped(input: String, key: String): T? {
|
private inline
|
||||||
|
fun <reified T> decryptMapped(input: String, key: String): T? {
|
||||||
return tryParseJson(decrypt(input, key))
|
return tryParseJson(decrypt(input, key))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -726,104 +642,89 @@ open class SflixProvider : MainAPI() {
|
||||||
url: String,
|
url: String,
|
||||||
subtitleCallback: (SubtitleFile) -> Unit,
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
callback: (ExtractorLink) -> Unit,
|
callback: (ExtractorLink) -> Unit,
|
||||||
useSidAuthentication: Boolean,
|
|
||||||
/** Used for extractorLink name, input: Source name */
|
|
||||||
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
|
||||||
val mainIframeUrl =
|
// val mainIframeUrl =
|
||||||
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 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
|
var isDone = false
|
||||||
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 = parseJson<PollingData>(text).sid
|
// Hardcoded for now, does not support Zoro yet.
|
||||||
ioSafe { app.get("$extractorData&t=${generateTimeStamp()}&sid=${pollingData.sid}") }
|
getSources(
|
||||||
|
"wss://wsx.dokicloud.one/socket.io/?EIO=4&transport=websocket",
|
||||||
|
mainIframeId
|
||||||
|
) { sourceResource ->
|
||||||
|
if (sourceResource !is Resource.Success) {
|
||||||
|
isDone = true
|
||||||
|
return@getSources
|
||||||
}
|
}
|
||||||
}
|
|
||||||
val getSourcesUrl = "${
|
val sourceObject = sourceResource.value
|
||||||
mainIframeUrl.replace(
|
|
||||||
"/embed",
|
sourceObject.tracks?.forEach { track ->
|
||||||
"/ajax/embed"
|
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"
|
||||||
)
|
)
|
||||||
}/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"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
println("Sflix response: $response")
|
list.forEach { subList ->
|
||||||
val sourceObject = if (response.text.contains("encrypted") && decryptKey != null) {
|
subList.first?.forEach { source ->
|
||||||
val encryptedMap = response.parsedSafe<SourceObjectEncrypted>()
|
source?.toExtractorLink(
|
||||||
val sources = encryptedMap?.sources
|
this,
|
||||||
if (sources == null || encryptedMap.encrypted == false) {
|
nameTransformer(subList.second),
|
||||||
response.parsedSafe()
|
)?.forEach(callback)
|
||||||
} else {
|
}
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
isDone = true
|
||||||
}
|
}
|
||||||
|
|
||||||
val list = listOf(
|
var elapsedTime = 0
|
||||||
sourceObject.sources to "source 1",
|
val maxTime = 30
|
||||||
sourceObject.sources1 to "source 2",
|
|
||||||
sourceObject.sources2 to "source 3",
|
|
||||||
sourceObject.sourcesBackup to "source backup"
|
|
||||||
)
|
|
||||||
|
|
||||||
list.forEach { subList ->
|
while (elapsedTime < maxTime && !isDone) {
|
||||||
subList.first?.forEach { source ->
|
elapsedTime++
|
||||||
source?.toExtractorLink(
|
delay(1_000)
|
||||||
this,
|
|
||||||
nameTransformer(subList.second),
|
|
||||||
extractorData,
|
|
||||||
)
|
|
||||||
?.forEach {
|
|
||||||
// Sets Zoro SID used for video loading
|
|
||||||
// (this as? ZoroProvider)?.sid?.set(it.url.hashCode(), sid)
|
|
||||||
callback(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//// 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 sid = null
|
||||||
|
// val getSourcesUrl = "${
|
||||||
|
// mainIframeUrl.replace(
|
||||||
|
// "/embed",
|
||||||
|
// "/ajax/embed"
|
||||||
|
// )
|
||||||
|
// }/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"
|
||||||
|
// )
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// println("Sflix response: $response")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
package com.lagradost
|
package com.lagradost
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import com.lagradost.SflixProvider.Companion.extractRabbitStream
|
import com.lagradost.SflixProvider.Companion.extractRabbitStream
|
||||||
import com.lagradost.SflixProvider.Companion.runSflixExtractorVerifierJob
|
|
||||||
import com.lagradost.cloudstream3.APIHolder.getCaptchaToken
|
import com.lagradost.cloudstream3.APIHolder.getCaptchaToken
|
||||||
import com.lagradost.cloudstream3.SubtitleFile
|
import com.lagradost.cloudstream3.SubtitleFile
|
||||||
import com.lagradost.cloudstream3.TvType
|
import com.lagradost.cloudstream3.TvType
|
||||||
|
@ -64,7 +62,7 @@ class TwoEmbedProvider : TmdbProvider() {
|
||||||
val mappedservers = parseJson<EmbedJson>(ajax)
|
val mappedservers = parseJson<EmbedJson>(ajax)
|
||||||
val iframeLink = mappedservers.link
|
val iframeLink = mappedservers.link
|
||||||
if (iframeLink.contains("rabbitstream")) {
|
if (iframeLink.contains("rabbitstream")) {
|
||||||
extractRabbitStream(iframeLink, subtitleCallback, callback, false, decryptKey = SflixProvider.getKey()) { it }
|
extractRabbitStream(iframeLink, subtitleCallback, callback) { it }
|
||||||
} else {
|
} else {
|
||||||
loadExtractor(iframeLink, embedUrl, subtitleCallback, callback)
|
loadExtractor(iframeLink, embedUrl, subtitleCallback, callback)
|
||||||
}
|
}
|
||||||
|
@ -72,8 +70,8 @@ class TwoEmbedProvider : TmdbProvider() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun extractorVerifierJob(extractorData: String?) {
|
// override suspend fun extractorVerifierJob(extractorData: String?) {
|
||||||
Log.d(this.name, "Starting ${this.name} job!")
|
// Log.d(this.name, "Starting ${this.name} job!")
|
||||||
runSflixExtractorVerifierJob(this, extractorData, "https://rabbitstream.net/")
|
// runSflixExtractorVerifierJob(this, extractorData, "https://rabbitstream.net/")
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
package com.lagradost
|
package com.lagradost
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import com.lagradost.SflixProvider.Companion.extractRabbitStream
|
|
||||||
import com.lagradost.SflixProvider.Companion.runSflixExtractorVerifierJob
|
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
|
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
|
||||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
|
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
|
||||||
|
@ -281,11 +278,6 @@ class ZoroProvider : MainAPI() {
|
||||||
@JsonProperty("link") val link: String
|
@JsonProperty("link") val link: String
|
||||||
)
|
)
|
||||||
|
|
||||||
override suspend fun extractorVerifierJob(extractorData: String?) {
|
|
||||||
Log.d(this.name, "Starting ${this.name} job!")
|
|
||||||
runSflixExtractorVerifierJob(this, extractorData, "https://rapid-cloud.ru/")
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Url hashcode to sid */
|
/** Url hashcode to sid */
|
||||||
var sid: HashMap<Int, String?> = hashMapOf()
|
var sid: HashMap<Int, String?> = hashMapOf()
|
||||||
|
|
||||||
|
@ -343,8 +335,8 @@ class ZoroProvider : MainAPI() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val extractorData =
|
// val extractorData =
|
||||||
"https://ws1.rapid-cloud.ru/socket.io/?EIO=4&transport=polling"
|
// "https://ws1.rapid-cloud.ru/socket.io/?EIO=4&transport=polling"
|
||||||
|
|
||||||
// Prevent duplicates
|
// Prevent duplicates
|
||||||
servers.distinctBy { it.second }.apmap {
|
servers.distinctBy { it.second }.apmap {
|
||||||
|
@ -354,21 +346,19 @@ class ZoroProvider : MainAPI() {
|
||||||
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) },
|
||||||
false,
|
//// false,
|
||||||
// extractorData,
|
//// extractorData,
|
||||||
decryptKey = getKey()
|
//// decryptKey = getKey()
|
||||||
|
// ) { sourceName ->
|
||||||
) { sourceName ->
|
// sourceName + " - ${it.first}"
|
||||||
sourceName + " - ${it.first}"
|
// }
|
||||||
}
|
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue