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

246 lines
8.5 KiB
Kotlin

package com.lagradost.cloudstream3.animeproviders
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
class AniflixProvider : MainAPI() {
override var mainUrl = "https://aniflix.pro"
override var name = "Aniflix"
override val hasMainPage = false
override val supportedTypes = setOf(
TvType.AnimeMovie,
TvType.OVA,
TvType.Anime,
)
companion object {
var token: String? = null
}
private suspend fun getToken(): String {
return token ?: run {
Regex("([^/]*)/_buildManifest\\.js").find(app.get(mainUrl).text)?.groupValues?.getOrNull(
1
)
?.also {
token = it
}
?: throw ErrorLoadingException("No token found")
}
}
private fun Anime.toSearchResponse(): SearchResponse? {
return newAnimeSearchResponse(
title?.english ?: title?.romaji ?: return null,
"$mainUrl/anime/${id ?: return null}"
) {
posterUrl = coverImage?.large ?: coverImage?.medium
}
}
override suspend fun search(query: String): List<SearchResponse>? {
val token = getToken()
val url = "$mainUrl/_next/data/$token/search.json?keyword=$query"
val response = app.get(url)
println("resp: $url ===> ${response.text}")
val searchResponse =
response.parsedSafe<Search>()
?: throw ErrorLoadingException("No Media")
return searchResponse.pageProps?.searchResults?.Page?.media?.mapNotNull { media ->
media.toSearchResponse()
}
}
override suspend fun load(url: String): LoadResponse {
val token = getToken()
val id = Regex("$mainUrl/anime/([0-9]*)").find(url)?.groupValues?.getOrNull(1)
?: throw ErrorLoadingException("Error parsing link for id")
val res = app.get("https://aniflix.pro/_next/data/$token/anime/$id.json?id=$id")
.parsedSafe<AnimeResponsePage>()?.pageProps
?: throw ErrorLoadingException("Invalid Json reponse")
val isMovie = res.anime.format == "MOVIE"
return newAnimeLoadResponse(
res.anime.title?.english ?: res.anime.title?.romaji
?: throw ErrorLoadingException("Invalid title reponse"),
url, if (isMovie) TvType.AnimeMovie else TvType.Anime
) {
recommendations = res.recommended.mapNotNull { it.toSearchResponse() }
tags = res.anime.genres
posterUrl = res.anime.coverImage?.large ?: res.anime.coverImage?.medium
plot = res.anime.description
showStatus = when (res.anime.status) {
"FINISHED" -> ShowStatus.Completed
"RELEASING" -> ShowStatus.Ongoing
else -> null
}
addAniListId(id.toIntOrNull())
// subbed because they are both subbed and dubbed
if (isMovie)
addEpisodes(
DubStatus.Subbed,
listOf(newEpisode("$mainUrl/api/anime/?id=$id&episode=1"))
)
else
addEpisodes(DubStatus.Subbed, res.episodes.episodes?.nodes?.mapNotNull { node ->
val ep = node?.number ?: return@mapNotNull null
//"$mainUrl/_next/data/$token/watch/$id.json?episode=${node.number ?: return@mapNotNull null}&id=$id"
newEpisode("$mainUrl/api/anime?id=$id&episode=${ep}") {
episode = ep
posterUrl = node.thumbnail?.original?.url
name = node.titles?.canonical
}
})
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
return app.get(data).parsed<AniLoadResponse>().let { res ->
val dubReferer = res.dub?.Referer ?: ""
res.dub?.sources?.forEach { source ->
callback(
ExtractorLink(
name,
"${source.label ?: name} (DUB)",
source.file ?: return@forEach,
dubReferer,
getQualityFromName(source.label),
source.type == "hls"
)
)
}
val subReferer = res.dub?.Referer ?: ""
res.sub?.sources?.forEach { source ->
callback(
ExtractorLink(
name,
"${source.label ?: name} (SUB)",
source.file ?: return@forEach,
subReferer,
getQualityFromName(source.label),
source.type == "hls"
)
)
}
!res.dub?.sources.isNullOrEmpty() && !res.sub?.sources.isNullOrEmpty()
}
}
data class AniLoadResponse(
@JsonProperty("sub") val sub: DubSubSource?,
@JsonProperty("dub") val dub: DubSubSource?,
@JsonProperty("episodes") val episodes: Int?
)
data class Sources(
@JsonProperty("file") val file: String?,
@JsonProperty("label") val label: String?,
@JsonProperty("type") val type: String?
)
data class DubSubSource(
@JsonProperty("Referer") var Referer: String?,
@JsonProperty("sources") var sources: ArrayList<Sources> = arrayListOf()
)
data class PageProps(
@JsonProperty("searchResults") val searchResults: SearchResults?
)
data class SearchResults(
@JsonProperty("Page") val Page: Page?
)
data class Page(
@JsonProperty("media") val media: ArrayList<Anime> = arrayListOf()
)
data class CoverImage(
@JsonProperty("color") val color: String?,
@JsonProperty("medium") val medium: String?,
@JsonProperty("large") val large: String?,
)
data class Title(
@JsonProperty("english") val english: String?,
@JsonProperty("romaji") val romaji: String?,
)
data class Search(
@JsonProperty("pageProps") val pageProps: PageProps?,
@JsonProperty("__N_SSP") val _NSSP: Boolean?
)
data class Anime(
@JsonProperty("status") val status: String?,
@JsonProperty("id") val id: Int?,
@JsonProperty("title") val title: Title?,
@JsonProperty("coverImage") val coverImage: CoverImage?,
@JsonProperty("format") val format: String?,
@JsonProperty("duration") val duration: Int?,
@JsonProperty("meanScore") val meanScore: Int?,
@JsonProperty("nextAiringEpisode") val nextAiringEpisode: String?,
@JsonProperty("bannerImage") val bannerImage: String?,
@JsonProperty("description") val description: String?,
@JsonProperty("genres") val genres: ArrayList<String>? = null,
@JsonProperty("season") val season: String?,
@JsonProperty("startDate") val startDate: StartDate?,
)
data class StartDate(
@JsonProperty("year") val year: Int?
)
data class AnimeResponsePage(
@JsonProperty("pageProps") val pageProps: AnimeResponse?,
)
data class AnimeResponse(
@JsonProperty("anime") val anime: Anime,
@JsonProperty("recommended") val recommended: ArrayList<Anime>,
@JsonProperty("episodes") val episodes: EpisodesParent,
)
data class EpisodesParent(
@JsonProperty("id") val id: String?,
@JsonProperty("season") val season: String?,
@JsonProperty("startDate") val startDate: String?,
@JsonProperty("episodeCount") val episodeCount: Int?,
@JsonProperty("episodes") val episodes: Episodes?,
)
data class Episodes(
@JsonProperty("nodes") val nodes: ArrayList<Nodes?> = arrayListOf()
)
data class Nodes(
@JsonProperty("number") val number: Int? = null,
@JsonProperty("titles") val titles: Titles?,
@JsonProperty("thumbnail") val thumbnail: Thumbnail?,
)
data class Titles(
@JsonProperty("canonical") val canonical: String?,
)
data class Original(
@JsonProperty("url") val url: String?,
)
data class Thumbnail(
@JsonProperty("original") val original: Original?,
)
}