Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 1 commit into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions lib/fireplayer-extractor/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
plugins {
id("lib-android")
}

dependencies {
implementation("dev.datlag.jsunpacker:jsunpacker:1.0.1") {
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk8")
}
implementation(project(":lib:playlist-utils"))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package eu.kanade.tachiyomi.lib.fireplayerextractor

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.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient

class FireplayerExtractor(
private val client: OkHttpClient,
private val defaultHost: String? = null,
) {
fun videosFromUrl(
url: String,
videoNameGen: (String) -> String = { quality -> quality },
videoHost: String? = null,
): List<Video> {
val host = videoHost ?: defaultHost ?: "https://${url.toHttpUrl().host}"

val headers = Headers.Builder()
.set("X-Requested-With", "XMLHttpRequest")
.set("Referer", host)
.set("Origin", "https://${host.toHttpUrl().host}")
.set("X-Requested-With", "XMLHttpRequest")
.build()

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("\\", "")

val playlistUtils = PlaylistUtils(client, headers)

return playlistUtils.extractFromHls(masterUrl, videoNameGen = videoNameGen)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
5 changes: 4 additions & 1 deletion src/pt/vizer/build.gradle
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
ext {
extName = 'Vizer.tv'
extClass = '.Vizer'
extVersionCode = 16
extVersionCode = 17
isNsfw = true
}

apply from: "$rootDir/common.gradle"

dependencies {
implementation(project(':lib:fireplayer-extractor'))
implementation(project(':lib:mixdrop-extractor'))
implementation(project(':lib:playlist-utils'))
implementation(project(':lib:streamtape-extractor'))
implementation "dev.datlag.jsunpacker:jsunpacker:1.0.1"
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ 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.interceptor.WebViewResolver
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.AnimesPage
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
import eu.kanade.tachiyomi.lib.fireplayerextractor.FireplayerExtractor
import eu.kanade.tachiyomi.lib.mixdropextractor.MixDropExtractor
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.network.GET
Expand All @@ -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"
Expand All @@ -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)!!
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -243,20 +246,24 @@ 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) }

private fun getVideosFromObject(videoObj: VideoDto): List<Video> {
val hosters = videoObj.hosters ?: return emptyList()

val langPrefix = if (videoObj.lang == "1") "LEG" else "DUB"

return hosters.iterator().flatMap { (name, status) ->
if (status != 3) return@flatMap emptyList()
// Always try the warezcdn
if (status != 3 && name != "warezcdn") 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, videoNameGen = { "WarezCDN($langPrefix) - $it" })
else -> emptyList()
}
}
Expand Down Expand Up @@ -295,17 +302,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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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/") }
}
}