fix 9anime
This commit is contained in:
@ -5,7 +5,7 @@ ext {
|
||||
extName = '9anime'
|
||||
pkgNameSuffix = 'en.nineanime'
|
||||
extClass = '.NineAnime'
|
||||
extVersionCode = 7
|
||||
extVersionCode = 8
|
||||
libVersion = '12'
|
||||
}
|
||||
|
||||
|
@ -2,16 +2,17 @@ package eu.kanade.tachiyomi.animeextension.en.nineanime
|
||||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import android.util.Log
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimesPage
|
||||
import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||
import eu.kanade.tachiyomi.animesource.model.SEpisode
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
@ -54,76 +55,60 @@ class NineAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
return Headers.Builder().add("Referer", baseUrl)
|
||||
}
|
||||
|
||||
override fun popularAnimeSelector(): String = "li"
|
||||
override fun popularAnimeSelector(): String = "div.ani.items > div"
|
||||
|
||||
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/ajax/home/widget?name=trending&page=$page")
|
||||
|
||||
override fun popularAnimeParse(response: Response): AnimesPage {
|
||||
val responseObject = json.decodeFromString<JsonObject>(response.body!!.string())
|
||||
val document = Jsoup.parse(JSONUtil.unescape(responseObject["html"]!!.jsonPrimitive.content))
|
||||
|
||||
val animes = document.select(popularAnimeSelector()).map { element ->
|
||||
popularAnimeFromElement(element)
|
||||
}
|
||||
|
||||
val hasNextPage = popularAnimeNextPageSelector().let { selector ->
|
||||
document.select(selector).first()
|
||||
} != null
|
||||
|
||||
return AnimesPage(animes, hasNextPage)
|
||||
}
|
||||
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/filter?sort=trending&page=$page")
|
||||
|
||||
override fun popularAnimeFromElement(element: Element) = SAnime.create().apply {
|
||||
setUrlWithoutDomain(element.select("a.name").attr("href").substringBefore("?"))
|
||||
thumbnail_url = element.select("a.poster img").attr("src")
|
||||
thumbnail_url = element.select("div.poster img").attr("src")
|
||||
title = element.select("a.name").text()
|
||||
}
|
||||
|
||||
override fun popularAnimeNextPageSelector(): String = "li"
|
||||
|
||||
override fun episodeListRequest(anime: SAnime): Request {
|
||||
val animeId = anime.url.substringAfterLast(".")
|
||||
val vrf = encode(getVrf(animeId))
|
||||
return GET("$baseUrl/ajax/anime/servers?id=$animeId&vrf=$vrf")
|
||||
val id = client.newCall(GET(baseUrl + anime.url)).execute().asJsoup().selectFirst("div[data-id]").attr("data-id")
|
||||
val vrf = encodeVrf(id)
|
||||
return GET("$baseUrl/ajax/episode/list/$id?vrf=$vrf")
|
||||
}
|
||||
|
||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||
val responseObject = json.decodeFromString<JsonObject>(response.body!!.string())
|
||||
val document = Jsoup.parse(JSONUtil.unescape(responseObject["html"]!!.jsonPrimitive.content))
|
||||
val animeId = response.request.url.queryParameter("id")!!
|
||||
val vrf = encode(response.request.url.queryParameter("vrf")!!)
|
||||
return document.select(episodeListSelector()).map { episodeFromElement(it, animeId, vrf) }.reversed()
|
||||
val document = Jsoup.parse(JSONUtil.unescape(responseObject["result"]!!.jsonPrimitive.content))
|
||||
return document.select(episodeListSelector()).map(::episodeFromElement).reversed()
|
||||
}
|
||||
|
||||
override fun episodeListSelector() = "ul.episodes li a"
|
||||
override fun episodeListSelector() = "div.episodes ul > li > a"
|
||||
|
||||
private fun episodeFromElement(element: Element, animeId: String, vrf: String): SEpisode {
|
||||
override fun episodeFromElement(element: Element): SEpisode {
|
||||
val episode = SEpisode.create()
|
||||
val epNum = element.attr("data-base")
|
||||
episode.url = "/ajax/anime/servers?id=$animeId&vrf=$vrf&episode=$epNum"
|
||||
val epNum = element.attr("data-num")
|
||||
val ids = element.attr("data-ids")
|
||||
val vrf = encodeVrf(ids)
|
||||
episode.url = "/ajax/server/list/$ids?vrf=$vrf"
|
||||
episode.episode_number = epNum.toFloat()
|
||||
episode.name = "Episode $epNum"
|
||||
val name = element.parent()?.select("span.d-title")?.text().orEmpty()
|
||||
val namePrefix = "Episode $epNum"
|
||||
episode.name = if (name.isNotEmpty() && name != namePrefix) {
|
||||
"Episode $epNum: $name"
|
||||
} else {
|
||||
"Episode $epNum"
|
||||
}
|
||||
return episode
|
||||
}
|
||||
|
||||
override fun episodeFromElement(element: Element) = throw Exception("not used")
|
||||
|
||||
override fun videoListParse(response: Response): List<Video> {
|
||||
val responseObject = json.decodeFromString<JsonObject>(response.body!!.string())
|
||||
val document = Jsoup.parse(JSONUtil.unescape(responseObject["html"]!!.jsonPrimitive.content))
|
||||
val epNum = response.request.url.queryParameter("episode")
|
||||
val sources = document.select("ul.episodes li a[data-base=$epNum]").attr("data-sources")
|
||||
val sourceId = json.decodeFromString<JsonObject>(sources)["41"]!!.jsonPrimitive.content
|
||||
fun getEpisodeBody(): String? {
|
||||
val res = network.client
|
||||
.newCall(GET("$baseUrl/ajax/anime/episode?id=$sourceId"))
|
||||
.execute()
|
||||
return if (res.code == 200) res.body!!.string() else null
|
||||
}
|
||||
// sometimes I have to retry the request for some reason (???)
|
||||
val episodeBody = getEpisodeBody() ?: getEpisodeBody()!!
|
||||
val encryptedSourceUrl = json.decodeFromString<JsonObject>(episodeBody)["url"]!!.jsonPrimitive.content
|
||||
val embedLink = getLink(encryptedSourceUrl)
|
||||
val document = Jsoup.parse(JSONUtil.unescape(responseObject["result"]!!.jsonPrimitive.content))
|
||||
val sourceId = document.select("ul > li[data-sv-id=41]").attr("data-link-id")
|
||||
val vrf = encodeVrf(sourceId)
|
||||
val episodeBody = network.client.newCall(GET("$baseUrl/ajax/server/$sourceId?vrf=$vrf"))
|
||||
.execute().body!!.string()
|
||||
Log.i("bruh", episodeBody)
|
||||
val encryptedSourceUrl = json.decodeFromString<JsonObject>(episodeBody)["result"]!!
|
||||
.jsonObject["url"]!!.jsonPrimitive.content
|
||||
val embedLink = decodeVrf(encryptedSourceUrl)
|
||||
val vizcloudClient = client.newBuilder().addInterceptor(VizcloudInterceptor()).build()
|
||||
val referer = Headers.headersOf("Referer", "$baseUrl/")
|
||||
val sourceObject = json.decodeFromString<JsonObject>(
|
||||
@ -171,18 +156,18 @@ class NineAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
override fun searchAnimeFromElement(element: Element): SAnime {
|
||||
val anime = SAnime.create()
|
||||
anime.setUrlWithoutDomain(baseUrl + element.select("a.name").attr("href"))
|
||||
anime.thumbnail_url = element.select("a.poster img").attr("src")
|
||||
anime.thumbnail_url = element.select("div.poster img").attr("src")
|
||||
anime.title = element.select("a.name").text()
|
||||
return anime
|
||||
}
|
||||
|
||||
override fun searchAnimeNextPageSelector(): String = "a.btn-primary.next:not(.disabled)"
|
||||
|
||||
override fun searchAnimeSelector(): String = "ul.anime-list li"
|
||||
override fun searchAnimeSelector(): String = "div.ani.items div"
|
||||
|
||||
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
|
||||
val vrf = encode(getVrf(query))
|
||||
return GET("$baseUrl/search?keyword=${encode(query)}&vrf=$vrf&page=$page")
|
||||
val vrf = encodeVrf(query)
|
||||
return GET("$baseUrl/filter?keyword=${encode(query)}&vrf=$vrf&page=$page")
|
||||
}
|
||||
|
||||
override fun animeDetailsParse(document: Document): SAnime {
|
||||
@ -258,19 +243,12 @@ class NineAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
screen.addPreference(videoQualityPref)
|
||||
}
|
||||
|
||||
private fun getVrf(id: String): String {
|
||||
val reversed = ue(encode(id) + "0000000").slice(0..5).reversed()
|
||||
return reversed + ue(je(reversed, encode(id))).replace("""=+$""".toRegex(), "")
|
||||
}
|
||||
private fun encodeVrf(id: String) = encode(encrypt(cipher(encode(id))).replace("""=+$""".toRegex(), ""))
|
||||
|
||||
private fun getLink(url: String): String {
|
||||
val i = url.slice(0..5)
|
||||
val n = url.slice(6..url.lastIndex)
|
||||
return decode(je(i, ze(n)))
|
||||
}
|
||||
private fun decodeVrf(text: String) = decode(cipher(decrypt(text)))
|
||||
|
||||
private fun ue(input: String): String {
|
||||
if (input.any { it.code >= 256 }) throw Exception("illegal characters!")
|
||||
private fun encrypt(input: String): String {
|
||||
if (input.any { it.code > 255 }) throw Exception("illegal characters!")
|
||||
var output = ""
|
||||
for (i in input.indices step 3) {
|
||||
val a = intArrayOf(-1, -1, -1, -1)
|
||||
@ -287,38 +265,38 @@ class NineAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
for (n in a) {
|
||||
if (n == -1) output += "="
|
||||
else {
|
||||
if (n in 0..63) output += key[n]
|
||||
if (n in 0..63) output += nineAnimeKey[n]
|
||||
}
|
||||
}
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
private fun je(inputOne: String, inputTwo: String): String {
|
||||
private fun cipher(input: String): String {
|
||||
val arr = IntArray(256) { it }
|
||||
var output = ""
|
||||
|
||||
var u = 0
|
||||
var r: Int
|
||||
for (a in arr.indices) {
|
||||
u = (u + arr[a] + inputOne[a % inputOne.length].code) % 256
|
||||
r = arr[a]
|
||||
arr[a] = arr[u]
|
||||
arr.indices.forEach {
|
||||
u = (u + arr[it] + cipherKey[it % cipherKey.length].code) % 256
|
||||
r = arr[it]
|
||||
arr[it] = arr[u]
|
||||
arr[u] = r
|
||||
}
|
||||
u = 0
|
||||
var c = 0
|
||||
for (f in inputTwo.indices) {
|
||||
c = (c + f) % 256
|
||||
|
||||
return input.indices.map { j ->
|
||||
c = (c + 1) % 256
|
||||
u = (u + arr[c]) % 256
|
||||
r = arr[c]
|
||||
arr[c] = arr[u]
|
||||
arr[u] = r
|
||||
output += (inputTwo[f].code xor arr[(arr[c] + arr[u]) % 256]).toChar()
|
||||
}
|
||||
return output
|
||||
(input[j].code xor arr[(arr[c] + arr[u]) % 256]).toChar()
|
||||
}.joinToString("")
|
||||
}
|
||||
|
||||
private fun ze(input: String): String {
|
||||
private fun decrypt(input: String): String {
|
||||
val t = if (input.replace("""[\t\n\f\r]""".toRegex(), "").length % 4 == 0) {
|
||||
input.replace("""==?$""".toRegex(), "")
|
||||
} else input
|
||||
@ -329,7 +307,7 @@ class NineAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
var u = 0
|
||||
for (o in t.indices) {
|
||||
e = e shl 6
|
||||
i = key.indexOf(t[o])
|
||||
i = nineAnimeKey.indexOf(t[o])
|
||||
e = e or i
|
||||
u += 6
|
||||
if (24 == u) {
|
||||
@ -358,4 +336,5 @@ class NineAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
private fun decode(input: String): String = java.net.URLDecoder.decode(input, "utf-8")
|
||||
}
|
||||
|
||||
private const val key = "c/aUAorINHBLxWTy3uRiPt8J+vjsOheFG1E0q2X9CYwDZlnmd4Kb5M6gSVzfk7pQ"
|
||||
private const val nineAnimeKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
private const val cipherKey = "rTKp3auwu0ULA6II"
|
||||
|
Reference in New Issue
Block a user