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 package com.lagradost.cloudstream3.animeproviders
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.databind.util.NameTransformer
import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.readValue
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getCaptchaToken
import com.lagradost.cloudstream3.movieproviders.SflixProvider 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.toExtractorLink
import com.lagradost.cloudstream3.movieproviders.SflixProvider.Companion.toSubtitleFile import com.lagradost.cloudstream3.movieproviders.SflixProvider.Companion.toSubtitleFile
import com.lagradost.cloudstream3.network.WebViewResolver import com.lagradost.cloudstream3.network.WebViewResolver
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup import org.jsoup.Jsoup
@ -233,7 +235,7 @@ class ZoroProvider : MainAPI() {
val actors = document.select("div.block-actors-content > div.bac-list-wrap > div.bac-item") val actors = document.select("div.block-actors-content > div.bac-list-wrap > div.bac-item")
?.mapNotNull { head -> ?.mapNotNull { head ->
val subItems = head.select(".per-info") ?: return@mapNotNull null val subItems = head.select(".per-info") ?: return@mapNotNull null
if(subItems.isEmpty()) return@mapNotNull null if (subItems.isEmpty()) return@mapNotNull null
var role: ActorRole? = null var role: ActorRole? = null
val mainActor = subItems.first()?.let { val mainActor = subItems.first()?.let {
role = when (it.selectFirst(".pi-detail > .pi-cast")?.text()?.trim()) { role = when (it.selectFirst(".pi-detail > .pi-cast")?.text()?.trim()) {
@ -243,7 +245,7 @@ class ZoroProvider : MainAPI() {
} }
it.getActor() it.getActor()
} ?: return@mapNotNull null } ?: return@mapNotNull null
val voiceActor = if(subItems.size >= 2) subItems[1]?.getActor() else null val voiceActor = if (subItems.size >= 2) subItems[1]?.getActor() else null
ActorData(actor = mainActor, role = role, voiceActor = voiceActor) ActorData(actor = mainActor, role = role, voiceActor = voiceActor)
} }
@ -328,42 +330,11 @@ class ZoroProvider : MainAPI() {
val extractorLink = app.get( val extractorLink = app.get(
link, link,
).mapped<RapidCloudResponse>().link ).mapped<RapidCloudResponse>().link
//.also { println("AAAAAAAAA: ${it.text}") }
// Loads the links in the appropriate extractor.
val hasLoadedExtractorLink = loadExtractor(extractorLink, mainUrl, callback) val hasLoadedExtractorLink = loadExtractor(extractorLink, mainUrl, callback)
if (!hasLoadedExtractorLink) { if (!hasLoadedExtractorLink) {
extractRabbitStream(extractorLink, subtitleCallback, callback) { sourceName ->
// Not an extractor because: sourceName + " - ${it.first}"
// 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)
}
} }
} }
} }

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 app.get("https://sflix.to/ajax/get_link/$serverId").mapped<IframeJson>().link
?: return@suspendSafeApiCall ?: 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. // 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"
// val sources = resolved.first?.let { app.baseClient.newCall(it).execute().text } extractRabbitStream(iframeLink, subtitleCallback, callback, extractorData) { it }
// ?: 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)
}
}
} }
} }
@ -510,7 +459,7 @@ class SflixProvider(providerUrl: String, providerName: String) : MainAPI() {
} }
//.also { println("Sflix post job ${it.text}") } //.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 { val time = measureTimeMillis {
// This acts as a timeout // 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 androidx.work.WorkManager
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.MainActivity 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.logError
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.services.VideoDownloadService 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.Coroutines.main
import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.getKey
import com.lagradost.cloudstream3.utils.DataStore.removeKey import com.lagradost.cloudstream3.utils.DataStore.removeKey
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.internal.closeQuietly import okhttp3.internal.closeQuietly
@ -677,7 +680,7 @@ object VideoDownloadManager {
extension: String, extension: String,
tryResume: Boolean, tryResume: Boolean,
parentId: Int?, parentId: Int?,
createNotificationCallback: (CreateNotificationMetadata) -> Unit createNotificationCallback: (CreateNotificationMetadata) -> Unit,
): Int { ): Int {
if (link.url.startsWith("magnet") || link.url.endsWith(".torrent")) { if (link.url.startsWith("magnet") || link.url.endsWith(".torrent")) {
return ERROR_UNKNOWN return ERROR_UNKNOWN
@ -1135,7 +1138,6 @@ object VideoDownloadManager {
val displayName = getDisplayName(name, extension) val displayName = getDisplayName(name, extension)
val fileStream = stream.fileStream!! val fileStream = stream.fileStream!!
val firstTs = tsIterator.next() val firstTs = tsIterator.next()
@ -1346,6 +1348,13 @@ object VideoDownloadManager {
val name = val name =
sanitizeFilename(ep.name ?: "${context.getString(R.string.episode)} ${ep.episode}") 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")) { if (link.isM3u8 || URI(link.url).path.endsWith(".m3u8")) {
val startIndex = if (tryResume) { val startIndex = if (tryResume) {
context.getKey<DownloadedFileInfo>( context.getKey<DownloadedFileInfo>(
@ -1369,7 +1378,7 @@ object VideoDownloadManager {
meta.hlsTotal meta.hlsTotal
) )
} }
} }.also { extractorJob.cancel() }
} }
return normalSafeApiCall { return normalSafeApiCall {
@ -1387,7 +1396,7 @@ object VideoDownloadManager {
) )
} }
} }
} ?: ERROR_UNKNOWN }.also { extractorJob.cancel() } ?: ERROR_UNKNOWN
} }
fun downloadCheck( fun downloadCheck(