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…
	
	Add table
		Add a link
		
	
		Reference in a new issue