From 17ac5f1736138400cc0746cf03cdd6b159c3294e Mon Sep 17 00:00:00 2001 From: no-commit <> Date: Fri, 3 Mar 2023 14:30:21 +0100 Subject: [PATCH] AllAnime fix for time-tagged shows --- AllAnimeProvider/build.gradle.kts | 2 +- .../kotlin/com/lagradost/AllAnimeProvider.kt | 95 ++++++++++++------- 2 files changed, 61 insertions(+), 36 deletions(-) diff --git a/AllAnimeProvider/build.gradle.kts b/AllAnimeProvider/build.gradle.kts index a8c3d1f..dd836ce 100644 --- a/AllAnimeProvider/build.gradle.kts +++ b/AllAnimeProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 6 +version = 7 cloudstream { diff --git a/AllAnimeProvider/src/main/kotlin/com/lagradost/AllAnimeProvider.kt b/AllAnimeProvider/src/main/kotlin/com/lagradost/AllAnimeProvider.kt index cf1e68e..b6e8fef 100644 --- a/AllAnimeProvider/src/main/kotlin/com/lagradost/AllAnimeProvider.kt +++ b/AllAnimeProvider/src/main/kotlin/com/lagradost/AllAnimeProvider.kt @@ -7,6 +7,7 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.mvvm.safeApiCall import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.toJson +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.Coroutines.mainWork import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.M3u8Helper @@ -45,6 +46,22 @@ class AllAnimeProvider : MainAPI() { @JsonProperty("__typename") val _typename: String ) + data class CharacterImage( + @JsonProperty("large") val large: String?, + @JsonProperty("medium") val medium: String? + ) + + data class CharacterName( + @JsonProperty("full") val full: String?, + @JsonProperty("native") val native: String? + ) + + data class Characters( + @JsonProperty("image") val image: CharacterImage?, + @JsonProperty("role") val role: String?, + @JsonProperty("name") val name: CharacterName?, + ) + private data class Edges( @JsonProperty("_id") val Id: String?, @JsonProperty("name") val name: String, @@ -60,6 +77,7 @@ class AllAnimeProvider : MainAPI() { @JsonProperty("studios") val studios: List?, @JsonProperty("genres") val genres: List?, @JsonProperty("averageScore") val averageScore: Int?, + @JsonProperty("characters") val characters: List?, @JsonProperty("description") val description: String?, @JsonProperty("status") val status: String?, @JsonProperty("banner") val banner: String?, @@ -182,11 +200,11 @@ class AllAnimeProvider : MainAPI() { override suspend fun search(query: String): List { val link = """$mainUrl/allanimeapi?variables={"search":{"allowAdult":false,"allowUnknown":false,"query":"$query"},"limit":26,"page":1,"translationType":"sub","countryOrigin":"ALL"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"9c7a8bc1e095a34f2972699e8105f7aaf9082c6e1ccd56eab99c2f1a971152c6"}}""" - var res = app.get(link).text - if (res.contains("PERSISTED_QUERY_NOT_FOUND")) { - res = app.get(link).text - if (res.contains("PERSISTED_QUERY_NOT_FOUND")) return emptyList() - } + val res = app.get(link).text.takeUnless { it.contains("PERSISTED_QUERY_NOT_FOUND") } + // Retries + ?: app.get(link).text.takeUnless { it.contains("PERSISTED_QUERY_NOT_FOUND") } + ?: return emptyList() + val response = parseJson(res) val results = response.data.shows.edges.filter { @@ -211,17 +229,19 @@ class AllAnimeProvider : MainAPI() { @JsonProperty("raw") val raw: List ) - override suspend fun load(url: String): LoadResponse? { - val (rhino, scope) = mainWork { - val rhino = Context.enter() - rhino.optimizationLevel = -1 - val scope: Scriptable = rhino.initSafeStandardObjects() - rhino to scope - } - - val html = app.get(url).text + /** + * @return the show info, or the redirect url if the url is outdated + **/ + private suspend fun getShow(url: String, rhino: Context): String? { + // Fix old bookmarks. + val fixedUrl = url.replace("https://allanime.site", mainUrl) + val html = app.get(fixedUrl).text val soup = Jsoup.parse(html) + val scope = mainWork { + rhino.initSafeStandardObjects() as Scriptable + } + val script = soup.select("script").firstOrNull { it.html().contains("window.__NUXT__") } ?: return null @@ -229,15 +249,30 @@ class AllAnimeProvider : MainAPI() { val js = """ const window = {} ${script.html()} - const returnValue = JSON.stringify(window.__NUXT__.fetch[0].show) + const returnValue = JSON.stringify(window.__NUXT__.fetch[0].show) || window.__NUXT__.fetch[0].errorQueryString """.trimIndent() - val jsEval = mainWork { + return mainWork { rhino.evaluateString(scope, js, "JavaScript", 1, null) scope.get("returnValue", scope) ?: return@mainWork null - } ?: return null + } as? String + } - val showData = parseJson(jsEval as String) + override suspend fun load(url: String): LoadResponse? { + val rhino = mainWork { + Context.enter().apply { + this.optimizationLevel = -1 + } + } + + val show = getShow(url, rhino) ?: return null + // Try parsing the maybe show string + val showData = tryParseJson(show) + // Use the redirect url if the url is outdated + ?: getShow( + fixUrl(show), + rhino + )?.let { parseJson(it) } ?: return null val title = showData.name val description = showData.description @@ -258,16 +293,16 @@ class AllAnimeProvider : MainAPI() { }) else null) } - val characters = soup.select("div.character > div.card-character-box").mapNotNull { - val img = it?.selectFirst("img")?.attr("src") ?: return@mapNotNull null - val name = it.selectFirst("div > a")?.ownText() ?: return@mapNotNull null - val role = when (it.selectFirst("div > .text-secondary")?.text()?.trim()) { + val characters = showData.characters?.map { + val role = when (it.role) { "Main" -> ActorRole.Main "Supporting" -> ActorRole.Supporting "Background" -> ActorRole.Background else -> null } - Pair(Actor(name, img), role) + val name = it.name?.full ?: it.name?.native ?: "" + val image = it.image?.large ?: it.image?.medium + Pair(Actor(name, image), role) } // bruh, they use graphql and bruh it is fucked @@ -325,14 +360,6 @@ class AllAnimeProvider : MainAPI() { return false } - private fun String.sanitize(): String { - var out = this - listOf(Pair("\\u002F", "/")).forEach { - out = out.replace(it.first, it.second) - } - return out - } - private data class Links( @JsonProperty("link") val link: String, @JsonProperty("hls") val hls: Boolean?, @@ -374,10 +401,8 @@ class AllAnimeProvider : MainAPI() { callback: (ExtractorLink) -> Unit ): Boolean { val loadData = parseJson(data) - var apiEndPoint = - parseJson(app.get("$mainUrl/getVersion").text).episodeIframeHead - if (apiEndPoint.endsWith("/")) apiEndPoint = - apiEndPoint.slice(0 until apiEndPoint.length - 1) + val apiEndPoint = + parseJson(app.get("$mainUrl/getVersion").text).episodeIframeHead.removeSuffix("/") val apiUrl = """$apiUrl/allanimeapi?variables={"showId":"${loadData.hash}","translationType":"${loadData.dubStatus}","episodeString":"${loadData.episode}"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"1f0a5d6c9ce6cd3127ee4efd304349345b0737fbf5ec33a60bbc3d18e3bb7c61"}}"""