2022-08-11 14:55:58 +00:00
|
|
|
package com.lagradost
|
|
|
|
|
|
|
|
import com.fasterxml.jackson.annotation.JsonProperty
|
|
|
|
import com.lagradost.cloudstream3.*
|
|
|
|
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
|
2022-12-09 19:07:35 +00:00
|
|
|
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
2022-08-11 14:55:58 +00:00
|
|
|
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
|
|
|
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
2023-02-18 01:13:46 +00:00
|
|
|
import com.lagradost.cloudstream3.utils.AppUtils.toJson
|
2023-03-03 13:30:21 +00:00
|
|
|
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
2023-02-18 01:13:46 +00:00
|
|
|
import com.lagradost.cloudstream3.utils.Coroutines.mainWork
|
2022-08-11 14:55:58 +00:00
|
|
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
|
|
|
import com.lagradost.cloudstream3.utils.M3u8Helper
|
|
|
|
import com.lagradost.cloudstream3.utils.Qualities
|
2023-02-18 01:13:46 +00:00
|
|
|
import com.lagradost.cloudstream3.utils.loadExtractor
|
2022-09-23 10:41:06 +00:00
|
|
|
import org.jsoup.Jsoup
|
|
|
|
import org.mozilla.javascript.Context
|
|
|
|
import org.mozilla.javascript.Scriptable
|
2022-08-11 14:55:58 +00:00
|
|
|
import java.net.URI
|
|
|
|
|
|
|
|
|
|
|
|
class AllAnimeProvider : MainAPI() {
|
2023-02-18 01:13:46 +00:00
|
|
|
override var mainUrl = "https://allanime.to"
|
|
|
|
private val apiUrl = "https://api.allanime.co"
|
2022-08-11 14:55:58 +00:00
|
|
|
override var name = "AllAnime"
|
|
|
|
override val hasQuickSearch = false
|
|
|
|
override val hasMainPage = true
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
private data class Data(
|
|
|
|
@JsonProperty("shows") val shows: Shows
|
|
|
|
)
|
|
|
|
|
|
|
|
private data class Shows(
|
|
|
|
@JsonProperty("pageInfo") val pageInfo: PageInfo,
|
|
|
|
@JsonProperty("edges") val edges: List<Edges>,
|
|
|
|
@JsonProperty("__typename") val _typename: String
|
|
|
|
)
|
|
|
|
|
2023-03-03 13:30:21 +00:00
|
|
|
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?,
|
|
|
|
)
|
|
|
|
|
2022-08-11 14:55:58 +00:00
|
|
|
private data class Edges(
|
|
|
|
@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>?,
|
2022-12-09 19:07:35 +00:00
|
|
|
@JsonProperty("genres") val genres: List<String>?,
|
|
|
|
@JsonProperty("averageScore") val averageScore: Int?,
|
2023-03-03 13:30:21 +00:00
|
|
|
@JsonProperty("characters") val characters: List<Characters>?,
|
2022-08-11 14:55:58 +00:00
|
|
|
@JsonProperty("description") val description: String?,
|
|
|
|
@JsonProperty("status") val status: String?,
|
2023-02-18 01:13:46 +00:00
|
|
|
@JsonProperty("banner") val banner: String?,
|
|
|
|
@JsonProperty("episodeDuration") val episodeDuration: Int?,
|
|
|
|
@JsonProperty("prevideos") val prevideos: List<String> = emptyList(),
|
|
|
|
)
|
2022-08-11 14:55:58 +00:00
|
|
|
|
2022-09-23 10:41:06 +00:00
|
|
|
private data class AvailableEpisodes(
|
|
|
|
@JsonProperty("sub") val sub: Int,
|
|
|
|
@JsonProperty("dub") val dub: Int,
|
|
|
|
@JsonProperty("raw") val raw: Int
|
|
|
|
)
|
|
|
|
|
|
|
|
private data class AiredStart(
|
2022-08-11 14:55:58 +00:00
|
|
|
@JsonProperty("year") val year: Int,
|
|
|
|
@JsonProperty("month") val month: Int,
|
|
|
|
@JsonProperty("date") val date: Int
|
|
|
|
)
|
|
|
|
|
|
|
|
private data class Season(
|
|
|
|
@JsonProperty("quarter") val quarter: String,
|
|
|
|
@JsonProperty("year") val year: Int
|
|
|
|
)
|
|
|
|
|
|
|
|
private data class PageInfo(
|
|
|
|
@JsonProperty("total") val total: Int,
|
|
|
|
@JsonProperty("__typename") val _typename: String
|
|
|
|
)
|
|
|
|
|
|
|
|
private data class AllAnimeQuery(
|
|
|
|
@JsonProperty("data") val data: Data
|
|
|
|
)
|
|
|
|
|
|
|
|
data class RandomMain(
|
2022-09-23 10:41:06 +00:00
|
|
|
@JsonProperty("data") var data: DataRan? = DataRan()
|
2022-08-11 14:55:58 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
data class DataRan(
|
2022-09-23 10:41:06 +00:00
|
|
|
@JsonProperty("queryRandomRecommendation") var queryRandomRecommendation: ArrayList<QueryRandomRecommendation> = arrayListOf()
|
2022-08-11 14:55:58 +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-10-31 18:20:29 +00:00
|
|
|
private val popularTitle = "Popular"
|
|
|
|
private val recentTitle = "Recently updated"
|
|
|
|
override val mainPage = listOf(
|
|
|
|
MainPageData(
|
|
|
|
recentTitle,
|
2023-03-10 19:07:03 +00:00
|
|
|
"""$mainUrl/allanimeapi?variables={"search":{"sortBy":"Recent","allowAdult":${settingsForProvider.enableAdult},"allowUnknown":false},"limit":26,"page":%d,"translationType":"sub","countryOrigin":"ALL"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"c4305f3918591071dfecd081da12243725364f6b7dd92072df09d915e390b1b7"}}"""
|
2022-10-31 18:20:29 +00:00
|
|
|
),
|
|
|
|
MainPageData(
|
|
|
|
popularTitle,
|
2023-03-10 19:07:03 +00:00
|
|
|
"""$mainUrl/allanimeapi?variables={"type":"anime","size":30,"dateRange":1,"page":%d,"allowAdult":${settingsForProvider.enableAdult},"allowUnknown":false}&extensions={"persistedQuery":{"version":1,"sha256Hash":"563c9c7c7fb5218aaf5562ad5d7cabb9ece03a36b4bc94f1384ba70709bd61da"}}"""
|
2022-08-11 14:55:58 +00:00
|
|
|
)
|
2022-10-31 18:20:29 +00:00
|
|
|
)
|
2022-08-11 14:55:58 +00:00
|
|
|
|
2022-10-31 18:20:29 +00:00
|
|
|
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
|
|
|
|
val url = request.data.format(page)
|
|
|
|
val test = app.get(url).text
|
2022-08-11 14:55:58 +00:00
|
|
|
|
2022-10-31 18:20:29 +00:00
|
|
|
val home = when (request.name) {
|
|
|
|
recentTitle -> {
|
|
|
|
val json = parseJson<AllAnimeQuery>(test)
|
|
|
|
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)
|
|
|
|
}
|
2022-08-11 14:55:58 +00:00
|
|
|
|
2022-10-31 18:20:29 +00:00
|
|
|
results.map {
|
2022-08-11 14:55:58 +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)
|
2022-10-31 18:20:29 +00:00
|
|
|
}
|
|
|
|
}
|
2022-08-11 14:55:58 +00:00
|
|
|
}
|
2022-10-31 18:20:29 +00:00
|
|
|
popularTitle -> {
|
|
|
|
val json = parseJson<PopularQuery>(test)
|
|
|
|
val results = json.data?.queryPopular?.recommendations?.filter {
|
|
|
|
// filtering in case there is an anime with 0 episodes available on the site.
|
|
|
|
!(it.anyCard?.availableEpisodes?.raw == 0 && it.anyCard.availableEpisodes.sub == 0 && it.anyCard.availableEpisodes.dub == 0)
|
|
|
|
}
|
|
|
|
results?.mapNotNull {
|
|
|
|
newAnimeSearchResponse(
|
|
|
|
it.anyCard?.name ?: return@mapNotNull null,
|
|
|
|
"$mainUrl/anime/${it.anyCard.Id ?: it.pageStatus?.Id}",
|
|
|
|
fix = false
|
|
|
|
) {
|
|
|
|
this.posterUrl = it.anyCard.thumbnail
|
|
|
|
this.otherName = it.anyCard.englishName
|
|
|
|
addDub(it.anyCard.availableEpisodes?.dub)
|
|
|
|
addSub(it.anyCard.availableEpisodes?.sub)
|
|
|
|
}
|
|
|
|
} ?: emptyList()
|
|
|
|
}
|
|
|
|
else -> emptyList()
|
2022-08-11 14:55:58 +00:00
|
|
|
}
|
|
|
|
|
2022-10-31 18:20:29 +00:00
|
|
|
|
|
|
|
|
|
|
|
return HomePageResponse(
|
|
|
|
listOf(
|
|
|
|
HomePageList(request.name, home)
|
|
|
|
), hasNext = home.isNotEmpty()
|
|
|
|
)
|
2022-08-11 14:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
override suspend fun search(query: String): List<SearchResponse> {
|
|
|
|
val link =
|
2023-03-10 19:07:03 +00:00
|
|
|
"""$mainUrl/allanimeapi?variables={"search":{"allowAdult":false,"allowUnknown":false,"query":"$query"},"limit":26,"page":1,"translationType":"sub","countryOrigin":"ALL"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"c4305f3918591071dfecd081da12243725364f6b7dd92072df09d915e390b1b7"}}"""
|
2023-03-03 13:30:21 +00:00
|
|
|
val res = app.get(link).text.takeUnless { it.contains("PERSISTED_QUERY_NOT_FOUND") }
|
2023-03-14 12:33:31 +00:00
|
|
|
// Retries
|
2023-03-03 13:30:21 +00:00
|
|
|
?: app.get(link).text.takeUnless { it.contains("PERSISTED_QUERY_NOT_FOUND") }
|
|
|
|
?: return emptyList()
|
|
|
|
|
2022-08-11 14:55:58 +00:00
|
|
|
val response = parseJson<AllAnimeQuery>(res)
|
|
|
|
|
|
|
|
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 {
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private data class AvailableEpisodesDetail(
|
|
|
|
@JsonProperty("sub") val sub: List<String>,
|
|
|
|
@JsonProperty("dub") val dub: List<String>,
|
|
|
|
@JsonProperty("raw") val raw: List<String>
|
|
|
|
)
|
|
|
|
|
2023-03-03 13:30:21 +00:00
|
|
|
/**
|
|
|
|
* @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
|
2022-09-23 10:41:06 +00:00
|
|
|
val soup = Jsoup.parse(html)
|
2022-09-12 17:04:59 +00:00
|
|
|
|
2023-03-03 13:30:21 +00:00
|
|
|
val scope = mainWork {
|
|
|
|
rhino.initSafeStandardObjects() as Scriptable
|
|
|
|
}
|
|
|
|
|
2022-09-23 10:41:06 +00:00
|
|
|
val script = soup.select("script").firstOrNull {
|
|
|
|
it.html().contains("window.__NUXT__")
|
|
|
|
} ?: return null
|
2022-09-12 17:04:59 +00:00
|
|
|
|
2022-09-23 10:41:06 +00:00
|
|
|
val js = """
|
|
|
|
const window = {}
|
|
|
|
${script.html()}
|
2023-03-03 13:30:21 +00:00
|
|
|
const returnValue = JSON.stringify(window.__NUXT__.fetch[0].show) || window.__NUXT__.fetch[0].errorQueryString
|
2022-09-23 10:41:06 +00:00
|
|
|
""".trimIndent()
|
2022-09-12 17:04:59 +00:00
|
|
|
|
2023-03-03 13:30:21 +00:00
|
|
|
return mainWork {
|
2023-02-18 01:13:46 +00:00
|
|
|
rhino.evaluateString(scope, js, "JavaScript", 1, null)
|
|
|
|
scope.get("returnValue", scope) ?: return@mainWork null
|
2023-03-03 13:30:21 +00:00
|
|
|
} as? String
|
|
|
|
}
|
2022-12-09 19:07:35 +00:00
|
|
|
|
2023-03-03 13:30:21 +00:00
|
|
|
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<Edges>(show)
|
|
|
|
// Use the redirect url if the url is outdated
|
|
|
|
?: getShow(
|
|
|
|
fixUrl(show),
|
|
|
|
rhino
|
|
|
|
)?.let { parseJson<Edges>(it) } ?: return null
|
2022-08-11 14:55:58 +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)
|
2023-02-18 01:13:46 +00:00
|
|
|
if (showData.Id == null) return@let Pair(null, null)
|
|
|
|
|
2022-08-11 14:55:58 +00:00
|
|
|
Pair(if (it.sub != 0) ((1..it.sub).map { epNum ->
|
|
|
|
Episode(
|
2023-02-18 01:13:46 +00:00
|
|
|
AllAnimeLoadData(showData.Id, "sub", epNum).toJson(), episode = epNum
|
2022-08-11 14:55:58 +00:00
|
|
|
)
|
|
|
|
}) else null, if (it.dub != 0) ((1..it.dub).map { epNum ->
|
|
|
|
Episode(
|
2023-02-18 01:13:46 +00:00
|
|
|
AllAnimeLoadData(showData.Id, "dub", epNum).toJson(), episode = epNum
|
2022-08-11 14:55:58 +00:00
|
|
|
)
|
|
|
|
}) else null)
|
|
|
|
}
|
|
|
|
|
2023-03-03 13:30:21 +00:00
|
|
|
val characters = showData.characters?.map {
|
|
|
|
val role = when (it.role) {
|
2022-08-11 14:55:58 +00:00
|
|
|
"Main" -> ActorRole.Main
|
|
|
|
"Supporting" -> ActorRole.Supporting
|
|
|
|
"Background" -> ActorRole.Background
|
|
|
|
else -> null
|
|
|
|
}
|
2023-03-03 13:30:21 +00:00
|
|
|
val name = it.name?.full ?: it.name?.native ?: ""
|
|
|
|
val image = it.image?.large ?: it.image?.medium
|
|
|
|
Pair(Actor(name, image), role)
|
2022-08-11 14:55:58 +00:00
|
|
|
}
|
|
|
|
|
2022-12-09 19:07:35 +00:00
|
|
|
// bruh, they use graphql and bruh it is fucked
|
2022-08-11 14:55:58 +00:00
|
|
|
//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-09-23 10:41:06 +00:00
|
|
|
return newAnimeLoadResponse(title, url, TvType.Anime) {
|
2022-08-11 14:55:58 +00:00
|
|
|
posterUrl = poster
|
2022-12-09 19:07:35 +00:00
|
|
|
backgroundPosterUrl = showData.banner
|
|
|
|
rating = showData.averageScore?.times(100)
|
|
|
|
tags = showData.genres
|
2022-08-11 14:55:58 +00:00
|
|
|
year = showData.airedStart?.year
|
2022-12-09 19:07:35 +00:00
|
|
|
duration = showData.episodeDuration?.div(60_000)
|
2023-02-18 01:13:46 +00:00
|
|
|
addTrailer(showData.prevideos.filter { it.isNotBlank() }
|
|
|
|
.map { "https://www.youtube.com/watch?v=$it" })
|
2022-08-11 14:55:58 +00:00
|
|
|
|
|
|
|
addEpisodes(DubStatus.Subbed, episodes.first)
|
|
|
|
addEpisodes(DubStatus.Dubbed, episodes.second)
|
|
|
|
addActors(characters)
|
|
|
|
//this.recommendations = recommendations
|
|
|
|
|
|
|
|
showStatus = getStatus(showData.status.toString())
|
|
|
|
|
|
|
|
plot = description?.replace(Regex("""<(.*?)>"""), "")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private val embedBlackList = listOf(
|
|
|
|
"https://mp4upload.com/",
|
|
|
|
"https://streamsb.net/",
|
|
|
|
"https://dood.to/",
|
|
|
|
"https://videobin.co/",
|
|
|
|
"https://ok.ru",
|
|
|
|
"https://streamlare.com",
|
2023-02-18 12:47:41 +00:00
|
|
|
"streaming.php",
|
2022-08-11 14:55:58 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
private fun embedIsBlacklisted(url: String): Boolean {
|
|
|
|
embedBlackList.forEach {
|
|
|
|
if (it.javaClass.name == "kotlin.text.Regex") {
|
|
|
|
if ((it as Regex).matches(url)) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (url.contains(it)) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-03-14 12:33:31 +00:00
|
|
|
private data class Stream(
|
|
|
|
@JsonProperty("format") val format: String? = null,
|
|
|
|
@JsonProperty("audio_lang") val audio_lang: String? = null,
|
|
|
|
@JsonProperty("hardsub_lang") val hardsub_lang: String? = null,
|
|
|
|
@JsonProperty("url") val url: String? = null,
|
|
|
|
)
|
|
|
|
|
|
|
|
private data class PortData(
|
|
|
|
@JsonProperty("streams") val streams: ArrayList<Stream>? = arrayListOf(),
|
|
|
|
)
|
|
|
|
|
2022-08-11 14:55:58 +00:00
|
|
|
private data class Links(
|
|
|
|
@JsonProperty("link") val link: String,
|
|
|
|
@JsonProperty("hls") val hls: Boolean?,
|
|
|
|
@JsonProperty("resolutionStr") val resolutionStr: String,
|
2023-03-14 12:33:31 +00:00
|
|
|
@JsonProperty("src") val src: String?,
|
|
|
|
@JsonProperty("portData") val portData: PortData? = null,
|
2022-08-11 14:55:58 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
private data class AllAnimeVideoApiResponse(
|
|
|
|
@JsonProperty("links") val links: List<Links>
|
|
|
|
)
|
|
|
|
|
|
|
|
private data class ApiEndPoint(
|
|
|
|
@JsonProperty("episodeIframeHead") val episodeIframeHead: String
|
|
|
|
)
|
|
|
|
|
|
|
|
private suspend fun getM3u8Qualities(
|
|
|
|
m3u8Link: String,
|
|
|
|
referer: String,
|
2022-09-23 10:41:06 +00:00
|
|
|
qualityName: String,
|
2022-08-11 14:55:58 +00:00
|
|
|
): List<ExtractorLink> {
|
2022-09-23 10:41:06 +00:00
|
|
|
return M3u8Helper.generateM3u8(
|
|
|
|
this.name,
|
|
|
|
m3u8Link,
|
|
|
|
referer,
|
|
|
|
name = "${this.name} - $qualityName"
|
2022-08-11 14:55:58 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-02-18 01:13:46 +00:00
|
|
|
data class AllAnimeLoadData(
|
|
|
|
val hash: String,
|
|
|
|
val dubStatus: String,
|
|
|
|
val episode: Int
|
|
|
|
)
|
|
|
|
|
2023-03-14 12:33:31 +00:00
|
|
|
private suspend fun PortData.extractAcSources(callback: (ExtractorLink) -> Unit) {
|
|
|
|
this.streams?.filter { it.format == "adaptive_hls" && it.hardsub_lang == "en-US" }?.map { source ->
|
|
|
|
M3u8Helper.generateM3u8(
|
|
|
|
"Crunchyroll",
|
|
|
|
source.url ?: return@map,
|
|
|
|
"https://static.crunchyroll.com/",
|
|
|
|
).forEach(callback)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-11 14:55:58 +00:00
|
|
|
override suspend fun loadLinks(
|
|
|
|
data: String,
|
|
|
|
isCasting: Boolean,
|
|
|
|
subtitleCallback: (SubtitleFile) -> Unit,
|
|
|
|
callback: (ExtractorLink) -> Unit
|
|
|
|
): Boolean {
|
2023-02-18 01:13:46 +00:00
|
|
|
val loadData = parseJson<AllAnimeLoadData>(data)
|
2023-03-03 13:30:21 +00:00
|
|
|
val apiEndPoint =
|
|
|
|
parseJson<ApiEndPoint>(app.get("$mainUrl/getVersion").text).episodeIframeHead.removeSuffix("/")
|
2022-08-11 14:55:58 +00:00
|
|
|
|
2023-02-18 01:13:46 +00:00
|
|
|
val apiUrl =
|
2023-03-10 19:07:03 +00:00
|
|
|
"""$apiUrl/allanimeapi?variables={"showId":"${loadData.hash}","translationType":"${loadData.dubStatus}","episodeString":"${loadData.episode}"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"919e327075ac9e249d003aa3f804a48bbdf22d7b1d107ffe659accd54283ce48"}}"""
|
2023-02-18 01:13:46 +00:00
|
|
|
val apiResponse = app.get(apiUrl).parsed<LinksQuery>()
|
2022-08-11 14:55:58 +00:00
|
|
|
|
2023-02-18 01:13:46 +00:00
|
|
|
apiResponse.data?.episode?.sourceUrls?.apmap { source ->
|
2022-08-11 14:55:58 +00:00
|
|
|
safeApiCall {
|
2023-02-18 01:13:46 +00:00
|
|
|
val link = source.sourceUrl?.replace(" ", "%20") ?: return@safeApiCall
|
2022-08-11 14:55:58 +00:00
|
|
|
if (URI(link).isAbsolute || link.startsWith("//")) {
|
2023-02-18 01:13:46 +00:00
|
|
|
val fixedLink = if (link.startsWith("//")) "https:$link" else link
|
|
|
|
val sourceName = source.sourceName ?: URI(link).host
|
|
|
|
|
|
|
|
if (embedIsBlacklisted(fixedLink)) {
|
|
|
|
loadExtractor(fixedLink, subtitleCallback, callback)
|
|
|
|
} else if (URI(fixedLink).path.contains(".m3u")) {
|
|
|
|
getM3u8Qualities(fixedLink, mainUrl, sourceName).forEach(callback)
|
|
|
|
} else {
|
|
|
|
callback(
|
|
|
|
ExtractorLink(
|
|
|
|
name,
|
|
|
|
sourceName,
|
|
|
|
fixedLink,
|
|
|
|
mainUrl,
|
|
|
|
Qualities.P1080.value,
|
|
|
|
false
|
2022-08-11 14:55:58 +00:00
|
|
|
)
|
2023-02-18 01:13:46 +00:00
|
|
|
)
|
2022-08-11 14:55:58 +00:00
|
|
|
}
|
|
|
|
} else {
|
2023-02-18 01:13:46 +00:00
|
|
|
val fixedLink = apiEndPoint + URI(link).path + ".json?" + URI(link).query
|
|
|
|
val links = app.get(fixedLink).parsedSafe<AllAnimeVideoApiResponse>()?.links
|
|
|
|
?: emptyList()
|
|
|
|
links.forEach { server ->
|
2023-03-14 12:33:31 +00:00
|
|
|
when {
|
|
|
|
source.sourceName == "Ac" -> {
|
|
|
|
server.portData?.extractAcSources(callback)
|
|
|
|
}
|
|
|
|
server.hls != null && server.hls -> {
|
|
|
|
getM3u8Qualities(
|
2022-08-11 14:55:58 +00:00
|
|
|
server.link,
|
|
|
|
"$apiEndPoint/player?uri=" + (if (URI(server.link).host.isNotEmpty()) server.link else apiEndPoint + URI(
|
|
|
|
server.link
|
|
|
|
).path),
|
2023-03-14 12:33:31 +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),
|
|
|
|
Qualities.P1080.value,
|
|
|
|
false
|
|
|
|
)
|
2022-08-11 14:55:58 +00:00
|
|
|
)
|
2023-03-14 12:33:31 +00:00
|
|
|
}
|
2022-08-11 14:55:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|