AnimeYT fixes (#741)
This commit is contained in:
@ -5,7 +5,7 @@ ext {
|
|||||||
extName = 'Animeyt'
|
extName = 'Animeyt'
|
||||||
pkgNameSuffix = 'es.animeyt'
|
pkgNameSuffix = 'es.animeyt'
|
||||||
extClass = '.Animeyt'
|
extClass = '.Animeyt'
|
||||||
extVersionCode = 2
|
extVersionCode = 3
|
||||||
libVersion = '13'
|
libVersion = '13'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ class Animeyt : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
.replace("http://", "")
|
.replace("http://", "")
|
||||||
val url = container.attr("src")
|
val url = container.attr("src")
|
||||||
if (server == "fastream") {
|
if (server == "fastream") {
|
||||||
videoList.add(FastreamExtractor(client).videoFromUrl(url, server))
|
FastreamExtractor(client).videoFromUrl(url).forEach { videoList.add(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return videoList
|
return videoList
|
||||||
|
@ -4,15 +4,41 @@ import eu.kanade.tachiyomi.animesource.model.Video
|
|||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.util.asJsoup
|
import eu.kanade.tachiyomi.util.asJsoup
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
class FastreamExtractor(private val client: OkHttpClient) {
|
class FastreamExtractor(private val client: OkHttpClient) {
|
||||||
fun videoFromUrl(url: String, server: String): Video {
|
private fun fetchUrls(text: String?): List<String> {
|
||||||
var url1 = ""
|
if (text.isNullOrEmpty()) return listOf()
|
||||||
client.newCall(GET(url)).execute().asJsoup().select("script").forEach {
|
val linkRegex = "(http|ftp|https):\\/\\/([\\w_-]+(?:(?:\\.[\\w_-]+)+))([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-])".toRegex()
|
||||||
if (it.data().contains("jwplayer(\"vplayer\").setup({")) {
|
return linkRegex.findAll(text).map { it.value.trim().removeSurrounding("\"") }.toList()
|
||||||
url1 = it.data().substringAfter("sources: [{file:\"").substringBefore("\"}],")
|
}
|
||||||
|
|
||||||
|
fun videoFromUrl(url: String, server: String = "Fastream"): List<Video> {
|
||||||
|
val videoList = mutableListOf<Video>()
|
||||||
|
try {
|
||||||
|
val document = client.newCall(GET(url)).execute()
|
||||||
|
if (document.isSuccessful) {
|
||||||
|
val content = document!!.asJsoup()
|
||||||
|
content!!.select("script").forEach {
|
||||||
|
if (it!!.data()!!.contains("jwplayer(jwplayer(\"vplayer\").setup({")) {
|
||||||
|
val basicUrl = it!!.data().substringAfter("file: '").substringBefore("',")
|
||||||
|
videoList.add(Video(basicUrl, server, basicUrl, headers = null))
|
||||||
|
} else {
|
||||||
|
val packedRegex = Regex("eval\\(function\\(p,a,c,k,e,.*\\)\\)")
|
||||||
|
packedRegex.findAll(it!!.data()).map { packed -> packed.value }.toList().map { eval ->
|
||||||
|
val unpack = JsUnpacker.unpack(eval)
|
||||||
|
fetchUrls(unpack!!.first()).map { url ->
|
||||||
|
val fastreamRegex = "fastream.*?\\.m3u8([^&\">]?)".toRegex()
|
||||||
|
if (fastreamRegex.containsMatchIn(url)) {
|
||||||
|
videoList.add(Video(url, server, url, headers = null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
}
|
}
|
||||||
return Video(url1, server, url1)
|
return videoList
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,193 @@
|
|||||||
|
package eu.kanade.tachiyomi.animeextension.es.animeyt.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{|}~"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user