fix 9anime

This commit is contained in:
jmir1
2022-07-25 23:29:06 +02:00
parent c36009ee71
commit bf82113cf4
2 changed files with 57 additions and 78 deletions

View File

@ -5,7 +5,7 @@ ext {
extName = '9anime'
pkgNameSuffix = 'en.nineanime'
extClass = '.NineAnime'
extVersionCode = 7
extVersionCode = 8
libVersion = '12'
}

View File

@ -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"