fix(pt/vizer): Fixed pt/Vizer videos empty (fix #256)

This commit is contained in:
WebDitto 2024-10-10 19:56:06 -03:00
parent 53856f9275
commit ec8981576a
7 changed files with 157 additions and 71 deletions

View file

@ -17,7 +17,12 @@ class MixDropExtractor(private val client: OkHttpClient) {
externalSubs: List<Track> = emptyList(),
referer: String = DEFAULT_REFERER,
): List<Video> {
val headers = Headers.headersOf("Referer", referer)
val headers = Headers.headersOf(
"Referer",
referer,
"User-Agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36",
)
val doc = client.newCall(GET(url, headers)).execute().asJsoup()
val unpacked = doc.selectFirst("script:containsData(eval):containsData(MDCore)")
?.data()

View file

@ -1,7 +1,7 @@
ext {
extName = 'Vizer.tv'
extClass = '.Vizer'
extVersionCode = 16
extVersionCode = 17
isNsfw = true
}
@ -9,5 +9,7 @@ apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(':lib:mixdrop-extractor'))
implementation(project(':lib:playlist-utils'))
implementation(project(':lib:streamtape-extractor'))
implementation "dev.datlag.jsunpacker:jsunpacker:1.0.1"
}

View file

@ -10,7 +10,8 @@ import eu.kanade.tachiyomi.animeextension.pt.vizer.dto.SearchItemDto
import eu.kanade.tachiyomi.animeextension.pt.vizer.dto.SearchResultDto
import eu.kanade.tachiyomi.animeextension.pt.vizer.dto.VideoDto
import eu.kanade.tachiyomi.animeextension.pt.vizer.dto.VideoListDto
import eu.kanade.tachiyomi.animeextension.pt.vizer.extractors.WarezExtractor
import eu.kanade.tachiyomi.animeextension.pt.vizer.extractors.FireplayerExtractor
import eu.kanade.tachiyomi.animeextension.pt.vizer.interceptor.WebViewResolver
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.AnimesPage
@ -41,7 +42,7 @@ class Vizer : ConfigurableAnimeSource, AnimeHttpSource() {
override val name = "Vizer.tv"
override val baseUrl = "https://vizertv.in"
override val baseUrl = "https://novizer.com"
private val apiUrl = "$baseUrl/includes/ajax"
override val lang = "pt-BR"
@ -58,6 +59,8 @@ class Vizer : ConfigurableAnimeSource, AnimeHttpSource() {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
private val webViewResolver by lazy { WebViewResolver(headers) }
// ============================== Popular ===============================
override fun popularAnimeRequest(page: Int): Request {
val pageType = preferences.getString(PREF_POPULAR_PAGE_KEY, PREF_POPULAR_PAGE_DEFAULT)!!
@ -176,7 +179,7 @@ class Vizer : ConfigurableAnimeSource, AnimeHttpSource() {
val response = episodesClient.newCall(apiRequest("getEpisodes=$id")).execute()
val episodes = response.parseAs<EpisodeListDto>().episodes
.values
.filter { it.released }
.filter { it.released === true }
.map {
SEpisode.create().apply {
name = "$sname: Ep ${it.name}".run {
@ -243,7 +246,7 @@ class Vizer : ConfigurableAnimeSource, AnimeHttpSource() {
private val mixdropExtractor by lazy { MixDropExtractor(client) }
private val streamtapeExtractor by lazy { StreamTapeExtractor(client) }
private val warezExtractor by lazy { WarezExtractor(client, headers) }
private val fireplayerExtractor by lazy { FireplayerExtractor(client, "https://basseqwevewcewcewecwcw.xyz") }
private fun getVideosFromObject(videoObj: VideoDto): List<Video> {
val hosters = videoObj.hosters ?: return emptyList()
@ -253,10 +256,13 @@ class Vizer : ConfigurableAnimeSource, AnimeHttpSource() {
return hosters.iterator().flatMap { (name, status) ->
if (status != 3) return@flatMap emptyList()
val url = getPlayerUrl(videoObj.id, name)
if (url.isNullOrBlank()) {
return emptyList()
}
when (name) {
"mixdrop" -> mixdropExtractor.videosFromUrl(url, langPrefix)
"streamtape" -> streamtapeExtractor.videosFromUrl(url, "StreamTape($langPrefix)")
"warezcdn" -> warezExtractor.videosFromUrl(url, langPrefix)
"warezcdn" -> fireplayerExtractor.videosFromUrl(url, langPrefix)
else -> emptyList()
}
}
@ -295,17 +301,8 @@ class Vizer : ConfigurableAnimeSource, AnimeHttpSource() {
// ============================= Utilities ==============================
private val noRedirectClient = client.newBuilder().followRedirects(false).build()
private fun getPlayerUrl(id: String, name: String): String {
val req = GET("$baseUrl/embed/getPlay.php?id=$id&sv=$name", headers)
return if (name == "warezcdn") {
val res = noRedirectClient.newCall(req).execute()
res.close()
res.headers["location"]!!
} else {
val res = client.newCall(req).execute()
val body = res.body.string()
body.substringAfter("location.href=\"", "").substringBefore("\";", "")
}
private fun getPlayerUrl(id: String, name: String): String? {
return webViewResolver.getUrl("$baseUrl/embed/getEmbed.php?id=$id&sv=$name", "$baseUrl/termos")
}
private fun apiRequest(body: String): Request {

View file

@ -83,7 +83,7 @@ class HostersDto(
object BooleanSerializer : JsonTransformingSerializer<Boolean>(Boolean.serializer()) {
override fun transformDeserialize(element: JsonElement): JsonElement {
require(element is JsonPrimitive)
return if (element.jsonPrimitive.isString) {
return if (element.jsonPrimitive.isString && element.jsonPrimitive.content == "true") {
JsonPrimitive(true)
} else {
JsonPrimitive(element.jsonPrimitive.booleanOrNull ?: false)

View file

@ -0,0 +1,56 @@
package eu.kanade.tachiyomi.animeextension.pt.vizer.extractors
import dev.datlag.jsunpacker.JsUnpacker
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
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
class FireplayerExtractor(private val client: OkHttpClient, private val host: String = "https://embedplayer.online") {
private val headers by lazy {
Headers.headersOf(
"X-Requested-With",
"XMLHttpRequest",
"Referer",
host,
"Origin",
host,
)
}
private val playlistUtils by lazy { PlaylistUtils(client, headers) }
fun videosFromUrl(url: String, lang: String): List<Video> {
var id = url.substringAfterLast("/")
if (id.length < 32) {
val doc = client.newCall(GET(url, headers)).execute().asJsoup()
val script = doc.selectFirst("script:containsData(eval):containsData(p,a,c,k,e,d)")?.data()
?.let(JsUnpacker::unpackAndCombine)
?: doc.selectFirst("script:containsData(FirePlayer)")?.data()
if (script?.contains("FirePlayer(") == true) {
id = script.substringAfter("FirePlayer(\"").substringBefore('"')
}
}
val postUrl = "$host/player/index.php?data=$id&do=getVideo"
val body = FormBody.Builder()
.add("hash", id)
.add("r", "")
.build()
val masterUrl = client.newCall(POST(postUrl, headers, body = body)).execute()
.body.string()
.substringAfter("securedLink\":\"")
.substringBefore('"')
.replace("\\", "")
return playlistUtils.extractFromHls(masterUrl, videoNameGen = { "WarezCDN($lang) - $it" })
}
}

View file

@ -1,52 +0,0 @@
package eu.kanade.tachiyomi.animeextension.pt.vizer.extractors
import android.util.Base64
import eu.kanade.tachiyomi.animesource.model.Video
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.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
class WarezExtractor(private val client: OkHttpClient, private val headers: Headers) {
fun videosFromUrl(url: String, lang: String): List<Video> {
val doc = client.newCall(GET(url, headers)).execute().asJsoup()
val httpUrl = doc.location().toHttpUrl()
val videoId = httpUrl.queryParameter("id") ?: return emptyList()
val script = doc.selectFirst("script:containsData(allowanceKey)")?.data()
?: return emptyList()
val key = script.substringAfter("allowanceKey").substringAfter('"').substringBefore('"')
val cdn = script.substringAfter("cdnListing").substringAfter('[').substringBefore(']')
.split(',')
.random()
val body = FormBody.Builder()
.add("getVideo", videoId)
.add("key", key)
.build()
val host = "https://" + httpUrl.host
val reqHeaders = headers.newBuilder()
.set("Origin", host)
.set("Referer", url)
.set("X-Requested-With", "XMLHttpRequest")
.build()
val req = client.newCall(POST("$host/player/functions.php", reqHeaders, body)).execute()
val id = req.body.string().substringAfter("id\":\"", "").substringBefore('"', "")
.ifBlank { return emptyList() }
val decrypted = decryptorium(id)
val videoUrl = "https://workerproxy.warezcdn.workers.dev/?url=https://cloclo$cdn.cloud.mail.ru/weblink/view/$decrypted"
return listOf(Video(videoUrl, "WarezCDN - $lang", videoUrl, headers))
}
private fun decryptorium(enc: String): String {
val b64dec = String(Base64.decode(enc, Base64.DEFAULT)).trim()
val start = b64dec.reversed().dropLast(5)
val end = b64dec.substring(0, 5)
return start + end
}
}

View file

@ -0,0 +1,78 @@
package eu.kanade.tachiyomi.animeextension.pt.vizer.interceptor
import android.annotation.SuppressLint
import android.app.Application
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView
import android.webkit.WebViewClient
import okhttp3.Headers
import uy.kohesive.injekt.injectLazy
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
class WebViewResolver(private val globalHeaders: Headers) {
private val context: Application by injectLazy()
private val handler by lazy { Handler(Looper.getMainLooper()) }
private val tag by lazy { javaClass.simpleName }
@SuppressLint("SetJavaScriptEnabled")
fun getUrl(origRequestUrl: String, baseUrl: String): String? {
val latch = CountDownLatch(1)
var webView: WebView? = null
var result: String? = null
handler.post {
val webview = WebView(context)
webView = webview
with(webview.settings) {
javaScriptEnabled = true
domStorageEnabled = true
databaseEnabled = true
useWideViewPort = false
loadWithOverviewMode = false
userAgentString = globalHeaders["User-Agent"]
}
webview.webViewClient = object : WebViewClient() {
override fun shouldInterceptRequest(
view: WebView,
request: WebResourceRequest,
): WebResourceResponse? {
val url = request.url.toString()
Log.d(tag, "Checking url $url")
if (VIDEO_REGEX.containsMatchIn(url)) {
result = url
latch.countDown()
}
return super.shouldInterceptRequest(view, request)
}
override fun onPageFinished(view: WebView?, url: String?) {
Log.d(tag, "onPageFinished $url")
super.onPageFinished(view, url)
view?.evaluateJavascript("document.body.innerHTML += '<iframe src=\"" + origRequestUrl + "\" scrolling=\"no\" frameborder=\"0\" allowfullscreen=\"\" webkitallowfullscreen=\"\" mozallowfullscreen=\"\"></iframe>'") {}
}
}
webView?.loadUrl(baseUrl)
}
latch.await(TIMEOUT_SEC, TimeUnit.SECONDS)
handler.post {
webView?.stopLoading()
webView?.destroy()
webView = null
}
return result
}
companion object {
const val TIMEOUT_SEC: Long = 25
private val VIDEO_REGEX by lazy { Regex("//(mixdrop|streamtape|warezcdn)|/video/") }
}
}