From 4bce41569009eadc4625910b729ed55f1803ff4e Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Fri, 23 Sep 2022 12:41:06 +0200 Subject: [PATCH] Fix allanime --- AllAnimeProvider/build.gradle.kts | 2 +- .../kotlin/com/lagradost/AllAnimeProvider.kt | 216 +++++------------- 2 files changed, 55 insertions(+), 163 deletions(-) diff --git a/AllAnimeProvider/build.gradle.kts b/AllAnimeProvider/build.gradle.kts index ae98728..dcfc449 100644 --- a/AllAnimeProvider/build.gradle.kts +++ b/AllAnimeProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 2 +version = 3 cloudstream { diff --git a/AllAnimeProvider/src/main/kotlin/com/lagradost/AllAnimeProvider.kt b/AllAnimeProvider/src/main/kotlin/com/lagradost/AllAnimeProvider.kt index 153d6e2..31bded0 100644 --- a/AllAnimeProvider/src/main/kotlin/com/lagradost/AllAnimeProvider.kt +++ b/AllAnimeProvider/src/main/kotlin/com/lagradost/AllAnimeProvider.kt @@ -8,7 +8,9 @@ import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.M3u8Helper import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.cloudstream3.utils.getQualityFromName +import org.jsoup.Jsoup +import org.mozilla.javascript.Context +import org.mozilla.javascript.Scriptable import java.net.URI import java.net.URLDecoder @@ -56,7 +58,13 @@ class AllAnimeProvider : MainAPI() { @JsonProperty("status") val status: String?, ) - data class AiredStart( + private data class AvailableEpisodes( + @JsonProperty("sub") val sub: Int, + @JsonProperty("dub") val dub: Int, + @JsonProperty("raw") val raw: Int + ) + + private data class AiredStart( @JsonProperty("year") val year: Int, @JsonProperty("month") val month: Int, @JsonProperty("date") val date: Int @@ -77,11 +85,11 @@ class AllAnimeProvider : MainAPI() { ) data class RandomMain( - @JsonProperty("data") val data: DataRan? = DataRan() + @JsonProperty("data") var data: DataRan? = DataRan() ) data class DataRan( - @JsonProperty("queryRandomRecommendation") val queryRandomRecommendation: ArrayList = arrayListOf() + @JsonProperty("queryRandomRecommendation") var queryRandomRecommendation: ArrayList = arrayListOf() ) data class QueryRandomRecommendation( @@ -96,7 +104,7 @@ class AllAnimeProvider : MainAPI() { @JsonProperty("__typename") val _typename: String? = null ) - override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { + override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { val items = ArrayList() val urls = listOf( // Pair( @@ -150,7 +158,7 @@ class AllAnimeProvider : MainAPI() { override suspend fun search(query: String): List { val link = - """ $mainUrl/graphql?variables={"search":{"allowAdult":false,"allowUnknown":false,"query":"$query"},"limit":26,"page":1,"translationType":"dub","countryOrigin":"ALL"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"d2670e3e27ee109630991152c8484fce5ff5e280c523378001f9a23dc1839068"}}""" + """$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 @@ -180,99 +188,29 @@ class AllAnimeProvider : MainAPI() { @JsonProperty("raw") val raw: List ) - data class PageDataRoot( - @JsonProperty("data") val data: PageData? = PageData() - ) - - data class PageData( - @JsonProperty("show") val show: Show? = Show() - ) - - data class Show( - @JsonProperty("_id") val Id: String? = null, - @JsonProperty("name") val name: String? = null, - @JsonProperty("description") val description: String? = null, - @JsonProperty("thumbnail") val thumbnail: String? = null, - @JsonProperty("thumbnails") val thumbnails: ArrayList = arrayListOf(), -// @JsonProperty("lastEpisodeInfo" ) val lastEpisodeInfo : LastEpisodeInfo? = LastEpisodeInfo(), -// @JsonProperty("lastEpisodeDate" ) val lastEpisodeDate : LastEpisodeDate? = LastEpisodeDate(), - @JsonProperty("type") val type: String? = null, - @JsonProperty("genres") val genres: ArrayList = arrayListOf(), - @JsonProperty("score") val score: Double? = null, - @JsonProperty("status") val status: String? = null, -// @JsonProperty("season" ) val season : Season? = Season(), - @JsonProperty("altNames") val altNames: ArrayList = arrayListOf(), - @JsonProperty("averageScore") val averageScore: Int? = null, - @JsonProperty("rating") val rating: String? = null, - @JsonProperty("episodeCount") val episodeCount: String? = null, - @JsonProperty("episodeDuration") val episodeDuration: String? = null, - @JsonProperty("broadcastInterval") val broadcastInterval: String? = null, - @JsonProperty("banner") val banner: String? = null, -// @JsonProperty("airedEnd" ) val airedEnd : AiredEnd? = AiredEnd(), - @JsonProperty("airedStart") val airedStart: AiredStart? = null, - @JsonProperty("studios") val studios: ArrayList = arrayListOf(), - @JsonProperty("countryOfOrigin") val countryOfOrigin: String? = null, - @JsonProperty("characters") val characters: ArrayList = arrayListOf(), -// @JsonProperty("availableEpisodesDetail" ) val availableEpisodesDetail : AvailableEpisodesDetail? = AvailableEpisodesDetail(), - @JsonProperty("availableEpisodes") val availableEpisodes: AvailableEpisodes? = null, - @JsonProperty("prevideos") val prevideos: ArrayList = arrayListOf(), - @JsonProperty("nameOnlyString") val nameOnlyString: String? = null, -// @JsonProperty("relatedShows" ) val relatedShows : ArrayList = arrayListOf(), -// @JsonProperty("relatedMangas" ) val relatedMangas : ArrayList = arrayListOf(), -// @JsonProperty("musics" ) val musics : ArrayList = arrayListOf(), - @JsonProperty("isAdult") val isAdult: Boolean? = null, - @JsonProperty("tags") val tags: ArrayList = arrayListOf(), -// @JsonProperty("pageStatus" ) val pageStatus : PageStatus? = PageStatus(), - @JsonProperty("__typename") val _typename: String? = null - ) - - data class AvailableEpisodes( - @JsonProperty("sub") val sub: Int, - @JsonProperty("dub") val dub: Int, - @JsonProperty("raw") val raw: Int - ) - - data class Characters( - @JsonProperty("role") val role: String? = null, - @JsonProperty("name") val name: Name? = Name(), - @JsonProperty("image") val image: Image? = Image(), - @JsonProperty("aniListId") val aniListId: Int? = null, - @JsonProperty("voiceActors") val voiceActors: ArrayList = arrayListOf() - ) - - data class Name( - @JsonProperty("full") val full: String? = null, - @JsonProperty("native") val native: String? = null - ) - - data class Image( - @JsonProperty("large") val large: String? = null, - @JsonProperty("medium") val medium: String? = null - ) - - data class VoiceActors( - @JsonProperty("language") val language: String? = null, - @JsonProperty("aniListId") val aniListId: Int? = null - ) - - data class AllAnimeEpisode( - val id: String, - val episodeNumber: Int, - val type: String - ) { - val url = - """https://allanime.site/allanimeapi?variables={"showId":"$id","translationType":"$type","episodeString":"$episodeNumber"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"29f49ce1a69320b2ab11a475fd114e5c07b03a7dc683f77dd502ca42b26df232"}}""" - } override suspend fun load(url: String): LoadResponse? { - // Needs to be backwards compatible! - val idRegex = Regex("""[^\/]*$""") - val id = idRegex.find(url)?.value ?: throw RuntimeException("Unable to find ID from $url") - val apiUrl = - """https://allanime.site/allanimeapi?variables={"_id":"$id"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"af4b72c51f94ed3b1bd6405ab279881ad84b3ba519ebc2382a1736d34c3c1bf6"}}""" + val rhino = Context.enter() + rhino.initSafeStandardObjects() + rhino.optimizationLevel = -1 + val scope: Scriptable = rhino.initSafeStandardObjects() - val showRoot = app.get(apiUrl).parsed() - val showData = showRoot.data?.show ?: throw RuntimeException("No data found for $showRoot") + val html = app.get(url).text + val soup = Jsoup.parse(html) + + val script = soup.select("script").firstOrNull { + it.html().contains("window.__NUXT__") + } ?: return null + + val js = """ + const window = {} + ${script.html()} + const returnValue = JSON.stringify(window.__NUXT__.fetch[0].show) + """.trimIndent() + + rhino.evaluateString(scope, js, "JavaScript", 1, null) + val jsEval = scope.get("returnValue", scope) ?: return null + val showData = parseJson(jsEval as String) val title = showData.name val description = showData.description @@ -282,19 +220,19 @@ class AllAnimeProvider : MainAPI() { if (it == null) return@let Pair(null, null) Pair(if (it.sub != 0) ((1..it.sub).map { epNum -> Episode( - AllAnimeEpisode(id, epNum, "sub").url, episode = epNum + "$mainUrl/anime/${showData.Id}/episodes/sub/$epNum", episode = epNum ) }) else null, if (it.dub != 0) ((1..it.dub).map { epNum -> Episode( - AllAnimeEpisode(id, epNum, "dub").url, episode = epNum + "$mainUrl/anime/${showData.Id}/episodes/dub/$epNum", episode = epNum ) }) else null) } - val characters = showData.characters.mapNotNull { - val img = it.image?.large ?: it.image?.medium ?: return@mapNotNull null - val name = it.name?.full ?: it.name?.native ?: return@mapNotNull null - val role = when (it.role?.trim()) { + 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()) { "Main" -> ActorRole.Main "Supporting" -> ActorRole.Supporting "Background" -> ActorRole.Background @@ -312,7 +250,7 @@ class AllAnimeProvider : MainAPI() { // AnimeSearchResponse(recName, href, this.name, TvType.Anime, img) //} - return newAnimeLoadResponse(title ?: "", url, TvType.Anime) { + return newAnimeLoadResponse(title, url, TvType.Anime) { posterUrl = poster year = showData.airedStart?.year @@ -374,61 +312,19 @@ class AllAnimeProvider : MainAPI() { @JsonProperty("episodeIframeHead") val episodeIframeHead: String ) - // We support tracks now! private suspend fun getM3u8Qualities( m3u8Link: String, referer: String, - sourceName: String, - qualityString: String?, + qualityName: String, ): List { - return listOf( - ExtractorLink( - this.name, - sourceName, - m3u8Link, - referer, - getQualityFromName(qualityString), - isM3u8 = true - ) + return M3u8Helper.generateM3u8( + this.name, + m3u8Link, + referer, + name = "${this.name} - $qualityName" ) -// return M3u8Helper.generateM3u8( -// this.name, -// m3u8Link, -// referer, -// name = "${this.name} - sourceName" -// ) } - - data class LinkData( - @JsonProperty("data") val data: EpisodeData? = EpisodeData() - ) - - data class SourceUrls( - @JsonProperty("sourceUrl") val sourceUrl: String? = null, - @JsonProperty("priority") val priority: Double? = null, - @JsonProperty("sourceName") val sourceName: String? = null, - @JsonProperty("type") val type: String? = null, - @JsonProperty("className") val className: String? = null, - @JsonProperty("streamerId") val streamerId: String? = null - ) - - data class EpisodeData( - @JsonProperty("episode") val episode: Episode? = Episode() - ) - - data class Episode( - @JsonProperty("episodeString") val episodeString: String? = null, -// @JsonProperty("uploadDate" ) val uploadDate : UploadDate? = UploadDate(), - @JsonProperty("sourceUrls") val sourceUrls: ArrayList = arrayListOf(), - @JsonProperty("thumbnail") val thumbnail: String? = null, - @JsonProperty("notes") val notes: String? = null, - @JsonProperty("show") val show: Show? = Show(), -// @JsonProperty("pageStatus" ) val pageStatus : PageStatus? = PageStatus(), - @JsonProperty("versionFix") val versionFix: String? = null, - @JsonProperty("__typename") val _typename: String? = null - ) - override suspend fun loadLinks( data: String, isCasting: Boolean, @@ -440,11 +336,13 @@ class AllAnimeProvider : MainAPI() { if (apiEndPoint.endsWith("/")) apiEndPoint = apiEndPoint.slice(0 until apiEndPoint.length - 1) - val sources = app.get(data).parsed().data?.episode?.sourceUrls + val html = app.get(data).text - sources?.apmap { + val sources = Regex("""sourceUrl[:=]"(.+?)"""").findAll(html).toList() + .map { URLDecoder.decode(it.destructured.component1().sanitize(), "UTF-8") } + sources.apmap { safeApiCall { - var link = it.sourceUrl?.replace(" ", "%20") ?: return@safeApiCall + var link = it.replace(" ", "%20") if (URI(link).isAbsolute || link.startsWith("//")) { if (link.startsWith("//")) link = "https:$it" @@ -452,17 +350,12 @@ class AllAnimeProvider : MainAPI() { // for now ignore } else if (!embedIsBlacklisted(link)) { if (URI(link).path.contains(".m3u")) { - getM3u8Qualities( - link, - data, - it.sourceName ?: URI(link).host, - null - ).forEach(callback) + getM3u8Qualities(link, data, URI(link).host).forEach(callback) } else { callback( ExtractorLink( "AllAnime - " + URI(link).host, - it.sourceName ?: "", + "", link, data, Qualities.P1080.value, @@ -484,7 +377,6 @@ class AllAnimeProvider : MainAPI() { "$apiEndPoint/player?uri=" + (if (URI(server.link).host.isNotEmpty()) server.link else apiEndPoint + URI( server.link ).path), - it.sourceName ?: server.resolutionStr, server.resolutionStr ).forEach(callback) } else {