forked from recloudstream/cloudstream
massively improved zoro
This commit is contained in:
parent
4b0a3e0c4e
commit
2a55a256aa
5 changed files with 105 additions and 53 deletions
|
@ -8,6 +8,7 @@ import com.lagradost.cloudstream3.movieproviders.SflixProvider.Companion.toExtra
|
||||||
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.ExtractorLink
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
|
import com.lagradost.cloudstream3.utils.loadExtractor
|
||||||
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
|
||||||
|
@ -152,7 +153,8 @@ class ZoroProvider : MainAPI() {
|
||||||
EnumSet.of(DubStatus.Subbed)
|
EnumSet.of(DubStatus.Subbed)
|
||||||
}
|
}
|
||||||
|
|
||||||
val tvType = getType(it.selectFirst(".film-detail > .fd-infor > .fdi-item")?.text().toString())
|
val tvType =
|
||||||
|
getType(it.selectFirst(".film-detail > .fd-infor > .fdi-item")?.text().toString())
|
||||||
val href = fixUrl(it.selectFirst(".film-name a").attr("href"))
|
val href = fixUrl(it.selectFirst(".film-name a").attr("href"))
|
||||||
|
|
||||||
AnimeSearchResponse(
|
AnimeSearchResponse(
|
||||||
|
@ -187,7 +189,8 @@ class ZoroProvider : MainAPI() {
|
||||||
when {
|
when {
|
||||||
(year != null && japaneseTitle != null && status != null) -> break
|
(year != null && japaneseTitle != null && status != null) -> break
|
||||||
text.contains("Premiered") && year == null ->
|
text.contains("Premiered") && year == null ->
|
||||||
year = info.selectFirst(".name")?.text().toString().split(" ").last().toIntOrNull()
|
year =
|
||||||
|
info.selectFirst(".name")?.text().toString().split(" ").last().toIntOrNull()
|
||||||
|
|
||||||
text.contains("Japanese") && japaneseTitle == null ->
|
text.contains("Japanese") && japaneseTitle == null ->
|
||||||
japaneseTitle = info.selectFirst(".name")?.text().toString()
|
japaneseTitle = info.selectFirst(".name")?.text().toString()
|
||||||
|
@ -255,61 +258,59 @@ class ZoroProvider : MainAPI() {
|
||||||
callback: (ExtractorLink) -> Unit
|
callback: (ExtractorLink) -> Unit
|
||||||
): Boolean {
|
): Boolean {
|
||||||
|
|
||||||
// Copy pasted from Sflix :)
|
|
||||||
|
|
||||||
val servers: List<Pair<DubStatus, String>> = Jsoup.parse(
|
val servers: List<Pair<DubStatus, String>> = Jsoup.parse(
|
||||||
mapper.readValue<Response>(
|
app.get("$mainUrl/ajax/v2/episode/servers?episodeId=" + data.split("=")[1])
|
||||||
app.get("$mainUrl/ajax/v2/episode/servers?episodeId=" + data.split("=")[1]).text
|
.mapped<Response>().html
|
||||||
).html
|
|
||||||
).select(".server-item[data-type][data-id]").map {
|
).select(".server-item[data-type][data-id]").map {
|
||||||
Pair(if (it.attr("data-type") == "sub") DubStatus.Subbed else DubStatus.Dubbed, it.attr("data-id")!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
val res = app.get(
|
|
||||||
data,
|
|
||||||
interceptor = WebViewResolver(
|
|
||||||
Regex("""/getSources""")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
// println("RES TEXT ${res.text}")
|
|
||||||
|
|
||||||
val recaptchaToken = res.response.request.url.queryParameter("_token")
|
|
||||||
|
|
||||||
val responses = servers.map {
|
|
||||||
val link = "$mainUrl/ajax/v2/episode/sources?id=${it.second}&_token=$recaptchaToken"
|
|
||||||
Pair(
|
Pair(
|
||||||
it.first,
|
if (it.attr("data-type") == "sub") DubStatus.Subbed else DubStatus.Dubbed,
|
||||||
getM3u8FromRapidCloud(
|
it.attr("data-id")!!
|
||||||
mapper.readValue<RapidCloudResponse>(
|
|
||||||
app.get(
|
|
||||||
link,
|
|
||||||
res.headers.toMap()
|
|
||||||
).text
|
|
||||||
).link
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
responses.forEach {
|
// Prevent duplicates
|
||||||
if (it.second.contains("<html")) return@forEach
|
servers.distinctBy { it.second }.pmap {
|
||||||
val mapped = mapper.readValue<SflixProvider.SourceObject>(it.second)
|
val link =
|
||||||
|
"$mainUrl/ajax/v2/episode/sources?id=${it.second}"
|
||||||
|
val extractorLink = app.get(
|
||||||
|
link,
|
||||||
|
).mapped<RapidCloudResponse>().link
|
||||||
|
|
||||||
mapped.tracks?.forEach { track ->
|
// Loads the links in the appropriate extractor.
|
||||||
track?.toSubtitleFile()?.let { subtitleFile ->
|
val hasLoadedExtractorLink = loadExtractor(extractorLink, mainUrl, callback)
|
||||||
subtitleCallback.invoke(subtitleFile)
|
|
||||||
|
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@pmap
|
||||||
|
val mapped = mapper.readValue<SflixProvider.SourceObject>(response)
|
||||||
|
|
||||||
|
mapped.tracks?.forEach { track ->
|
||||||
|
track?.toSubtitleFile()?.let { subtitleFile ->
|
||||||
|
subtitleCallback.invoke(subtitleFile)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val list = listOf(
|
val list = listOf(
|
||||||
mapped.sources to "source 1",
|
mapped.sources to "source 1",
|
||||||
mapped.sources1 to "source 2",
|
mapped.sources1 to "source 2",
|
||||||
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 { a ->
|
subList.first?.forEach { a ->
|
||||||
a?.toExtractorLink(this, subList.second + " - ${it.first}")?.forEach(callback)
|
a?.toExtractorLink(this, subList.second + " - ${it.first}")
|
||||||
|
?.forEach(callback)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
package com.lagradost.cloudstream3.extractors
|
||||||
|
|
||||||
|
import com.lagradost.cloudstream3.app
|
||||||
|
import com.lagradost.cloudstream3.network.WebViewResolver
|
||||||
|
import com.lagradost.cloudstream3.utils.ExtractorApi
|
||||||
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
|
import com.lagradost.cloudstream3.utils.M3u8Helper
|
||||||
|
import com.lagradost.cloudstream3.utils.getQualityFromName
|
||||||
|
|
||||||
|
class WatchSB : ExtractorApi() {
|
||||||
|
override val name: String
|
||||||
|
get() = "WatchSB"
|
||||||
|
override val mainUrl: String
|
||||||
|
get() = "https://watchsb.com"
|
||||||
|
override val requiresReferer: Boolean
|
||||||
|
get() = false
|
||||||
|
|
||||||
|
override fun getUrl(url: String, referer: String?): List<ExtractorLink> {
|
||||||
|
val response = app.get(
|
||||||
|
url, interceptor = WebViewResolver(
|
||||||
|
Regex("""master\.m3u8""")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val extractedLinksList = M3u8Helper().m3u8Generation(
|
||||||
|
M3u8Helper.M3u8Stream(
|
||||||
|
response.url,
|
||||||
|
headers = response.headers.toMap()
|
||||||
|
), true
|
||||||
|
)
|
||||||
|
.map { stream ->
|
||||||
|
val qualityString = if ((stream.quality ?: 0) == 0) "" else "${stream.quality}p"
|
||||||
|
ExtractorLink(
|
||||||
|
name,
|
||||||
|
"$name $qualityString",
|
||||||
|
stream.streamUrl,
|
||||||
|
url,
|
||||||
|
getQualityFromName(stream.quality.toString()),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return extractedLinksList
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,8 @@ import com.lagradost.cloudstream3.app
|
||||||
import com.lagradost.cloudstream3.mapper
|
import com.lagradost.cloudstream3.mapper
|
||||||
import okhttp3.*
|
import okhttp3.*
|
||||||
import okhttp3.Headers.Companion.toHeaders
|
import okhttp3.Headers.Companion.toHeaders
|
||||||
|
import org.jsoup.Jsoup
|
||||||
|
import org.jsoup.nodes.Document
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -78,6 +80,7 @@ class AppResponse(
|
||||||
val body by lazy { response.body }
|
val body by lazy { response.body }
|
||||||
val code = response.code
|
val code = response.code
|
||||||
val headers = response.headers
|
val headers = response.headers
|
||||||
|
val document: Document by lazy { Jsoup.parse(text) }
|
||||||
|
|
||||||
/** Same as using mapper.readValue<T>() */
|
/** Same as using mapper.readValue<T>() */
|
||||||
inline fun <reified T : Any> mapped(): T {
|
inline fun <reified T : Any> mapped(): T {
|
||||||
|
|
|
@ -741,9 +741,7 @@ class PlayerFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun safeReleasePlayer() {
|
private fun safeReleasePlayer() {
|
||||||
thread {
|
simpleCache?.release()
|
||||||
simpleCache?.release()
|
|
||||||
}
|
|
||||||
if (this::exoPlayer.isInitialized) {
|
if (this::exoPlayer.isInitialized) {
|
||||||
exoPlayer.release()
|
exoPlayer.release()
|
||||||
}
|
}
|
||||||
|
@ -1928,7 +1926,6 @@ class PlayerFragment : Fragment() {
|
||||||
// torrentStream?.stopStream()
|
// torrentStream?.stopStream()
|
||||||
// torrentStream = null
|
// torrentStream = null
|
||||||
|
|
||||||
super.onDestroy()
|
|
||||||
canEnterPipMode = false
|
canEnterPipMode = false
|
||||||
|
|
||||||
savePositionInPlayer()
|
savePositionInPlayer()
|
||||||
|
@ -1938,6 +1935,7 @@ class PlayerFragment : Fragment() {
|
||||||
|
|
||||||
activity?.showSystemUI()
|
activity?.showSystemUI()
|
||||||
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER
|
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER
|
||||||
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
|
|
|
@ -58,13 +58,17 @@ fun getAndUnpack(string: String): String {
|
||||||
return JsUnpacker(packedText).unpack() ?: string
|
return JsUnpacker(packedText).unpack() ?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadExtractor(url: String, referer: String?, callback: (ExtractorLink) -> Unit) {
|
/**
|
||||||
|
* Tries to load the appropriate extractor based on link, returns true if any extractor is loaded.
|
||||||
|
* */
|
||||||
|
fun loadExtractor(url: String, referer: String?, callback: (ExtractorLink) -> Unit) : Boolean {
|
||||||
for (extractor in extractorApis) {
|
for (extractor in extractorApis) {
|
||||||
if (url.startsWith(extractor.mainUrl)) {
|
if (url.startsWith(extractor.mainUrl)) {
|
||||||
extractor.getSafeUrl(url, referer)?.forEach(callback)
|
extractor.getSafeUrl(url, referer)?.forEach(callback)
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
val extractorApis: Array<ExtractorApi> = arrayOf(
|
val extractorApis: Array<ExtractorApi> = arrayOf(
|
||||||
|
@ -78,6 +82,7 @@ val extractorApis: Array<ExtractorApi> = arrayOf(
|
||||||
Streamhub(),
|
Streamhub(),
|
||||||
SBPlay(),
|
SBPlay(),
|
||||||
FEmbed(),
|
FEmbed(),
|
||||||
|
WatchSB(),
|
||||||
|
|
||||||
// dood extractors
|
// dood extractors
|
||||||
DoodToExtractor(),
|
DoodToExtractor(),
|
||||||
|
|
Loading…
Reference in a new issue