forked from recloudstream/cloudstream
		
	fix zoro
This commit is contained in:
		
							parent
							
								
									3be125f12a
								
							
						
					
					
						commit
						ed573f4f22
					
				
					 5 changed files with 293 additions and 203 deletions
				
			
		|  | @ -15,6 +15,7 @@ import com.lagradost.cloudstream3.metaproviders.CrossTmdbProvider | ||||||
| import com.lagradost.cloudstream3.movieproviders.* | import com.lagradost.cloudstream3.movieproviders.* | ||||||
| import com.lagradost.cloudstream3.ui.player.SubtitleData | import com.lagradost.cloudstream3.ui.player.SubtitleData | ||||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | import com.lagradost.cloudstream3.utils.ExtractorLink | ||||||
|  | import okhttp3.Interceptor | ||||||
| import java.util.* | import java.util.* | ||||||
| import kotlin.math.absoluteValue | import kotlin.math.absoluteValue | ||||||
| 
 | 
 | ||||||
|  | @ -394,6 +395,11 @@ abstract class MainAPI { | ||||||
|     ): Boolean { |     ): Boolean { | ||||||
|         throw NotImplementedError() |         throw NotImplementedError() | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     /** An okhttp interceptor for used in OkHttpDataSource */ | ||||||
|  |     open fun getVideoInterceptor(extractorLink: ExtractorLink) : Interceptor? { | ||||||
|  |         return null | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** Might need a different implementation for desktop*/ | /** Might need a different implementation for desktop*/ | ||||||
|  |  | ||||||
|  | @ -1,22 +1,23 @@ | ||||||
| package com.lagradost.cloudstream3.animeproviders | package com.lagradost.cloudstream3.animeproviders | ||||||
| 
 | 
 | ||||||
|  | import android.util.Log | ||||||
| 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.Companion.extractRabbitStream | import com.lagradost.cloudstream3.movieproviders.SflixProvider.Companion.extractRabbitStream | ||||||
| import com.lagradost.cloudstream3.movieproviders.SflixProvider.Companion.toExtractorLink | import com.lagradost.cloudstream3.movieproviders.SflixProvider.Companion.runSflixExtractorVerifierJob | ||||||
| import com.lagradost.cloudstream3.movieproviders.SflixProvider.Companion.toSubtitleFile | import com.lagradost.cloudstream3.network.Requests.Companion.await | ||||||
| import com.lagradost.cloudstream3.network.WebViewResolver | import com.lagradost.cloudstream3.utils.Coroutines.ioSafe | ||||||
| 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 okhttp3.Interceptor | ||||||
| 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 | ||||||
| import java.util.* | import java.util.* | ||||||
| 
 | 
 | ||||||
|  | private const val OPTIONS = "OPTIONS" | ||||||
|  | 
 | ||||||
| class ZoroProvider : MainAPI() { | class ZoroProvider : MainAPI() { | ||||||
|     override var mainUrl = "https://zoro.to" |     override var mainUrl = "https://zoro.to" | ||||||
|     override var name = "Zoro" |     override var name = "Zoro" | ||||||
|  | @ -285,26 +286,48 @@ class ZoroProvider : MainAPI() { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private suspend fun getM3u8FromRapidCloud(url: String): String { |  | ||||||
|         return /*Regex("""/(embed-\d+)/(.*?)\?z=""").find(url)?.groupValues?.let { |  | ||||||
|             val jsonLink = "https://rapid-cloud.ru/ajax/${it[1]}/getSources?id=${it[2]}" |  | ||||||
|             app.get(jsonLink).text |  | ||||||
|         } ?:*/ app.get( |  | ||||||
|             "$url&autoPlay=1&oa=0", |  | ||||||
|             headers = mapOf( |  | ||||||
|                 "Referer" to "https://zoro.to/", |  | ||||||
|                 "User-Agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0" |  | ||||||
|             ), |  | ||||||
|             interceptor = WebViewResolver( |  | ||||||
|                 Regex("""/getSources""") |  | ||||||
|             ) |  | ||||||
|         ).text |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private data class RapidCloudResponse( |     private data class RapidCloudResponse( | ||||||
|         @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 */ | ||||||
|  |     var sid: HashMap<Int, String?> = hashMapOf() | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Makes an identical Options request before .ts request | ||||||
|  |      * Adds an SID header to the .ts request. | ||||||
|  |      * */ | ||||||
|  |     override fun getVideoInterceptor(extractorLink: ExtractorLink): Interceptor { | ||||||
|  |         return Interceptor { chain -> | ||||||
|  |             val request = chain.request() | ||||||
|  |             if (request.url.toString().endsWith(".ts") | ||||||
|  |                 && request.method != OPTIONS | ||||||
|  |                 // No option requests on VidCloud | ||||||
|  |                 && !request.url.toString().contains("betterstream") | ||||||
|  |             ) { | ||||||
|  |                 val newRequest = | ||||||
|  |                     chain.request() | ||||||
|  |                         .newBuilder().apply { | ||||||
|  |                             sid[extractorLink.url.hashCode()]?.let { sid -> | ||||||
|  |                                 addHeader("SID", sid) | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         .build() | ||||||
|  |                 val options = request.newBuilder().method(OPTIONS, request.body).build() | ||||||
|  |                 ioSafe { app.baseClient.newCall(options).await() } | ||||||
|  | 
 | ||||||
|  |                 return@Interceptor chain.proceed(newRequest) | ||||||
|  |             } else { | ||||||
|  |                 return@Interceptor chain.proceed(chain.request()) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     override suspend fun loadLinks( |     override suspend fun loadLinks( | ||||||
|         data: String, |         data: String, | ||||||
|         isCasting: Boolean, |         isCasting: Boolean, | ||||||
|  | @ -322,6 +345,8 @@ class ZoroProvider : MainAPI() { | ||||||
|             ) |             ) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         val extractorData = | ||||||
|  |             "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 { | ||||||
|  | @ -330,10 +355,17 @@ class ZoroProvider : MainAPI() { | ||||||
|             val extractorLink = app.get( |             val extractorLink = app.get( | ||||||
|                 link, |                 link, | ||||||
|             ).mapped<RapidCloudResponse>().link |             ).mapped<RapidCloudResponse>().link | ||||||
|             val hasLoadedExtractorLink = loadExtractor(extractorLink, mainUrl, callback) |             val hasLoadedExtractorLink = | ||||||
|  |                 loadExtractor(extractorLink, "https://rapid-cloud.ru/", callback) | ||||||
| 
 | 
 | ||||||
|             if (!hasLoadedExtractorLink) { |             if (!hasLoadedExtractorLink) { | ||||||
|                 extractRabbitStream(extractorLink, subtitleCallback, callback) { sourceName -> |                 extractRabbitStream( | ||||||
|  |                     extractorLink, | ||||||
|  |                     subtitleCallback, | ||||||
|  |                     // Blacklist VidCloud for now | ||||||
|  |                     { videoLink -> if (!videoLink.url.contains("betterstream")) callback(videoLink) }, | ||||||
|  |                     extractorData | ||||||
|  |                 ) { sourceName -> | ||||||
|                     sourceName + " - ${it.first}" |                     sourceName + " - ${it.first}" | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  | @ -7,11 +7,13 @@ 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.setDuration | import com.lagradost.cloudstream3.LoadResponse.Companion.setDuration | ||||||
|  | import com.lagradost.cloudstream3.animeproviders.ZoroProvider | ||||||
| import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall | import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall | ||||||
| import com.lagradost.cloudstream3.network.AppResponse | import com.lagradost.cloudstream3.network.AppResponse | ||||||
| import com.lagradost.cloudstream3.utils.AppUtils.parseJson | import com.lagradost.cloudstream3.utils.AppUtils.parseJson | ||||||
| import com.lagradost.cloudstream3.utils.AppUtils.toJson | import com.lagradost.cloudstream3.utils.AppUtils.toJson | ||||||
| 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.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 | ||||||
|  | @ -357,6 +359,39 @@ 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 { | ||||||
|  |         val img = this.select("img") | ||||||
|  |         val title = img.attr("title") | ||||||
|  |         val posterUrl = img.attr("data-src") | ||||||
|  |         val href = fixUrl(this.select("a").attr("href")) | ||||||
|  |         val isMovie = href.contains("/movie/") | ||||||
|  |         return if (isMovie) { | ||||||
|  |             MovieSearchResponse( | ||||||
|  |                 title, | ||||||
|  |                 href, | ||||||
|  |                 this@SflixProvider.name, | ||||||
|  |                 TvType.Movie, | ||||||
|  |                 posterUrl, | ||||||
|  |                 null | ||||||
|  |             ) | ||||||
|  |         } else { | ||||||
|  |             TvSeriesSearchResponse( | ||||||
|  |                 title, | ||||||
|  |                 href, | ||||||
|  |                 this@SflixProvider.name, | ||||||
|  |                 TvType.Movie, | ||||||
|  |                 posterUrl, | ||||||
|  |                 null, | ||||||
|  |                 null | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     companion object { | ||||||
|         data class PollingData( |         data class PollingData( | ||||||
|             @JsonProperty("sid") val sid: String? = null, |             @JsonProperty("sid") val sid: String? = null, | ||||||
|             @JsonProperty("upgrades") val upgrades: ArrayList<String> = arrayListOf(), |             @JsonProperty("upgrades") val upgrades: ArrayList<String> = arrayListOf(), | ||||||
|  | @ -389,6 +424,7 @@ open class SflixProvider : MainAPI() { | ||||||
| 
 | 
 | ||||||
|         /** |         /** | ||||||
|          * Generates a session |          * Generates a session | ||||||
|  |          * 1 Get request. | ||||||
|          * */ |          * */ | ||||||
|         private suspend fun negotiateNewSid(baseUrl: String): PollingData? { |         private suspend fun negotiateNewSid(baseUrl: String): PollingData? { | ||||||
|             // Tries multiple times |             // Tries multiple times | ||||||
|  | @ -419,20 +455,21 @@ open class SflixProvider : MainAPI() { | ||||||
|             return data to false |             return data to false | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     override suspend fun extractorVerifierJob(extractorData: String?) { |  | ||||||
|         if (extractorData == null) return |  | ||||||
| 
 | 
 | ||||||
|  |         private suspend fun initPolling( | ||||||
|  |             extractorData: String, | ||||||
|  |             referer: String | ||||||
|  |         ): Pair<PollingData?, String?> { | ||||||
|             val headers = mapOf( |             val headers = mapOf( | ||||||
|             "Referer" to "https://rabbitstream.net/" |                 "Referer" to referer // "https://rabbitstream.net/" | ||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
|         var data = negotiateNewSid(extractorData) ?: return |             val data = negotiateNewSid(extractorData) ?: return null to null | ||||||
|         // 40 is hardcoded, dunno how it's generated, but it seems to work everywhere. |  | ||||||
|         // This request is obligatory |  | ||||||
|             app.post( |             app.post( | ||||||
|                 "$extractorData&t=${generateTimeStamp()}&sid=${data.sid}", |                 "$extractorData&t=${generateTimeStamp()}&sid=${data.sid}", | ||||||
|                 data = 40, headers = headers |                 data = 40, headers = headers | ||||||
|         )//.also { println("First post ${it.text}") } |             ) | ||||||
|  | 
 | ||||||
|             // This makes the second get request work, and re-connect work. |             // This makes the second get request work, and re-connect work. | ||||||
|             val reconnectSid = |             val reconnectSid = | ||||||
|                 parseJson<PollingData>( |                 parseJson<PollingData>( | ||||||
|  | @ -443,6 +480,7 @@ open class SflixProvider : MainAPI() { | ||||||
| //                    .also { println("First get ${it.text}") } | //                    .also { println("First get ${it.text}") } | ||||||
|                         .text.replaceBefore("{", "") |                         .text.replaceBefore("{", "") | ||||||
|                 ).sid |                 ).sid | ||||||
|  | 
 | ||||||
|             // This response is used in the post requests. Same contents in all it seems. |             // This response is used in the post requests. Same contents in all it seems. | ||||||
|             val authInt = |             val authInt = | ||||||
|                 app.get( |                 app.get( | ||||||
|  | @ -454,16 +492,40 @@ open class SflixProvider : MainAPI() { | ||||||
|                     // Dunno if it's actually generated like this, just guessing. |                     // Dunno if it's actually generated like this, just guessing. | ||||||
|                     .toIntOrNull()?.plus(1) ?: 3 |                     .toIntOrNull()?.plus(1) ?: 3 | ||||||
| 
 | 
 | ||||||
|  |             return data to reconnectSid | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         suspend fun runSflixExtractorVerifierJob( | ||||||
|  |             api: MainAPI, | ||||||
|  |             extractorData: String?, | ||||||
|  |             referer: String | ||||||
|  |         ) { | ||||||
|  |             if (extractorData == null) return | ||||||
|  |             val headers = mapOf( | ||||||
|  |                 "Referer" to referer // "https://rabbitstream.net/" | ||||||
|  |             ) | ||||||
|  | 
 | ||||||
|  |             lateinit var data: PollingData | ||||||
|  |             var reconnectSid = "" | ||||||
|  | 
 | ||||||
|  |             initPolling(extractorData, referer) | ||||||
|  |                 .also { | ||||||
|  |                     data = it.first ?: throw RuntimeException("Data Null") | ||||||
|  |                     reconnectSid = it.second ?: throw RuntimeException("ReconnectSid Null") | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|             // Prevents them from fucking us over with doing a while(true){} loop |             // Prevents them from fucking us over with doing a while(true){} loop | ||||||
|             val interval = maxOf(data.pingInterval?.toLong()?.plus(2000) ?: return, 10000L) |             val interval = maxOf(data.pingInterval?.toLong()?.plus(2000) ?: return, 10000L) | ||||||
|             var reconnect = false |             var reconnect = false | ||||||
|             var newAuth = false |             var newAuth = false | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|             while (true) { |             while (true) { | ||||||
|                 val authData = |                 val authData = | ||||||
|                     when { |                     when { | ||||||
|                         newAuth -> "40" |                         newAuth -> "40" | ||||||
|                         reconnect -> """42["_reconnect", "$reconnectSid"]""" |                         reconnect -> """42["_reconnect", "$reconnectSid"]""" | ||||||
|                     else -> authInt |                         else -> "3" | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                 val url = "${extractorData}&t=${generateTimeStamp()}&sid=${data.sid}" |                 val url = "${extractorData}&t=${generateTimeStamp()}&sid=${data.sid}" | ||||||
|  | @ -478,20 +540,17 @@ open class SflixProvider : MainAPI() { | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 //.also { println("Sflix post job ${it.text}") } |                 //.also { println("Sflix post job ${it.text}") } | ||||||
|             Log.d(this.name, "Running ${this.name} job $url") |                 Log.d(api.name, "Running ${api.name} job $url") | ||||||
| 
 | 
 | ||||||
|                 val time = measureTimeMillis { |                 val time = measureTimeMillis { | ||||||
|                     // This acts as a timeout |                     // This acts as a timeout | ||||||
|                     val getResponse = app.get( |                     val getResponse = app.get( | ||||||
|                     "${extractorData}&t=${generateTimeStamp()}&sid=${data.sid}", |                         url, | ||||||
|                     timeout = 60, |                         timeout = interval / 1000, | ||||||
|                         headers = headers |                         headers = headers | ||||||
|                     ) |                     ) | ||||||
| //                    .also { println("Sflix get job ${it.text}") } | //                    .also { println("Sflix get job ${it.text}") } | ||||||
|                 if (getResponse.text.contains("sid")) { |                     reconnect = getResponse.text.contains("sid") | ||||||
|                     reconnect = true |  | ||||||
| //                    println("Reconnecting") |  | ||||||
|                 } |  | ||||||
|                 } |                 } | ||||||
|                 // Always waits even if the get response is instant, to prevent a while true loop. |                 // Always waits even if the get response is instant, to prevent a while true loop. | ||||||
|                 if (time < interval - 4000) |                 if (time < interval - 4000) | ||||||
|  | @ -499,56 +558,6 @@ open class SflixProvider : MainAPI() { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     private fun Element.toSearchResult(): SearchResponse { |  | ||||||
|         val inner = this.selectFirst("div.film-poster") |  | ||||||
|         val img = inner.select("img") |  | ||||||
|         val title = img.attr("title") |  | ||||||
|         val posterUrl = img.attr("data-src") ?: img.attr("src") |  | ||||||
|         val href = fixUrl(inner.select("a").attr("href")) |  | ||||||
|         val isMovie = href.contains("/movie/") |  | ||||||
|         val otherInfo = this.selectFirst("div.film-detail > div.fd-infor")?.select("span")?.toList() ?: listOf() |  | ||||||
|         var rating: Int? = null |  | ||||||
|         var year: Int? = null |  | ||||||
|         var quality: SearchQuality? = null |  | ||||||
|         when (otherInfo.size) { |  | ||||||
|             1 -> { |  | ||||||
|                 year = otherInfo[0]?.text()?.trim()?.toIntOrNull() |  | ||||||
|             } |  | ||||||
|             2 -> { |  | ||||||
|                 year = otherInfo[0]?.text()?.trim()?.toIntOrNull() |  | ||||||
|             } |  | ||||||
|             3 -> { |  | ||||||
|                 rating = otherInfo[0]?.text()?.toRatingInt() |  | ||||||
|                 quality = getQualityFromString(otherInfo[1]?.text()) |  | ||||||
|                 year = otherInfo[2]?.text()?.trim()?.toIntOrNull() |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return if (isMovie) { |  | ||||||
|             MovieSearchResponse( |  | ||||||
|                 title, |  | ||||||
|                 href, |  | ||||||
|                 this@SflixProvider.name, |  | ||||||
|                 TvType.Movie, |  | ||||||
|                 posterUrl = posterUrl, |  | ||||||
|                 year = year, |  | ||||||
|                 quality = quality |  | ||||||
|             ) |  | ||||||
|         } else { |  | ||||||
|             TvSeriesSearchResponse( |  | ||||||
|                 title, |  | ||||||
|                 href, |  | ||||||
|                 this@SflixProvider.name, |  | ||||||
|                 TvType.Movie, |  | ||||||
|                 posterUrl, |  | ||||||
|                 year = year, |  | ||||||
|                 episodes = null, |  | ||||||
|                 quality = quality |  | ||||||
|             ) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     companion object { |  | ||||||
|         fun String?.isValidServer(): Boolean { |         fun String?.isValidServer(): Boolean { | ||||||
|             if (this.isNullOrEmpty()) return false |             if (this.isNullOrEmpty()) return false | ||||||
|             if (this.equals("UpCloud", ignoreCase = true) || this.equals( |             if (this.equals("UpCloud", ignoreCase = true) || this.equals( | ||||||
|  | @ -563,7 +572,7 @@ open class SflixProvider : MainAPI() { | ||||||
|         fun Sources.toExtractorLink( |         fun Sources.toExtractorLink( | ||||||
|             caller: MainAPI, |             caller: MainAPI, | ||||||
|             name: String, |             name: String, | ||||||
|             extractorData: String? = null |             extractorData: String? = null, | ||||||
|         ): List<ExtractorLink>? { |         ): List<ExtractorLink>? { | ||||||
|             return this.file?.let { file -> |             return this.file?.let { file -> | ||||||
|                 //println("FILE::: $file") |                 //println("FILE::: $file") | ||||||
|  | @ -632,13 +641,30 @@ open class SflixProvider : MainAPI() { | ||||||
|             val number = |             val number = | ||||||
|                 Regex("""recaptchaNumber = '(.*?)'""").find(iframe.text)?.groupValues?.get(1) |                 Regex("""recaptchaNumber = '(.*?)'""").find(iframe.text)?.groupValues?.get(1) | ||||||
| 
 | 
 | ||||||
|  |             var sid: String? = null | ||||||
|  | 
 | ||||||
|  |             extractorData?.let { negotiateNewSid(it) }?.also { | ||||||
|  |                 app.post( | ||||||
|  |                     "$extractorData&t=${generateTimeStamp()}&sid=${it.sid}", | ||||||
|  |                     data = "40", | ||||||
|  |                     timeout = 60 | ||||||
|  |                 ) | ||||||
|  |                 val text = app.get( | ||||||
|  |                     "$extractorData&t=${generateTimeStamp()}&sid=${it.sid}", | ||||||
|  |                     timeout = 60 | ||||||
|  |                 ).text.replaceBefore("{", "") | ||||||
|  | 
 | ||||||
|  |                 sid = parseJson<PollingData>(text).sid | ||||||
|  |                 ioSafe { app.get("$extractorData&t=${generateTimeStamp()}&sid=${it.sid}") } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             val mapped = app.get( |             val mapped = app.get( | ||||||
|                 "${ |                 "${ | ||||||
|                     mainIframeUrl.replace( |                     mainIframeUrl.replace( | ||||||
|                         "/embed", |                         "/embed", | ||||||
|                         "/ajax/embed" |                         "/ajax/embed" | ||||||
|                     ) |                     ) | ||||||
|                 }/getSources?id=$mainIframeId&_token=$iframeToken&_number=$number", |                 }/getSources?id=$mainIframeId&_token=$iframeToken&_number=$number${sid?.let { "&sid=$it" } ?: ""}", | ||||||
|                 referer = mainUrl, |                 referer = mainUrl, | ||||||
|                 headers = mapOf( |                 headers = mapOf( | ||||||
|                     "X-Requested-With" to "XMLHttpRequest", |                     "X-Requested-With" to "XMLHttpRequest", | ||||||
|  | @ -667,11 +693,18 @@ open class SflixProvider : MainAPI() { | ||||||
|                 mapped.sources2 to "source 3", |                 mapped.sources2 to "source 3", | ||||||
|                 mapped.sourcesBackup to "source backup" |                 mapped.sourcesBackup to "source backup" | ||||||
|             ) |             ) | ||||||
| 
 |  | ||||||
|             list.forEach { subList -> |             list.forEach { subList -> | ||||||
|                 subList.first?.forEach { source -> |                 subList.first?.forEach { source -> | ||||||
|                     source?.toExtractorLink(this, nameTransformer(subList.second), extractorData) |                     source?.toExtractorLink( | ||||||
|                         ?.forEach(callback) |                         this, | ||||||
|  |                         nameTransformer(subList.second), | ||||||
|  |                         extractorData, | ||||||
|  |                     ) | ||||||
|  |                         ?.forEach { | ||||||
|  |                             // Sets Zoro SID used for video loading | ||||||
|  |                             (this as? ZoroProvider)?.sid?.set(it.url.hashCode(), sid) | ||||||
|  |                             callback(it) | ||||||
|  |                         } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -64,6 +64,12 @@ class APIRepository(val api: MainAPI) { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     suspend fun extractorVerifierJob(extractorData: String?) { | ||||||
|  |         safeApiCall { | ||||||
|  |             api.extractorVerifierJob(extractorData) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     suspend fun loadLinks( |     suspend fun loadLinks( | ||||||
|         data: String, |         data: String, | ||||||
|         isCasting: Boolean, |         isCasting: Boolean, | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ import android.util.Log | ||||||
| import android.widget.FrameLayout | import android.widget.FrameLayout | ||||||
| import com.google.android.exoplayer2.* | import com.google.android.exoplayer2.* | ||||||
| import com.google.android.exoplayer2.database.StandaloneDatabaseProvider | import com.google.android.exoplayer2.database.StandaloneDatabaseProvider | ||||||
|  | import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource | ||||||
| import com.google.android.exoplayer2.source.DefaultMediaSourceFactory | import com.google.android.exoplayer2.source.DefaultMediaSourceFactory | ||||||
| import com.google.android.exoplayer2.source.MergingMediaSource | import com.google.android.exoplayer2.source.MergingMediaSource | ||||||
| import com.google.android.exoplayer2.source.SingleSampleMediaSource | import com.google.android.exoplayer2.source.SingleSampleMediaSource | ||||||
|  | @ -17,12 +18,13 @@ import com.google.android.exoplayer2.trackselection.TrackSelector | ||||||
| import com.google.android.exoplayer2.ui.SubtitleView | import com.google.android.exoplayer2.ui.SubtitleView | ||||||
| import com.google.android.exoplayer2.upstream.DataSource | import com.google.android.exoplayer2.upstream.DataSource | ||||||
| import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory | import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory | ||||||
| import com.google.android.exoplayer2.upstream.DefaultHttpDataSource |  | ||||||
| import com.google.android.exoplayer2.upstream.cache.CacheDataSource | import com.google.android.exoplayer2.upstream.cache.CacheDataSource | ||||||
| import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor | import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor | ||||||
| import com.google.android.exoplayer2.upstream.cache.SimpleCache | import com.google.android.exoplayer2.upstream.cache.SimpleCache | ||||||
| import com.google.android.exoplayer2.util.MimeTypes | import com.google.android.exoplayer2.util.MimeTypes | ||||||
|  | import com.lagradost.cloudstream3.APIHolder.getApiFromName | ||||||
| import com.lagradost.cloudstream3.USER_AGENT | import com.lagradost.cloudstream3.USER_AGENT | ||||||
|  | import com.lagradost.cloudstream3.app | ||||||
| import com.lagradost.cloudstream3.mvvm.logError | import com.lagradost.cloudstream3.mvvm.logError | ||||||
| import com.lagradost.cloudstream3.ui.subtitles.SaveCaptionStyle | import com.lagradost.cloudstream3.ui.subtitles.SaveCaptionStyle | ||||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | import com.lagradost.cloudstream3.utils.ExtractorLink | ||||||
|  | @ -307,8 +309,19 @@ class CS3IPlayer : IPlayer { | ||||||
|         var requestSubtitleUpdate: (() -> Unit)? = null |         var requestSubtitleUpdate: (() -> Unit)? = null | ||||||
| 
 | 
 | ||||||
|         private fun createOnlineSource(link: ExtractorLink): DataSource.Factory { |         private fun createOnlineSource(link: ExtractorLink): DataSource.Factory { | ||||||
|             // Because Trailers.to seems to fail with http/1.1 the normal one uses. |             val provider = getApiFromName(link.source) | ||||||
|             return DefaultHttpDataSource.Factory().apply { |             val interceptor = provider.getVideoInterceptor(link) | ||||||
|  | 
 | ||||||
|  |             val client = app.baseClient | ||||||
|  |                 .let { | ||||||
|  |                     if (interceptor != null) | ||||||
|  |                         it.newBuilder() | ||||||
|  |                             .addInterceptor(interceptor) | ||||||
|  |                             .build() | ||||||
|  |                     else it | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |             return OkHttpDataSource.Factory(client).apply { | ||||||
|                 setUserAgent(USER_AGENT) |                 setUserAgent(USER_AGENT) | ||||||
|                 val headers = mapOf( |                 val headers = mapOf( | ||||||
|                     "referer" to link.referer, |                     "referer" to link.referer, | ||||||
|  | @ -322,7 +335,7 @@ class CS3IPlayer : IPlayer { | ||||||
|                 setDefaultRequestProperties(headers) |                 setDefaultRequestProperties(headers) | ||||||
| 
 | 
 | ||||||
|                 //https://stackoverflow.com/questions/69040127/error-code-io-bad-http-status-exoplayer-android |                 //https://stackoverflow.com/questions/69040127/error-code-io-bad-http-status-exoplayer-android | ||||||
|                 setAllowCrossProtocolRedirects(true) | //                setAllowCrossProtocolRedirects(true) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue