Extension fixes (#736)
This commit is contained in:
parent
c908ef7440
commit
bd076c3e5d
@ -5,7 +5,7 @@ ext {
|
||||
extName = 'MundoDonghua'
|
||||
pkgNameSuffix = 'es.mundodonghua'
|
||||
extClass = '.MundoDonghua'
|
||||
extVersionCode = 2
|
||||
extVersionCode = 3
|
||||
libVersion = '13'
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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{|}~"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ ext {
|
||||
extName = 'Pelisplushd'
|
||||
pkgNameSuffix = 'es.pelisplushd'
|
||||
extClass = '.Pelisplushd'
|
||||
extVersionCode = 13
|
||||
extVersionCode = 14
|
||||
libVersion = '13'
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user