Extension fixes (#736)

This commit is contained in:
miguelantonioe 2022-08-06 11:10:58 -05:00 committed by GitHub
parent c908ef7440
commit bd076c3e5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 328 additions and 32 deletions

View File

@ -5,7 +5,7 @@ ext {
extName = 'MundoDonghua'
pkgNameSuffix = 'es.mundodonghua'
extClass = '.MundoDonghua'
extVersionCode = 2
extVersionCode = 3
libVersion = '13'
}

View File

@ -2,10 +2,11 @@ package eu.kanade.tachiyomi.animeextension.es.mundodonghua
import android.app.Application
import android.content.SharedPreferences
import android.util.Log
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.es.mundodonghua.extractors.FembedExtractor
import eu.kanade.tachiyomi.animeextension.es.mundodonghua.extractors.JsUnpacker
import eu.kanade.tachiyomi.animeextension.es.mundodonghua.extractors.ProteaExtractor
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
@ -78,31 +79,70 @@ class MundoDonghua : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
return episode
}
private fun getAndUnpack(string: String): Sequence<String> {
return JsUnpacker.unpack(string)
}
private fun fetchUrls(text: String?): List<String> {
if (text.isNullOrEmpty()) return listOf()
val linkRegex = Regex("""(https?://(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&/=]*))""")
return linkRegex.findAll(text).map { it.value.trim().removeSurrounding("\"") }.toList()
}
private fun fixUrl(url: String): String {
if (url.startsWith("http")) return url
if (url.isEmpty()) return ""
val startsWithNoHttp = url.startsWith("//")
if (startsWithNoHttp) return "https:$url"
else {
if (url.startsWith('/')) return baseUrl + url
return "$baseUrl/$url"
}
}
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
val videoList = mutableListOf<Video>()
document.select("script").forEach { script ->
if (script.data().contains("|fembed_done|")) {
var key = script.data().substringAfter("|view_counter|html5|").substringBefore("|width|height|").split("|").joinToString("-")
.substringAfter("|fembed_tab|com|").substringBefore("|width|height|").split("|").joinToString("-")
.substringAfter("|src|https|r4|com|").substringAfter("|").split("|").joinToString("-")
.substringAfter("|append|src|https|").substringBefore("|").split("|").joinToString("-")
.substringAfter("|append|src|https|").substringBefore("|").split("|").joinToString("-")
.substringAfter("https-fembed_tab-com-")
Log.i("bruh key", key)
var serverName = ""
if (script.data().contains("diasfem")) {
serverName = "suzihaza"
if (script.data().contains("eval(function(p,a,c,k,e")) {
val packedRegex = Regex("eval\\(function\\(p,a,c,k,e,.*\\)\\)")
packedRegex.findAll(script.data()).map {
it.value
}.toList().map {
val unpack = getAndUnpack(it)
fetchUrls(unpack!!.first()).map { url ->
if (url.contains("diasfem")) {
var serverUrl = url.replace("diasfem", "embedsito")
var videos = FembedExtractor().videosFromUrl(serverUrl)
videoList.addAll(videos)
}
}
if (unpack!!.first()!!.contains("protea_tab")) {
val protearegex = Regex("(protea_tab.*slug.*,type)")
val slug = protearegex.findAll(unpack!!.first()).map {
it.value.replace(Regex("(protea_tab.*slug\":\")"), "").replace("\"},type", "")
}.first()
val requestlink = "$baseUrl/api_donghua.php?slug=$slug"
val headers = headers.newBuilder()
.set("authority", "www.mundodonghua.com")
.set("accept", "*/*")
.set("accept-language", "es-MX,es-419;q=0.9,es;q=0.8,en;q=0.7")
.set("dnt", "1")
.set("Connection", "keep-alive")
.set("Sec-Fetch-Dest", "empty")
.set("Sec-Fetch-Mode", "no-cors")
.set("Sec-Fetch-Site", "same-origin")
.set("TE", "trailers")
.set("Pragma", "no-cache")
.set("Cache-Control", "no-cache")
.set("referer", response!!.request!!.url!!.toString())
.set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36")
.build()
ProteaExtractor().videosFromUrl(requestlink, "Protea", headers = headers, baseUrl).forEach {
videoList.add(it)
}
}
}
var url = "https://$serverName.com/v/$key"
Log.i("bruh url", url)
var videos = FembedExtractor().videosFromUrl(url)
if (videos!!.first()!!.url!!.contains("not used")) {
videos = FembedExtractor().videosFromUrl("$url-")
Log.i("bruh alternative url", "$url-")
}
videoList.addAll(videos)
}
}
return videoList
@ -115,7 +155,9 @@ class MundoDonghua : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun videoFromElement(element: Element) = throw Exception("not used")
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString("preferred_quality", "Fembed:720p")
var quality = preferences.getString("preferred_quality", "Protea:720p")
if (quality == null) quality = preferences.getString("preferred_quality", "Fembed:720p")
if (quality != null) {
val newList = mutableListOf<Video>()
var preferred = 0
@ -232,8 +274,8 @@ class MundoDonghua : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val videoQualityPref = ListPreference(screen.context).apply {
key = "preferred_quality"
title = "Preferred quality"
entries = arrayOf("Fembed:480p", "Fembed:720p", "Stape", "hd", "sd", "low", "lowest", "mobile")
entryValues = arrayOf("Fembed:480p", "Fembed:720p", "Stape", "hd", "sd", "low", "lowest", "mobile")
entries = arrayOf("Protea:1080p", "Protea:720p", "Protea:480p", "Protea:380p", "Protea:360p", "Fembed:1080p", "Fembed:720p", "Fembed:480p", "Fembed:380p", "Fembed:360p")
entryValues = arrayOf("Protea:1080p", "Protea:720p", "Protea:480p", "Protea:380p", "Protea:360p", "Fembed:1080p", "Fembed:720p", "Fembed:480p", "Fembed:380p", "Fembed:360p")
setDefaultValue("Fembed:720p")
summary = "%s"

View File

@ -0,0 +1,193 @@
package eu.kanade.tachiyomi.animeextension.es.mundodonghua.extractors
import kotlin.math.pow
object JsUnpacker {
/**
* Regex to detect packed functions.
*/
private val packedRegex = Regex("eval[(]function[(]p,a,c,k,e,[r|d]?", setOf(RegexOption.IGNORE_CASE, RegexOption.MULTILINE))
/**
* Regex to get and group the packed javascript.
* Needed to get information and unpack the code.
*/
private val packedExtractRegex = Regex("[}][(]'(.*)', *(\\d+), *(\\d+), *'(.*?)'[.]split[(]'[|]'[)]", setOf(RegexOption.IGNORE_CASE, RegexOption.MULTILINE))
/**
* Matches function names and variables to de-obfuscate the code.
*/
private val unpackReplaceRegex = Regex("\\b\\w+\\b", setOf(RegexOption.IGNORE_CASE, RegexOption.MULTILINE))
/**
* Check if script is packed.
*
* @param scriptBlock the String to check if it is packed.
*
* @return whether the [scriptBlock] contains packed code or not.
*/
fun detect(scriptBlock: String): Boolean {
return scriptBlock.contains(packedRegex)
}
/**
* Check if scripts are packed.
*
* @param scriptBlock (multiple) String(s) to check if it is packed.
*
* @return the packed scripts passed in [scriptBlock].
*/
fun detect(vararg scriptBlock: String): List<String> {
return scriptBlock.mapNotNull {
if (it.contains(packedRegex)) {
it
} else {
null
}
}
}
/**
* Check if scripts are packed.
*
* @param scriptBlocks multiple Strings to check if it is packed.
*
* @return the packed scripts passed in [scriptBlocks].
*/
fun detect(scriptBlocks: Collection<String>): List<String> {
return detect(*scriptBlocks.toTypedArray())
}
/**
* Unpack the passed [scriptBlock].
* It matches all found occurrences and returns them as separate Strings in a list.
*
* @param scriptBlock the String to unpack.
*
* @return unpacked code in a list or an empty list if non is packed.
*/
fun unpack(scriptBlock: String): Sequence<String> {
return if (!detect(scriptBlock)) {
emptySequence()
} else {
unpacking(scriptBlock)
}
}
/**
* Unpack the passed [scriptBlock].
* It matches all found occurrences and combines them into a single String.
*
* @param scriptBlock the String to unpack.
*
* @return unpacked code in a list combined by a whitespace to a single String.
*/
fun unpackAndCombine(scriptBlock: String): String? {
val unpacked = unpack(scriptBlock)
return if (unpacked.toList().isEmpty()) {
null
} else {
unpacked.joinToString(" ")
}
}
/**
* Unpack the passed [scriptBlock].
* It matches all found occurrences and returns them as separate Strings in a list.
*
* @param scriptBlock (multiple) String(s) to unpack.
*
* @return unpacked code in a flat list or an empty list if non is packed.
*/
fun unpack(vararg scriptBlock: String): List<String> {
val packedScripts = detect(*scriptBlock)
return packedScripts.flatMap {
unpacking(it)
}
}
/**
* Unpack the passed [scriptBlocks].
* It matches all found occurrences and returns them as separate Strings in a list.
*
* @param scriptBlocks multiple Strings to unpack.
*
* @return unpacked code in a flat list or an empty list if non is packed.
*/
fun unpack(scriptBlocks: Collection<String>): List<String> {
return unpack(*scriptBlocks.toTypedArray())
}
/**
* Unpacking functionality.
* Match all found occurrences, get the information group and unbase it.
* If found symtabs are more or less than the count provided in code, the occurrence will be ignored
* because it cannot be unpacked correctly.
*
* @param scriptBlock the String to unpack.
*
* @return a list of all unpacked code from all found packed and unpackable occurrences found.
*/
private fun unpacking(scriptBlock: String): Sequence<String> {
val unpacked = packedExtractRegex.findAll(scriptBlock).mapNotNull { result ->
val payload = result.groups[1]?.value
val symtab = result.groups[4]?.value?.split('|')
val radix = result.groups[2]?.value?.toIntOrNull() ?: 10
val count = result.groups[3]?.value?.toIntOrNull()
val unbaser = Unbaser(radix)
if (symtab == null || count == null || symtab.size != count) {
null
} else {
payload?.replace(unpackReplaceRegex) { match ->
val word = match.value
val unbased = symtab[unbaser.unbase(word)]
unbased.ifEmpty {
word
}
}
}
}
return unpacked
}
internal data class Unbaser(
private val base: Int
) {
private val selector: Int = when {
base > 62 -> 95
base > 54 -> 62
base > 52 -> 54
else -> 52
}
fun unbase(value: String): Int {
return if (base in 2..36) {
value.toIntOrNull(base) ?: 0
} else {
val dict = ALPHABET[selector]?.toCharArray()?.mapIndexed { index, c ->
c to index
}?.toMap()
var returnVal = 0
val valArray = value.toCharArray().reversed()
for (i in valArray.indices) {
val cipher = valArray[i]
returnVal += (base.toFloat().pow(i) * (dict?.get(cipher) ?: 0)).toInt()
}
returnVal
}
}
companion object {
private val ALPHABET = mapOf<Int, String>(
52 to "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP",
54 to "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR",
62 to "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
95 to " !\"#\$%&\\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
)
}
}
}

View File

@ -0,0 +1,63 @@
package eu.kanade.tachiyomi.animeextension.es.mundodonghua.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.Headers
import org.jsoup.Connection
import org.jsoup.Jsoup
import uy.kohesive.injekt.injectLazy
import java.io.IOException
class ProteaExtractor() {
private val json: Json by injectLazy()
fun videosFromUrl(url: String, qualityPrefix: String = "Protea", headers: Headers, baseUrl: String): List<Video> {
val videoList = mutableListOf<Video>()
try {
val document = Jsoup.connect(url).headers(headers.toMap()).ignoreContentType(true).method(Connection.Method.POST).execute()
if (document!!.body()!!.isNotEmpty()) {
val responseString = document.body().removePrefix("[").removeSuffix("]")
val jObject = json.decodeFromString<JsonObject>(responseString)
val sources = jObject["source"]!!.jsonArray
sources!!.forEach { source ->
var item = source!!.jsonObject
var quality = "$qualityPrefix:${ item["label"]!!.jsonPrimitive.content }"
var urlVideo = item["file"]!!.jsonPrimitive!!.content.removePrefix("//")
var newHeaders = Headers.Builder()
.set("authority", "www.nemonicplayer.xyz")
.set("accept", "*/*")
.set("accept-language", "es-MX,es-419;q=0.9,es;q=0.8,en;q=0.7")
.set("dnt", "1")
.set("referer", "https://www.mundodonghua.com/")
.set("sec-ch-ua", "\"Chromium\";v=\"104\", \" Not A;Brand\";v=\"99\", \"Google Chrome\";v=\"104\"")
.set("sec-ch-ua-mobile", "?0")
.set("sec-ch-ua-platform", "\"Windows\"")
.set("sec-fetch-mode", "no-cors")
.set("sec-fetch-dest", "video")
.set("sec-fetch-site", "cross-site")
.set("sec-gpc", "1")
.set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36")
.build()
videoList.add(Video("https://$urlVideo", quality, "https://$urlVideo", headers = newHeaders))
}
}
} catch (e: IOException) {
}
return videoList
}
private fun fixUrl(url: String, baseUrl: String,): String {
if (url.startsWith("http")) return url
if (url.isEmpty()) return ""
val startsWithNoHttp = url.startsWith("//")
if (startsWithNoHttp) return "https:$url"
else {
if (url.startsWith('/')) return baseUrl + url
return "$baseUrl/$url"
}
}
}

View File

@ -5,7 +5,7 @@ ext {
extName = 'Pelisplushd'
pkgNameSuffix = 'es.pelisplushd'
extClass = '.Pelisplushd'
extVersionCode = 13
extVersionCode = 14
libVersion = '13'
}

View File

@ -101,14 +101,12 @@ class Pelisplushd : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val videoList = mutableListOf<Video>()
val data = document.selectFirst("script:containsData(video[1] = )").data()
val apiUrl = data.substringAfter("video[1] = '", "")
.substringBefore("';", "")
.ifEmpty { throw Exception("no video links found.") }
val apiUrl = data.substringAfter("video[1] = '", "").substringBefore("';", "")
val alternativeServers = document.select("ul.TbVideoNv.nav.nav-tabs li:not(:first-child)")
if (apiUrl.contains("/embed.php?") || apiUrl.contains("/video/")) {
if (apiUrl.isNotEmpty()) {
val apiResponse = client.newCall(GET(apiUrl)).execute().asJsoup()
var encryptedList = apiResponse.select("li[data-r]")
var decryptedList = apiResponse.select(".REactiv li:not([data-r])")
var encryptedList = apiResponse!!.select("#PlayerDisplay div[class*=\"OptionsLangDisp\"] div[class*=\"ODDIV\"] div[class*=\"OD\"] li[data-r]")
var decryptedList = apiResponse!!.select("#PlayerDisplay div[class*=\"OptionsLangDisp\"] div[class*=\"ODDIV\"] div[class*=\"OD\"] li:not([data-r])")
encryptedList.forEach {
val url = String(Base64.decode(it.attr("data-r"), Base64.DEFAULT))
val server = it.select("span").text()