Skip to content

Commit

Permalink
fix(pt/vizer): Update baseUrl, fix episode list, add WarezCDN extract…
Browse files Browse the repository at this point in the history
…or (#3036)
  • Loading branch information
Claudemirovsky authored Mar 10, 2024
1 parent d6da1c9 commit b85bc9a
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 59 deletions.
4 changes: 2 additions & 2 deletions src/pt/vizer/build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
ext {
extName = 'Vizer.tv'
extClass = '.Vizer'
extVersionCode = 13
extVersionCode = 14
isNsfw = true
}

Expand All @@ -10,4 +10,4 @@ apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(':lib:mixdrop-extractor'))
implementation(project(':lib:streamtape-extractor'))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.pt.vizer.VizerFilters.FilterSearchParams
import eu.kanade.tachiyomi.animeextension.pt.vizer.dto.EpisodeListDto
import eu.kanade.tachiyomi.animeextension.pt.vizer.dto.PlayersDto
import eu.kanade.tachiyomi.animeextension.pt.vizer.dto.HostersDto
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.VideoLanguagesDto
import eu.kanade.tachiyomi.animeextension.pt.vizer.dto.VideoListDto
import eu.kanade.tachiyomi.animeextension.pt.vizer.extractors.WarezExtractor
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.AnimesPage
Expand All @@ -24,7 +25,6 @@ import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.awaitSuccess
import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parseAs
import kotlinx.serialization.json.Json
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Request
Expand All @@ -33,20 +33,19 @@ import okhttp3.Response
import org.jsoup.nodes.Element
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy

class Vizer : ConfigurableAnimeSource, AnimeHttpSource() {

override val name = "Vizer.tv"

override val baseUrl = "https://vizer.tv"
override val baseUrl = "https://vizertv.in"
private val apiUrl = "$baseUrl/includes/ajax"

override val lang = "pt-BR"

override val supportsLatest = true

private val json: Json by injectLazy()
override fun headersBuilder() = super.headersBuilder().add("Referer", "$baseUrl/")

private val preferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
Expand All @@ -65,7 +64,7 @@ class Vizer : ConfigurableAnimeSource, AnimeHttpSource() {

override fun popularAnimeParse(response: Response): AnimesPage {
val result = response.parseAs<SearchResultDto>()
val animes = result.list.map(::animeFromObject)
val animes = result.items.values.map(::animeFromObject)
val hasNext = result.quantity == 35
return AnimesPage(animes, hasNext)
}
Expand All @@ -89,7 +88,7 @@ class Vizer : ConfigurableAnimeSource, AnimeHttpSource() {

override fun latestUpdatesParse(response: Response): AnimesPage {
val parsedData = response.parseAs<SearchResultDto>()
val animes = parsedData.list.map(::animeFromObject)
val animes = parsedData.items.values.map(::animeFromObject)
return AnimesPage(animes, false)
}

Expand Down Expand Up @@ -165,6 +164,7 @@ class Vizer : ConfigurableAnimeSource, AnimeHttpSource() {
val sname = seasonElement.text()
val response = client.newCall(apiRequest("getEpisodes=$id")).execute()
val episodes = response.parseAs<EpisodeListDto>().episodes
.values
.filter { it.released }
.map {
SEpisode.create().apply {
Expand All @@ -178,7 +178,7 @@ class Vizer : ConfigurableAnimeSource, AnimeHttpSource() {

override fun episodeListParse(response: Response): List<SEpisode> {
val doc = response.asJsoup()
val seasons = doc.select("div#seasonsList div.item[data-season-id]")
val seasons = doc.select("div.seasons div.list div.item[data-season-id]")
return if (seasons.size > 0) {
seasons.flatMap(::getSeasonEps).reversed()
} else {
Expand All @@ -201,40 +201,48 @@ class Vizer : ConfigurableAnimeSource, AnimeHttpSource() {
} else {
// Fake url, its an ID that will be used to get episode languages
// (sub/dub) and then return the video link
apiRequest("getEpisodeLanguages=$url")
apiRequest("getEpisodeData=$url")
}
}

override fun videoListParse(response: Response): List<Video> {
val body = response.body.string()
val videoObjectList = if (body.startsWith("{")) {
json.decodeFromString<VideoLanguagesDto>(body).videos
body.parseAs<VideoListDto>().videos.values.toList()
} else {
val videoJson = body.substringAfterLast("videoPlayerBox(").substringBefore(");")
json.decodeFromString<VideoLanguagesDto>(videoJson).videos
val doc = response.asJsoup(body)
doc.select("div.audios div[data-load-player]").mapNotNull {
try {
val movieHosters = it.attr("data-players").parseAs<HostersDto>()
val movieId = it.attr("data-load-player")
val movieLang = if (it.hasClass("legendado")) "1" else "0"
VideoDto(movieId, movieLang).apply { hosters = movieHosters }
} catch (_: Throwable) { null }
}
}

return videoObjectList.flatMap(::getVideosFromObject)
}

private val mixdropExtractor by lazy { MixDropExtractor(client) }
private val streamtapeExtractor by lazy { StreamTapeExtractor(client) }
private val warezExtractor by lazy { WarezExtractor(client, headers) }

private fun getVideosFromObject(videoObj: VideoDto): List<Video> {
val players = client.newCall(apiRequest("getVideoPlayers=${videoObj.id}"))
.execute()
.parseAs<PlayersDto>()
val hosters = videoObj.hosters ?: return emptyList()

val langPrefix = if (videoObj.lang == "1") "LEG" else "DUB"
val videoList = players.iterator().mapNotNull { (name, status) ->
if (status == "0") return@mapNotNull null

return hosters.iterator().flatMap { (name, status) ->
if (status != 3) return@flatMap emptyList()
val url = getPlayerUrl(videoObj.id, name)
when (name) {
"mixdrop" -> mixdropExtractor.videoFromUrl(url, langPrefix)
"mixdrop" -> mixdropExtractor.videosFromUrl(url, langPrefix)
"streamtape" -> streamtapeExtractor.videosFromUrl(url, "StreamTape($langPrefix)")
else -> null
"warezcdn" -> warezExtractor.videosFromUrl(url, langPrefix)
else -> emptyList()
}
}.flatten()
return videoList
}
}

// ============================== Settings ==============================
Expand Down Expand Up @@ -289,10 +297,19 @@ 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")
val body = client.newCall(req).execute().body.string()
return body.substringAfter("location.href=\"").substringBefore("\";")
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 apiRequest(body: String): Request {
Expand Down Expand Up @@ -333,6 +350,7 @@ class Vizer : ConfigurableAnimeSource, AnimeHttpSource() {
private val PREF_PLAYER_ARRAY = arrayOf(
"MixDrop",
"StreamTape",
"WarezCDN",
)

private const val PREF_LANGUAGE_KEY = "pref_language"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
package eu.kanade.tachiyomi.animeextension.pt.vizer.dto

import eu.kanade.tachiyomi.util.parseAs
import kotlinx.serialization.EncodeDefault
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonTransformingSerializer

typealias FakeList<T> = Map<String, T>

@Serializable
data class SearchResultDto(
class SearchResultDto(
val quantity: Int = 0,
@Serializable(with = GenericListSerializer::class)
@EncodeDefault
val list: List<SearchItemDto> = emptyList(),
@SerialName("list")
val items: FakeList<SearchItemDto> = emptyMap(),
)

@Serializable
data class SearchItemDto(
class SearchItemDto(
val id: String,
val title: String,
val url: String,
Expand All @@ -28,51 +25,50 @@ data class SearchItemDto(
)

@Serializable
data class EpisodeListDto(
@Serializable(with = GenericListSerializer::class)
class EpisodeListDto(
@SerialName("list")
val episodes: List<EpisodeItemDto>,
val episodes: FakeList<EpisodeItemDto>,
)

@Serializable
data class EpisodeItemDto(
class EpisodeItemDto(
val id: String,
val name: String,
val released: Boolean,
val title: String,
)

@Serializable
data class VideoLanguagesDto(
class VideoListDto(
@SerialName("list")
@Serializable(with = GenericListSerializer::class)
val videos: List<VideoDto>,
val videos: FakeList<VideoDto>,
)

@Serializable
data class VideoDto(
class VideoDto(
val id: String,
val lang: String,
)
@SerialName("players")
private val players: String? = null,
) {
var hosters = try {
players?.parseAs<HostersDto>()
} catch (e: Throwable) {
null
}
}

@Serializable
data class PlayersDto(
val mixdrop: String = "0",
val streamtape: String = "0",
class HostersDto(
val mixdrop: Int = 0,
val streamtape: Int = 0,
val warezcdn: Int = 0,
) {
operator fun iterator(): List<Pair<String, String>> {
operator fun iterator(): List<Pair<String, Int>> {
return listOf(
"mixdrop" to mixdrop,
"streamtape" to streamtape,
"warezcdn" to warezcdn,
)
}
}

class GenericListSerializer<T>(
private val itemSerializer: KSerializer<T>,
) : JsonTransformingSerializer<List<T>>(ListSerializer(itemSerializer)) {
override fun transformDeserialize(element: JsonElement): JsonElement {
val jsonObj = element as JsonObject
return JsonArray(jsonObj.values.toList())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
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
}
}

0 comments on commit b85bc9a

Please sign in to comment.