forked from recloudstream/cloudstream
optimized zoro and probably fixed sflix downloading
This commit is contained in:
parent
aab7e2dc6c
commit
c4d4173694
3 changed files with 88 additions and 93 deletions
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in a new issue