optimized zoro and probably fixed sflix downloading

This commit is contained in:
Blatzar 2022-02-26 00:04:00 +01:00
parent aab7e2dc6c
commit c4d4173694
3 changed files with 88 additions and 93 deletions

View file

@ -1,13 +1,15 @@
package com.lagradost.cloudstream3.animeproviders
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.databind.util.NameTransformer
import com.fasterxml.jackson.module.kotlin.readValue
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getCaptchaToken
import com.lagradost.cloudstream3.movieproviders.SflixProvider
import com.lagradost.cloudstream3.movieproviders.SflixProvider.Companion.extractRabbitStream
import com.lagradost.cloudstream3.movieproviders.SflixProvider.Companion.toExtractorLink
import com.lagradost.cloudstream3.movieproviders.SflixProvider.Companion.toSubtitleFile
import com.lagradost.cloudstream3.network.WebViewResolver
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup
@ -328,42 +330,11 @@ class ZoroProvider : MainAPI() {
val extractorLink = app.get(
link,
).mapped<RapidCloudResponse>().link
//.also { println("AAAAAAAAA: ${it.text}") }
// Loads the links in the appropriate extractor.
val hasLoadedExtractorLink = loadExtractor(extractorLink, mainUrl, callback)
if (!hasLoadedExtractorLink) {
// Not an extractor because:
// 1. No subtitle callback
// 2. Missing dub/sub status in parameter (might be substituted in the referer)
val response =
getM3u8FromRapidCloud(
extractorLink
)
if (response.contains("<html")) return@apmap
val mapped = parseJson<SflixProvider.SourceObject>(response)
mapped.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"
)
list.forEach { subList ->
subList.first?.forEach { a ->
a?.toExtractorLink(this, subList.second + " - ${it.first}", null)
?.forEach(callback)
}
extractRabbitStream(extractorLink, subtitleCallback, callback) { sourceName ->
sourceName + " - ${it.first}"
}
}
}

View file

@ -327,62 +327,11 @@ class SflixProvider(providerUrl: String, providerName: String) : MainAPI() {
app.get("https://sflix.to/ajax/get_link/$serverId").mapped<IframeJson>().link
?: return@suspendSafeApiCall
// ------- Iframe -------
val mainIframeUrl =
iframeLink.substringBeforeLast("/") // "https://rabbitstream.net/embed-4/6sBcv1i8vUF6?z=" -> "https://rabbitstream.net/embed-4"
val mainIframeId = iframeLink.substringAfterLast("/")
.substringBefore("?") // "https://rabbitstream.net/embed-4/6sBcv1i8vUF6?z=" -> "6sBcv1i8vUF6"
val iframe = app.get(iframeLink, referer = mainUrl)
val iframeKey =
iframe.document.select("script[src*=https://www.google.com/recaptcha/api.js?render=]")
.attr("src").substringAfter("render=")
val iframeToken = getCaptchaToken(iframeLink, iframeKey)
val number = Regex("""recaptchaNumber = '(.*?)'""").find(iframe.text)?.groupValues?.get(1)
val mapped = app.get(
"${mainIframeUrl.replace("/embed", "/ajax/embed")}/getSources?id=$mainIframeId&_token=$iframeToken&_number=$number",
referer = "https://rabbitstream.net/",
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"
)
).mapped<SourceObject>()
// Some smarter ws11 or w10 selection might be required in the future.
val extractorData =
"https://ws11.rabbitstream.net/socket.io/?EIO=4&transport=polling"
// val sources = resolved.first?.let { app.baseClient.newCall(it).execute().text }
// ?: return@suspendSafeApiCall
// val mapped = parseJson<SourceObject>(sources)
mapped.tracks?.forEach {
it?.toSubtitleFile()?.let { subtitleFile ->
subtitleCallback.invoke(subtitleFile)
}
}
listOf(
mapped.sources to "",
mapped.sources1 to "source 2",
mapped.sources2 to "source 3",
mapped.sourcesBackup to "source backup"
).forEach { (sources, sourceName) ->
sources?.forEach {
it?.toExtractorLink(this, sourceName, extractorData)?.forEach(callback)
}
}
extractRabbitStream(iframeLink, subtitleCallback, callback, extractorData) { it }
}
}
@ -510,7 +459,7 @@ class SflixProvider(providerUrl: String, providerName: String) : MainAPI() {
}
//.also { println("Sflix post job ${it.text}") }
Log.d(this.name, "Running Sflix job $url")
Log.d(this.name, "Running ${this.name} job $url")
val time = measureTimeMillis {
// This acts as a timeout
@ -620,6 +569,72 @@ class SflixProvider(providerUrl: String, providerName: String) : MainAPI() {
)
}
}
suspend fun MainAPI.extractRabbitStream(
url: String,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit,
/** Used for extractorLink name, input: Source name */
extractorData: String? = null,
nameTransformer: (String) -> String
) {
// 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
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 mapped = app.get(
"${
mainIframeUrl.replace(
"/embed",
"/ajax/embed"
)
}/getSources?id=$mainIframeId&_token=$iframeToken&_number=$number",
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"
)
).mapped<SourceObject>()
mapped.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"
)
list.forEach { subList ->
subList.first?.forEach { source ->
source?.toExtractorLink(this, nameTransformer(subList.second), extractorData)
?.forEach(callback)
}
}
}
}
}

View file

@ -22,6 +22,7 @@ import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import com.fasterxml.jackson.annotation.JsonProperty
import com.hippo.unifile.UniFile
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.MainActivity
@ -29,11 +30,13 @@ import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.services.VideoDownloadService
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.DataStore.getKey
import com.lagradost.cloudstream3.utils.DataStore.removeKey
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import okhttp3.internal.closeQuietly
@ -677,7 +680,7 @@ object VideoDownloadManager {
extension: String,
tryResume: Boolean,
parentId: Int?,
createNotificationCallback: (CreateNotificationMetadata) -> Unit
createNotificationCallback: (CreateNotificationMetadata) -> Unit,
): Int {
if (link.url.startsWith("magnet") || link.url.endsWith(".torrent")) {
return ERROR_UNKNOWN
@ -1135,7 +1138,6 @@ object VideoDownloadManager {
val displayName = getDisplayName(name, extension)
val fileStream = stream.fileStream!!
val firstTs = tsIterator.next()
@ -1346,6 +1348,13 @@ object VideoDownloadManager {
val name =
sanitizeFilename(ep.name ?: "${context.getString(R.string.episode)} ${ep.episode}")
// Make sure this is cancelled when download is done or cancelled.
val extractorJob = ioSafe {
if (link.extractorData != null) {
getApiFromNameNull(link.source)?.extractorVerifierJob(link.extractorData)
}
}
if (link.isM3u8 || URI(link.url).path.endsWith(".m3u8")) {
val startIndex = if (tryResume) {
context.getKey<DownloadedFileInfo>(
@ -1369,7 +1378,7 @@ object VideoDownloadManager {
meta.hlsTotal
)
}
}
}.also { extractorJob.cancel() }
}
return normalSafeApiCall {
@ -1387,7 +1396,7 @@ object VideoDownloadManager {
)
}
}
} ?: ERROR_UNKNOWN
}.also { extractorJob.cancel() } ?: ERROR_UNKNOWN
}
fun downloadCheck(