AquaStream/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AllAnimeProvider.kt

405 lines
16 KiB
Kotlin
Raw Normal View History

2021-10-28 18:47:46 +00:00
package com.lagradost.cloudstream3.animeproviders
2021-11-02 15:09:29 +00:00
import com.fasterxml.jackson.annotation.JsonProperty
2021-10-28 18:47:46 +00:00
import com.lagradost.cloudstream3.*
2022-02-05 23:58:56 +00:00
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
import com.lagradost.cloudstream3.mvvm.safeApiCall
2022-04-07 15:54:20 +00:00
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
2021-10-28 18:47:46 +00:00
import com.lagradost.cloudstream3.utils.ExtractorLink
2021-11-02 15:09:29 +00:00
import com.lagradost.cloudstream3.utils.M3u8Helper
2022-04-18 23:20:28 +00:00
import com.lagradost.cloudstream3.utils.Qualities
2021-10-28 18:47:46 +00:00
import org.jsoup.Jsoup
import org.mozilla.javascript.Context
import org.mozilla.javascript.Scriptable
import java.net.URI
import java.net.URLDecoder
class AllAnimeProvider : MainAPI() {
2022-03-16 15:29:11 +00:00
override var mainUrl = "https://allanime.site"
override var name = "AllAnime"
override val hasQuickSearch = false
2022-04-07 15:54:20 +00:00
override val hasMainPage = true
2021-11-02 15:09:29 +00:00
2021-10-28 18:47:46 +00:00
private fun getStatus(t: String): ShowStatus {
return when (t) {
"Finished" -> ShowStatus.Completed
"Releasing" -> ShowStatus.Ongoing
else -> ShowStatus.Completed
}
}
override val supportedTypes = setOf(TvType.Anime, TvType.AnimeMovie)
2021-10-28 18:47:46 +00:00
2021-11-02 15:09:29 +00:00
private data class Data(
2021-10-28 18:47:46 +00:00
@JsonProperty("shows") val shows: Shows
)
2021-11-02 15:09:29 +00:00
private data class Shows(
2021-10-28 18:47:46 +00:00
@JsonProperty("pageInfo") val pageInfo: PageInfo,
@JsonProperty("edges") val edges: List<Edges>,
@JsonProperty("__typename") val _typename: String
)
2021-11-02 15:09:29 +00:00
private data class Edges(
2021-10-28 18:47:46 +00:00
@JsonProperty("_id") val Id: String?,
@JsonProperty("name") val name: String,
@JsonProperty("englishName") val englishName: String?,
@JsonProperty("nativeName") val nativeName: String?,
@JsonProperty("thumbnail") val thumbnail: String?,
@JsonProperty("type") val type: String?,
@JsonProperty("season") val season: Season?,
@JsonProperty("score") val score: Double?,
@JsonProperty("airedStart") val airedStart: AiredStart?,
@JsonProperty("availableEpisodes") val availableEpisodes: AvailableEpisodes?,
@JsonProperty("availableEpisodesDetail") val availableEpisodesDetail: AvailableEpisodesDetail?,
@JsonProperty("studios") val studios: List<String>?,
@JsonProperty("description") val description: String?,
@JsonProperty("status") val status: String?,
)
2021-11-02 15:09:29 +00:00
private data class AvailableEpisodes(
2021-10-28 18:47:46 +00:00
@JsonProperty("sub") val sub: Int,
@JsonProperty("dub") val dub: Int,
@JsonProperty("raw") val raw: Int
)
2021-11-02 15:09:29 +00:00
private data class AiredStart(
2021-10-28 18:47:46 +00:00
@JsonProperty("year") val year: Int,
@JsonProperty("month") val month: Int,
@JsonProperty("date") val date: Int
)
2021-11-02 15:09:29 +00:00
private data class Season(
2021-10-28 18:47:46 +00:00
@JsonProperty("quarter") val quarter: String,
@JsonProperty("year") val year: Int
)
2021-11-02 15:09:29 +00:00
private data class PageInfo(
2021-10-28 18:47:46 +00:00
@JsonProperty("total") val total: Int,
@JsonProperty("__typename") val _typename: String
)
2021-11-02 15:09:29 +00:00
private data class AllAnimeQuery(
2021-10-28 18:47:46 +00:00
@JsonProperty("data") val data: Data
)
2022-04-13 17:29:30 +00:00
data class RandomMain(
@JsonProperty("data") var data: DataRan? = DataRan()
2022-04-07 15:54:20 +00:00
)
2022-04-13 17:29:30 +00:00
data class DataRan(
@JsonProperty("queryRandomRecommendation") var queryRandomRecommendation: ArrayList<QueryRandomRecommendation> = arrayListOf()
2022-04-07 15:54:20 +00:00
)
2022-04-13 17:29:30 +00:00
data class QueryRandomRecommendation(
@JsonProperty("_id") val Id: String? = null,
@JsonProperty("name") val name: String? = null,
@JsonProperty("englishName") val englishName: String? = null,
@JsonProperty("nativeName") val nativeName: String? = null,
@JsonProperty("thumbnail") val thumbnail: String? = null,
@JsonProperty("airedStart") val airedStart: String? = null,
@JsonProperty("availableChapters") val availableChapters: String? = null,
@JsonProperty("availableEpisodes") val availableEpisodes: String? = null,
@JsonProperty("__typename") val _typename: String? = null
2022-04-07 15:54:20 +00:00
)
2022-07-31 15:02:22 +00:00
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
2022-04-07 15:54:20 +00:00
val items = ArrayList<HomePageList>()
val urls = listOf(
2022-07-07 13:12:41 +00:00
// Pair(
// "Top Anime",
// """$mainUrl/graphql?variables={"type":"anime","size":30,"dateRange":30}&extensions={"persistedQuery":{"version":1,"sha256Hash":"276d52ba09ca48ce2b8beb3affb26d9d673b22f9d1fd4892aaa39524128bc745"}}"""
// ),
// "countryOrigin":"JP" for Japanese only
2022-04-13 17:29:30 +00:00
Pair(
2022-07-07 13:12:41 +00:00
"Recently updated",
"""$mainUrl/graphql?variables={"search":{"allowAdult":false,"allowUnknown":false},"limit":30,"page":1,"translationType":"dub","countryOrigin":"ALL"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"d2670e3e27ee109630991152c8484fce5ff5e280c523378001f9a23dc1839068"}}"""
2022-04-13 17:29:30 +00:00
),
2022-04-07 15:54:20 +00:00
)
2022-04-13 17:29:30 +00:00
val random =
2022-07-07 13:12:41 +00:00
"""$mainUrl/graphql?variables={"format":"anime"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"21ac672633498a3698e8f6a93ce6c2b3722b29a216dcca93363bf012c360cd54"}}"""
2022-04-07 15:54:20 +00:00
val ranlink = app.get(random).text
val jsonran = parseJson<RandomMain>(ranlink)
val ranhome = jsonran.data?.queryRandomRecommendation?.map {
2022-04-18 23:20:28 +00:00
newAnimeSearchResponse(it.name!!, "$mainUrl/anime/${it.Id}", fix = false) {
2022-04-13 17:29:30 +00:00
this.posterUrl = it.thumbnail
this.otherName = it.nativeName
}
2022-04-07 15:54:20 +00:00
}
items.add(HomePageList("Random", ranhome!!))
urls.apmap { (HomeName, url) ->
val test = app.get(url).text
val json = parseJson<AllAnimeQuery>(test)
val home = ArrayList<SearchResponse>()
val results = json.data.shows.edges.filter {
// filtering in case there is an anime with 0 episodes available on the site.
!(it.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0)
}
results.map {
2022-04-13 17:29:30 +00:00
home.add(
newAnimeSearchResponse(it.name, "$mainUrl/anime/${it.Id}", fix = false) {
this.posterUrl = it.thumbnail
this.year = it.airedStart?.year
this.otherName = it.englishName
addDub(it.availableEpisodes?.dub)
addSub(it.availableEpisodes?.sub)
})
2022-04-07 15:54:20 +00:00
}
items.add(HomePageList(HomeName, home))
}
if (items.size <= 0) throw ErrorLoadingException()
return HomePageResponse(items)
}
override suspend fun search(query: String): List<SearchResponse> {
2021-11-02 15:09:29 +00:00
val link =
2022-07-07 13:12:41 +00:00
""" $mainUrl/graphql?variables={"search":{"allowAdult":false,"allowUnknown":false,"query":"$query"},"limit":26,"page":1,"translationType":"dub","countryOrigin":"ALL"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"d2670e3e27ee109630991152c8484fce5ff5e280c523378001f9a23dc1839068"}}"""
var res = app.get(link).text
2021-10-28 18:47:46 +00:00
if (res.contains("PERSISTED_QUERY_NOT_FOUND")) {
res = app.get(link).text
if (res.contains("PERSISTED_QUERY_NOT_FOUND")) return emptyList()
2021-10-28 18:47:46 +00:00
}
val response = parseJson<AllAnimeQuery>(res)
2021-10-28 18:47:46 +00:00
val results = response.data.shows.edges.filter {
// filtering in case there is an anime with 0 episodes available on the site.
!(it.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0)
}
return results.map {
2022-04-13 17:29:30 +00:00
newAnimeSearchResponse(it.name, "$mainUrl/anime/${it.Id}", fix = false) {
this.posterUrl = it.thumbnail
this.year = it.airedStart?.year
this.otherName = it.englishName
addDub(it.availableEpisodes?.dub)
addSub(it.availableEpisodes?.sub)
}
}
2021-10-28 18:47:46 +00:00
}
2021-11-02 15:09:29 +00:00
private data class AvailableEpisodesDetail(
2021-10-28 18:47:46 +00:00
@JsonProperty("sub") val sub: List<String>,
@JsonProperty("dub") val dub: List<String>,
@JsonProperty("raw") val raw: List<String>
)
2022-01-16 22:31:42 +00:00
override suspend fun load(url: String): LoadResponse? {
2021-10-28 18:47:46 +00:00
val rhino = Context.enter()
rhino.initStandardObjects()
rhino.optimizationLevel = -1
val scope: Scriptable = rhino.initStandardObjects()
val html = app.get(url).text
2021-10-28 18:47:46 +00:00
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<Edges>(jsEval as String)
2021-10-28 18:47:46 +00:00
val title = showData.name
val description = showData.description
val poster = showData.thumbnail
val episodes = showData.availableEpisodes.let {
if (it == null) return@let Pair(null, null)
Pair(if (it.sub != 0) ((1..it.sub).map { epNum ->
Episode(
2021-11-02 15:09:29 +00:00
"$mainUrl/anime/${showData.Id}/episodes/sub/$epNum", episode = epNum
2021-10-28 18:47:46 +00:00
)
}) else null, if (it.dub != 0) ((1..it.dub).map { epNum ->
Episode(
2021-11-02 15:09:29 +00:00
"$mainUrl/anime/${showData.Id}/episodes/dub/$epNum", episode = epNum
2021-10-28 18:47:46 +00:00
)
}) else null)
}
val characters = soup.select("div.character > div.card-character-box").mapNotNull {
2022-02-05 23:58:56 +00:00
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
else -> null
}
Pair(Actor(name, img), role)
}
2022-02-06 14:53:39 +00:00
// bruh, they use graphql
//val recommendations = soup.select("#suggesction > div > div.p > .swipercard")?.mapNotNull {
// val recTitle = it?.selectFirst(".showname > a") ?: return@mapNotNull null
// val recName = recTitle.text() ?: return@mapNotNull null
// val href = fixUrlNull(recTitle.attr("href")) ?: return@mapNotNull null
// val img = it.selectFirst(".image > img").attr("src") ?: return@mapNotNull null
// AnimeSearchResponse(recName, href, this.name, TvType.Anime, img)
//}
2022-02-05 23:58:56 +00:00
2021-11-02 15:09:29 +00:00
return newAnimeLoadResponse(title, url, TvType.Anime) {
posterUrl = poster
year = showData.airedStart?.year
addEpisodes(DubStatus.Subbed, episodes.first)
addEpisodes(DubStatus.Dubbed, episodes.second)
2022-02-05 23:58:56 +00:00
addActors(characters)
2022-02-06 14:53:39 +00:00
//this.recommendations = recommendations
2021-11-02 15:09:29 +00:00
showStatus = getStatus(showData.status.toString())
plot = description?.replace(Regex("""<(.*?)>"""), "")
}
2021-10-28 18:47:46 +00:00
}
private val embedBlackList = listOf(
"https://mp4upload.com/",
"https://streamsb.net/",
"https://dood.to/",
"https://videobin.co/",
"https://ok.ru",
"https://streamlare.com",
)
private fun embedIsBlacklisted(url: String): Boolean {
embedBlackList.forEach {
if (it.javaClass.name == "kotlin.text.Regex") {
if ((it as Regex).matches(url)) {
return true
}
} else {
2021-11-02 15:09:29 +00:00
if (url.contains(it)) {
2021-10-28 18:47:46 +00:00
return true
}
}
}
return false
}
private fun String.sanitize(): String {
var out = this
listOf(Pair("\\u002F", "/")).forEach {
out = out.replace(it.first, it.second)
}
return out
}
2021-11-02 15:09:29 +00:00
private data class Links(
2021-10-28 18:47:46 +00:00
@JsonProperty("link") val link: String,
@JsonProperty("hls") val hls: Boolean?,
@JsonProperty("resolutionStr") val resolutionStr: String,
@JsonProperty("src") val src: String?
)
2021-11-02 15:09:29 +00:00
private data class AllAnimeVideoApiResponse(
2021-10-28 18:47:46 +00:00
@JsonProperty("links") val links: List<Links>
)
private data class ApiEndPoint(
@JsonProperty("episodeIframeHead") val episodeIframeHead: String
)
2021-11-02 15:09:29 +00:00
private suspend fun getM3u8Qualities(
2022-01-29 18:57:19 +00:00
m3u8Link: String,
referer: String,
qualityName: String,
): List<ExtractorLink> {
2022-04-18 23:20:28 +00:00
return M3u8Helper.generateM3u8(
this.name,
m3u8Link,
referer,
name = "${this.name} - $qualityName"
)
}
2021-10-28 18:47:46 +00:00
2022-01-16 22:31:42 +00:00
override suspend fun loadLinks(
2021-10-28 18:47:46 +00:00
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
2022-01-29 18:57:19 +00:00
var apiEndPoint =
parseJson<ApiEndPoint>(app.get("$mainUrl/getVersion").text).episodeIframeHead
2022-01-29 18:57:19 +00:00
if (apiEndPoint.endsWith("/")) apiEndPoint =
apiEndPoint.slice(0 until apiEndPoint.length - 1)
2021-10-28 18:47:46 +00:00
val html = app.get(data).text
2021-10-28 18:47:46 +00:00
2021-11-02 15:09:29 +00:00
val sources = Regex("""sourceUrl[:=]"(.+?)"""").findAll(html).toList()
.map { URLDecoder.decode(it.destructured.component1().sanitize(), "UTF-8") }
2022-01-30 01:50:49 +00:00
sources.apmap {
2022-02-05 23:58:56 +00:00
safeApiCall {
var link = it.replace(" ", "%20")
if (URI(link).isAbsolute || link.startsWith("//")) {
if (link.startsWith("//")) link = "https:$it"
if (Regex("""streaming\.php\?""").matches(link)) {
// for now ignore
} else if (!embedIsBlacklisted(link)) {
if (URI(link).path.contains(".m3u")) {
getM3u8Qualities(link, data, URI(link).host).forEach(callback)
2021-11-02 15:09:29 +00:00
} else {
callback(
ExtractorLink(
2022-02-05 23:58:56 +00:00
"AllAnime - " + URI(link).host,
"",
link,
data,
2022-04-18 23:20:28 +00:00
Qualities.P1080.value,
2022-02-05 23:58:56 +00:00
false
)
)
}
}
} else {
link = apiEndPoint + URI(link).path + ".json?" + URI(link).query
val response = app.get(link)
if (response.code < 400) {
val links = parseJson<AllAnimeVideoApiResponse>(response.text).links
2022-02-05 23:58:56 +00:00
links.forEach { server ->
if (server.hls != null && server.hls) {
getM3u8Qualities(
2021-11-02 15:09:29 +00:00
server.link,
"$apiEndPoint/player?uri=" + (if (URI(server.link).host.isNotEmpty()) server.link else apiEndPoint + URI(
server.link
).path),
2022-02-05 23:58:56 +00:00
server.resolutionStr
).forEach(callback)
} else {
callback(
ExtractorLink(
"AllAnime - " + URI(server.link).host,
server.resolutionStr,
server.link,
"$apiEndPoint/player?uri=" + (if (URI(server.link).host.isNotEmpty()) server.link else apiEndPoint + URI(
server.link
).path),
2022-04-18 23:20:28 +00:00
Qualities.P1080.value,
2022-02-05 23:58:56 +00:00
false
)
2021-11-02 15:09:29 +00:00
)
2022-02-05 23:58:56 +00:00
}
}
2021-10-28 18:47:46 +00:00
}
}
}
}
return true
}
}