Merge branch 'Kohi-den:main' into main
This commit is contained in:
commit
f403709871
3 changed files with 106 additions and 21 deletions
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Aniwave'
|
||||
extClass = '.Aniwave'
|
||||
extVersionCode = 74
|
||||
extVersionCode = 75
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.animeextension.en.nineanime
|
|||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import android.util.Log
|
||||
import android.webkit.URLUtil
|
||||
import android.widget.Toast
|
||||
import androidx.preference.EditTextPreference
|
||||
|
@ -97,7 +96,7 @@ class Aniwave : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
|
||||
val filters = AniwaveFilters.getSearchParameters(filters)
|
||||
|
||||
val vrf = if (query.isNotBlank()) utils.vrfEncrypt(ENCRYPTION_KEY, query) else ""
|
||||
val vrf = if (query.isNotBlank()) utils.vrfEncrypt(query) else ""
|
||||
var url = "$baseUrl/filter?keyword=$query"
|
||||
|
||||
if (filters.genre.isNotBlank()) url += filters.genre
|
||||
|
@ -150,13 +149,12 @@ class Aniwave : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
// ============================== Episodes ==============================
|
||||
|
||||
override fun episodeListRequest(anime: SAnime): Request {
|
||||
Log.i(name, "episodeListRequest")
|
||||
val response = client.newCall(GET(baseUrl + anime.url)).execute()
|
||||
var document = response.asJsoup()
|
||||
document = resolveSearchAnime(anime, document)
|
||||
val id = document.selectFirst("div[data-id]")?.attr("data-id") ?: throw Exception("ID not found")
|
||||
|
||||
val vrf = utils.vrfEncrypt(ENCRYPTION_KEY, id)
|
||||
val vrf = utils.vrfEncrypt(id)
|
||||
|
||||
val listHeaders = headers.newBuilder().apply {
|
||||
add("Accept", "application/json, text/javascript, */*; q=0.01")
|
||||
|
@ -212,7 +210,7 @@ class Aniwave : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
|
||||
override fun videoListRequest(episode: SEpisode): Request {
|
||||
val ids = episode.url.substringBefore("&")
|
||||
val vrf = utils.vrfEncrypt(ENCRYPTION_KEY, ids)
|
||||
val vrf = utils.vrfEncrypt(ids)
|
||||
val url = "/ajax/server/list/$ids?vrf=$vrf"
|
||||
val epurl = episode.url.substringAfter("epurl=")
|
||||
|
||||
|
@ -265,7 +263,7 @@ class Aniwave : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
private val mp4uploadExtractor by lazy { Mp4uploadExtractor(client) }
|
||||
|
||||
private fun extractVideo(server: VideoData, epUrl: String): List<Video> {
|
||||
val vrf = utils.vrfEncrypt(ENCRYPTION_KEY, server.serverId)
|
||||
val vrf = utils.vrfEncrypt(server.serverId)
|
||||
|
||||
val listHeaders = headers.newBuilder().apply {
|
||||
add("Accept", "application/json, text/javascript, */*; q=0.01")
|
||||
|
@ -280,7 +278,7 @@ class Aniwave : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
|
||||
return runCatching {
|
||||
val parsed = response.parseAs<ServerResponse>()
|
||||
val embedLink = utils.vrfDecrypt(DECRYPTION_KEY, parsed.result.url)
|
||||
val embedLink = utils.vrfDecrypt(parsed.result.url)
|
||||
when (server.serverName) {
|
||||
"vidstream" -> vidsrcExtractor.videosFromUrl(embedLink, "Vidstream", server.type)
|
||||
"megaf" -> vidsrcExtractor.videosFromUrl(embedLink, "MegaF", server.type)
|
||||
|
@ -327,7 +325,8 @@ class Aniwave : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
private fun resolveSearchAnime(anime: SAnime, document: Document): Document {
|
||||
if (document.location().startsWith("$baseUrl/filter?keyword=")) { // redirected to search
|
||||
val element = document.selectFirst(searchAnimeSelector())
|
||||
val foundAnimePath = element?.selectFirst("a[href]")?.attr("href") ?: throw Exception("Search element not found (resolveSearch)")
|
||||
val foundAnimePath = element?.selectFirst("a[href]")?.attr("href")
|
||||
?: throw Exception("Search element not found (resolveSearch)")
|
||||
anime.url = foundAnimePath // probably doesn't work as intended
|
||||
return client.newCall(GET(baseUrl + foundAnimePath)).execute().asJsoup()
|
||||
}
|
||||
|
@ -398,9 +397,6 @@ class Aniwave : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
private const val PREF_TYPE_TOGGLE_KEY = "type_selection"
|
||||
private val TYPES = arrayOf("Sub", "Softsub", "Dub")
|
||||
private val PREF_TYPES_TOGGLE_DEFAULT = TYPES.toSet()
|
||||
|
||||
private const val DECRYPTION_KEY = "ctpAbOz5u7S6OMkx"
|
||||
private const val ENCRYPTION_KEY = "T78s2WjTc7hSIZZR"
|
||||
}
|
||||
|
||||
// ============================== Settings ==============================
|
||||
|
@ -410,7 +406,7 @@ class Aniwave : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
try {
|
||||
getHosters()
|
||||
} catch (e: Exception) {
|
||||
Log.w(name, e.toString())
|
||||
Toast.makeText(screen.context, e.toString(), Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
ListPreference(screen.context).apply {
|
||||
|
|
|
@ -7,22 +7,111 @@ import javax.crypto.spec.SecretKeySpec
|
|||
|
||||
class AniwaveUtils {
|
||||
|
||||
fun vrfEncrypt(key: String, input: String): String {
|
||||
fun vrfEncrypt(input: String): String {
|
||||
var vrf = input
|
||||
ORDER.sortedBy {
|
||||
it.first
|
||||
}.forEach { item ->
|
||||
when (item.second) {
|
||||
"exchange" -> vrf = exchange(vrf, item.third)
|
||||
"rc4" -> vrf = rc4Encrypt(item.third.get(0), vrf)
|
||||
"reverse" -> vrf = vrf.reversed()
|
||||
"base64" -> vrf = Base64.encode(vrf.toByteArray(), Base64.URL_SAFE or Base64.NO_WRAP).toString(Charsets.UTF_8)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
return java.net.URLEncoder.encode(vrf, "utf-8")
|
||||
}
|
||||
|
||||
fun vrfDecrypt(input: String): String {
|
||||
var vrf = input
|
||||
ORDER.sortedByDescending {
|
||||
it.first
|
||||
}.forEach { item ->
|
||||
when (item.second) {
|
||||
"exchange" -> vrf = exchange(vrf, item.third.reversed())
|
||||
"rc4" -> vrf = rc4Decrypt(item.third.get(0), vrf)
|
||||
"reverse" -> vrf = vrf.reversed()
|
||||
"base64" -> vrf = Base64.decode(vrf, Base64.URL_SAFE).toString(Charsets.UTF_8)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
return URLDecoder.decode(vrf, "utf-8")
|
||||
}
|
||||
|
||||
private fun rc4Encrypt(key: String, input: String): String {
|
||||
val rc4Key = SecretKeySpec(key.toByteArray(), "RC4")
|
||||
val cipher = Cipher.getInstance("RC4")
|
||||
cipher.init(Cipher.DECRYPT_MODE, rc4Key, cipher.parameters)
|
||||
var vrf = cipher.doFinal(input.toByteArray())
|
||||
vrf = Base64.encode(vrf, Base64.URL_SAFE or Base64.NO_WRAP)
|
||||
var vrfString = vrf.toString(Charsets.UTF_8)
|
||||
return java.net.URLEncoder.encode(vrfString, "utf-8")
|
||||
|
||||
var output = cipher.doFinal(input.toByteArray())
|
||||
output = Base64.encode(output, Base64.URL_SAFE or Base64.NO_WRAP)
|
||||
return output.toString(Charsets.UTF_8)
|
||||
}
|
||||
|
||||
fun vrfDecrypt(key: String, input: String): String {
|
||||
var vrf = Base64.decode(input.toByteArray(), Base64.URL_SAFE)
|
||||
private fun rc4Decrypt(key: String, input: String): String {
|
||||
var vrf = input.toByteArray()
|
||||
vrf = Base64.decode(vrf, Base64.URL_SAFE)
|
||||
|
||||
val rc4Key = SecretKeySpec(key.toByteArray(), "RC4")
|
||||
val cipher = Cipher.getInstance("RC4")
|
||||
cipher.init(Cipher.DECRYPT_MODE, rc4Key, cipher.parameters)
|
||||
vrf = cipher.doFinal(vrf)
|
||||
return URLDecoder.decode(vrf.toString(Charsets.UTF_8), "utf-8")
|
||||
return vrf.toString(Charsets.UTF_8)
|
||||
}
|
||||
|
||||
private fun exchange(input: String, keys: List<String>): String {
|
||||
val key1 = keys.get(0)
|
||||
val key2 = keys.get(1)
|
||||
return input.map { i ->
|
||||
val index = key1.indexOf(i)
|
||||
if (index != -1) {
|
||||
key2[index]
|
||||
} else {
|
||||
i
|
||||
}
|
||||
}.joinToString("")
|
||||
}
|
||||
|
||||
private fun rot13(vrf: ByteArray): ByteArray {
|
||||
for (i in vrf.indices) {
|
||||
val byte = vrf[i]
|
||||
if (byte in 'A'.code..'Z'.code) {
|
||||
vrf[i] = ((byte - 'A'.code + 13) % 26 + 'A'.code).toByte()
|
||||
} else if (byte in 'a'.code..'z'.code) {
|
||||
vrf[i] = ((byte - 'a'.code + 13) % 26 + 'a'.code).toByte()
|
||||
}
|
||||
}
|
||||
return vrf
|
||||
}
|
||||
|
||||
private fun vrfShift(vrf: ByteArray): ByteArray {
|
||||
for (i in vrf.indices) {
|
||||
val shift = arrayOf(-2, -4, -5, 6, 2, -3, 3, 6)[i % 8]
|
||||
vrf[i] = vrf[i].plus(shift).toByte()
|
||||
}
|
||||
return vrf
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val EXCHANGE_KEY_1 = listOf("AP6GeR8H0lwUz1", "UAz8Gwl10P6ReH")
|
||||
private val KEY_1 = "ItFKjuWokn4ZpB"
|
||||
private val KEY_2 = "fOyt97QWFB3"
|
||||
private val EXCHANGE_KEY_2 = listOf("1majSlPQd2M5", "da1l2jSmP5QM")
|
||||
private val EXCHANGE_KEY_3 = listOf("CPYvHj09Au3", "0jHA9CPYu3v")
|
||||
private val KEY_3 = "736y1uTJpBLUX"
|
||||
|
||||
private val ORDER = listOf(
|
||||
Triple(1, "exchange", EXCHANGE_KEY_1),
|
||||
Triple(2, "rc4", listOf(KEY_1)),
|
||||
Triple(3, "rc4", listOf(KEY_2)),
|
||||
Triple(4, "exchange", EXCHANGE_KEY_2),
|
||||
Triple(5, "exchange", EXCHANGE_KEY_3),
|
||||
Triple(5, "reverse", emptyList()),
|
||||
Triple(6, "rc4", listOf(KEY_3)),
|
||||
Triple(7, "base64", emptyList()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue