zoro: fix vidcloud extractor

This commit is contained in:
jmir1
2022-03-21 16:19:23 +01:00
parent 2d51aa2e76
commit 86f56a4f6e
4 changed files with 89 additions and 113 deletions

View File

@ -5,7 +5,7 @@ ext {
extName = 'zoro.to (experimental)'
pkgNameSuffix = 'en.zoro'
extClass = '.Zoro'
extVersionCode = 2
extVersionCode = 3
libVersion = '12'
}

View File

@ -1,99 +0,0 @@
package eu.kanade.tachiyomi.animeextension.en.zoro
import android.annotation.SuppressLint
import android.app.Application
import android.os.Handler
import android.os.Looper
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import eu.kanade.tachiyomi.network.GET
import okhttp3.Headers.Companion.toHeaders
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.IOException
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
class GetSourcesInterceptor(private val getSources: String, private val client: OkHttpClient) : Interceptor {
private val context = Injekt.get<Application>()
private val handler by lazy { Handler(Looper.getMainLooper()) }
private val initWebView by lazy {
WebSettings.getDefaultUserAgent(context)
}
@Synchronized
override fun intercept(chain: Interceptor.Chain): Response {
initWebView
val request = chain.request()
try {
val newRequest = resolveWithWebView(request)
return chain.proceed(newRequest ?: request)
} catch (e: Exception) {
throw IOException(e)
}
}
@SuppressLint("SetJavaScriptEnabled")
private fun resolveWithWebView(request: Request): Request? {
val latch = CountDownLatch(1)
var webView: WebView? = null
val origRequestUrl = request.url.toString()
val headers = request.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" }.toMutableMap()
var newRequest: Request? = null
handler.post {
val webview = WebView(context)
webView = webview
with(webview.settings) {
javaScriptEnabled = true
domStorageEnabled = true
databaseEnabled = true
useWideViewPort = false
loadWithOverviewMode = false
userAgentString = "Mozilla/5.0 (Linux; Android) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.109 Safari/537.36 CrKey/1.54.248666"
}
webview.webViewClient = object : WebViewClient() {
override fun shouldInterceptRequest(
view: WebView,
request: WebResourceRequest
): WebResourceResponse? {
val url = request.url.toString()
if (url.contains(getSources)) {
val newHeaders = request.requestHeaders.toHeaders()
newRequest = GET(url, newHeaders)
latch.countDown()
}
return super.shouldInterceptRequest(view, request)
}
}
webView?.loadUrl(origRequestUrl, headers)
}
latch.await(TIMEOUT_SEC, TimeUnit.SECONDS)
handler.post {
webView?.stopLoading()
webView?.destroy()
webView = null
}
return newRequest
}
companion object {
const val TIMEOUT_SEC: Long = 10
}
}

View File

@ -12,7 +12,6 @@ import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.network.GET
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
@ -94,9 +93,10 @@ class Zoro : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val unescapedData = JSONUtil.unescape(data)
val serversHtml = Jsoup.parse(unescapedData)
val videoList = mutableListOf<Video>()
serversHtml.select("div.server-item").forEach {
val id = it.attr("data-id")
val subDub = it.attr("data-type")
for (server in serversHtml.select("div.server-item")) {
if (server.text() == "StreamSB" || server.text() == "Streamtape") continue
val id = server.attr("data-id")
val subDub = server.attr("data-type")
val videos = getVideosFromServer(
client.newCall(GET("$baseUrl/ajax/v2/episode/sources?id=$id", episodeReferer)).execute(),
subDub
@ -108,16 +108,12 @@ class Zoro : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
private fun getVideosFromServer(response: Response, subDub: String): List<Video>? {
val body = response.body!!.string()
val url = body.substringAfter("\"link\":\"").substringBefore("\"").toHttpUrl()
val id = url.pathSegments.last()
val getSources = url.toString().substringBefore("embed-6") + "ajax/embed-6/getSources?id=$id&_token="
val url = body.substringAfter("\"link\":\"").substringBefore("\"") + "&autoPlay=1&oa=0"
val getSourcesLink = ZoroExtractor(client).getSourcesLink(url) ?: return null
val referer = Headers.headersOf("Referer", baseUrl)
val recaptchaClient = client.newBuilder().addInterceptor(GetSourcesInterceptor(getSources, client)).build()
val lol = recaptchaClient.newCall(GET("$url&autoPlay=1", referer)).execute().body!!.string()
if (!lol.contains("{\"sources\":[{\"file\":\"")) return null
val masterUrl = lol.substringAfter("{\"sources\":[{\"file\":\"").substringBefore("\"")
val source = client.newCall(GET(getSourcesLink)).execute().body!!.string()
if (!source.contains("{\"sources\":[{\"file\":\"")) return null
val masterUrl = source.substringAfter("{\"sources\":[{\"file\":\"").substringBefore("\"")
val masterPlaylist = client.newCall(GET(masterUrl)).execute().body!!.string()
val videoList = mutableListOf<Video>()
masterPlaylist.substringAfter("#EXT-X-STREAM-INF:").split("#EXT-X-STREAM-INF:").forEach {

View File

@ -0,0 +1,79 @@
package eu.kanade.tachiyomi.animeextension.en.zoro
import android.net.Uri
import android.util.Base64
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.FormBody
import okhttp3.Headers
import okhttp3.OkHttpClient
import okhttp3.Response
import okhttp3.WebSocket
import okhttp3.WebSocketListener
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
class ZoroExtractor(private val client: OkHttpClient) {
fun getSourcesLink(url: String): String? {
val html = client.newCall(GET(url, Headers.headersOf("referer", "https://zoro.to/"))).execute().body!!.string()
val key = html.substringAfter("var recaptchaSiteKey = '", "")
.substringBefore("',", "").ifEmpty { return null }
val number = html.substringAfter("recaptchaNumber = '", "")
.substringBefore("';", "").ifEmpty { return null }
val captcha = captcha(url, key) ?: return null
val id = url.substringAfter("/embed-6/", "")
.substringBefore("?z=", "").ifEmpty { return null }
val sId = sId() ?: return null
return "https://rapid-cloud.ru/ajax/embed-6/getSources?id=$id&_token=$captcha&_number=$number&sId=$sId"
}
private fun sId(): String? {
val latch = CountDownLatch(1)
var sId: String? = null
val listener = object : WebSocketListener() {
override fun onOpen(webSocket: WebSocket, response: Response) {
webSocket.send("40")
}
override fun onMessage(webSocket: WebSocket, text: String) {
if (text.startsWith("40")) {
sId = text
webSocket.close(1000, null)
latch.countDown()
}
}
}
client.newWebSocket(GET("wss://ws1.rapid-cloud.ru/socket.io/?EIO=4&transport=websocket"), listener)
latch.await(30, TimeUnit.SECONDS)
return sId?.substringAfter("40{\"sid\":\"", "")
?.substringBefore("\"", "")
}
private fun captcha(url: String, key: String): String? {
val uri = Uri.parse(url)
val domain = (Base64.encodeToString((uri.scheme + "://" + uri.host + ":443").encodeToByteArray(), Base64.NO_PADDING) + ".")
.replace("\n", "")
val headers = Headers.headersOf("referer", uri.scheme + "://" + uri.host)
val vToken = client.newCall(GET("https://www.google.com/recaptcha/api.js?render=$key", headers)).execute().body!!.string()
.replace("\n", "").substringAfter("/releases/", "")
.substringBefore("/recaptcha", "").ifEmpty { return null }
if (vToken.isEmpty()) return null
val anchorUrl = "https://www.google.com/recaptcha/api2/anchor?ar=1&hl=en&size=invisible&cb=kr60249sk&k=$key&co=$domain&v=$vToken"
val recapToken = client.newCall(GET(anchorUrl)).execute().asJsoup()
.selectFirst("#recaptcha-token")?.attr("value") ?: return null
val body = FormBody.Builder()
.add("v", vToken)
.add("k", key)
.add("c", recapToken)
.add("co", domain)
.add("sa", "")
.add("reason", "q")
.build()
return client.newCall(POST("https://www.google.com/recaptcha/api2/reload?k=$key", body = body)).execute().body!!.string()
.replace("\n", "")
.substringAfter("rresp\",\"", "")
.substringBefore("\",null", "")
.ifEmpty { null }
}
}