added Anroll

This commit is contained in:
hexated 2023-05-14 20:55:48 +07:00
parent b74f198e4d
commit a46583ee2c
4 changed files with 283 additions and 0 deletions

26
Anroll/build.gradle.kts Normal file
View file

@ -0,0 +1,26 @@
// use an integer for version numbers
version = 1
cloudstream {
language = "pt"
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
authors = listOf("Hexated")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
tvTypes = listOf(
"Anime",
"AnimeMovie",
"OVA",
)
iconUrl = "https://www.google.com/s2/favicons?domain=www.anroll.net&sz=%size%"
}

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.hexated"/>

View file

@ -0,0 +1,241 @@
package com.hexated
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import org.jsoup.nodes.Element
class Anroll : MainAPI() {
override var mainUrl = "https://www.anroll.net"
override var name = "Anroll"
override val hasMainPage = true
override var lang = "pt"
override val hasDownloadSupport = true
override val hasQuickSearch = true
override val supportedTypes = setOf(
TvType.Anime,
TvType.AnimeMovie,
TvType.OVA
)
companion object {
private const val searchUrl = "https://apiv2-prd.anroll.net"
private const val episodeUrl = "https://apiv3-prd.anroll.net"
private const val posterUrl = "https://static.anroll.net"
private const val videoUrl = "https://cdn-zenitsu.gamabunta.xyz"
}
override suspend fun getMainPage(
page: Int,
request: MainPageRequest
): HomePageResponse {
val document = app.get("$mainUrl/home").document
val home = mutableListOf<HomePageList>()
document.select("div.sc-f5d5b250-1.iJHcsI").map { div ->
val header = div.selectFirst("h2")?.text() ?: return@map
val child = HomePageList(
header,
div.select("ul li").mapNotNull {
it.toSearchResult()
},
header == "Últimos Laçamentos"
)
home.add(child)
}
return HomePageResponse(home)
}
private fun Element.toSearchResult(): AnimeSearchResponse? {
val title = this.selectFirst("h1")?.text()?.trim() ?: ""
val href = fixUrl(this.selectFirst("a")?.attr("href") ?: return null)
val posterUrl = fixUrlNull(this.select("img").attr("src"))
val epNum = this.selectFirst("span.sc-f5d5b250-3.fsTgnD b")?.text()?.toIntOrNull()
val isDub = this.selectFirst("div.sc-9dbd1f1d-5.efznig")?.text() == "DUB"
return newAnimeSearchResponse(title, href, TvType.Anime) {
this.posterUrl = posterUrl
addDubStatus(isDub, epNum)
}
}
override suspend fun quickSearch(query: String): List<SearchResponse> = search(query)
override suspend fun search(query: String): List<SearchResponse> {
val res = app.get("$searchUrl/search?q=$query").parsedSafe<SearchAnime>()
val collection = mutableListOf<SearchResponse>()
val anime = res?.data_anime?.mapNotNull {
addAnimeSearch(
it.titulo ?: return@mapNotNull null,
"a/${it.generate_id}",
it.slug_serie ?: return@mapNotNull null,
Image.Anime
)
}
if (anime?.isNotEmpty() == true) collection.addAll(anime)
val filme = res?.data_filme?.mapNotNull {
addAnimeSearch(
it.nome_filme ?: return@mapNotNull null,
"f/${it.generate_id}",
it.slug_filme ?: return@mapNotNull null,
Image.Filme
)
}
if (filme?.isNotEmpty() == true) collection.addAll(filme)
return collection
}
override suspend fun load(url: String): LoadResponse? {
val fixUrl = getProperAnimeLink(url) ?: throw ErrorLoadingException()
val document = app.get(fixUrl).document
val article = document.selectFirst("article.sc-f5d5b250-9") ?: return null
val title = article.selectFirst("h2")?.text() ?: return null
val poster = fixUrlNull(document.select("article.sc-f5d5b250-8 img").attr("src"))
val tags = article.select("div#generos a").map { it.text() }
val year = article.selectFirst("div.sc-f5d5b250-4")?.nextElementSibling()?.text()
?.toIntOrNull()
val description = document.select("div.sinopse").text().trim()
val type = if (fixUrl.contains("/a/")) TvType.Anime else TvType.AnimeMovie
val episodes = mutableListOf<Episode>()
if (type == TvType.Anime) {
for (i in 1..10) {
val dataEpisode = app.get("$episodeUrl/animes/${fixUrl.substringAfterLast("/")}/episodes?page=$i&order=desc")
.parsedSafe<LoadAnime>()?.data?.map {
Episode(
Load(it.anime?.get("slug_serie"), it.n_episodio, "animes").toJson(),
it.titulo_episodio,
episode = it.n_episodio?.toIntOrNull(),
posterUrl = it.anime?.get("slug_serie")?.fixImageUrl(Image.Episode),
description = it.sinopse_episodio
)
}?.reversed() ?: emptyList()
if(dataEpisode.isEmpty()) break else episodes.addAll(dataEpisode)
}
} else {
val dataEpisode = listOf(
Episode(
Load(
document.selectFirst("script:containsData(slug_filme)")?.data()?.let {
Regex("[\"']slug_filme[\"']:[\"'](\\S+?)[\"']").find(it)?.groupValues?.get(1)
} ?: return null, "movie", "movies"
).toJson()
)
)
episodes.addAll(dataEpisode)
}
return newAnimeLoadResponse(title, url, type) {
engName = title
posterUrl = poster
this.year = year
addEpisodes(DubStatus.Subbed, episodes)
plot = description
this.tags = tags
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val load = tryParseJson<Load>(data)
callback.invoke(
ExtractorLink(
this.name,
this.name,
if(load?.type == "movies") {
"$videoUrl/hls/${load.type}/${load.slug_serie}/${load.n_episodio}.mp4/media-1/stream.m3u8"
} else {
"$videoUrl/cf/hls/${load?.type}/${load?.slug_serie}/${load?.n_episodio}.mp4/media-1/stream.m3u8"
},
"$mainUrl/",
Qualities.Unknown.value,
true
)
)
return true
}
private suspend fun getProperAnimeLink(uri: String): String? {
return if (uri.contains("/e/")) {
app.get(uri).document.selectFirst("div.epcontrol2 a[href*=/a/]")?.attr("href")?.let {
fixUrl(it)
}
} else {
uri
}
}
private fun addAnimeSearch(titulo: String, id: String, slug: String, type: Image): AnimeSearchResponse {
return newAnimeSearchResponse(titulo, "$mainUrl/$id", TvType.Anime) {
this.posterUrl = slug.fixImageUrl(type)
}
}
private fun String.fixImageUrl(param: Image): String {
return when (param) {
Image.Episode -> {
"$posterUrl/images/animes/screens/$this/130x74/007.jpg"
}
Image.Anime -> {
"$mainUrl/_next/image?url=$posterUrl/images/animes/capas/130x209/$this.jpg&w=384&q=75"
}
Image.Filme -> {
"$mainUrl/_next/image?url=$posterUrl/images/filmes/capas/130x209/$this.jpg&w=384&q=75"
}
}
}
enum class Image {
Episode,
Anime,
Filme,
}
data class Load(
val slug_serie: String? = null,
val n_episodio: String? = null,
val type: String? = null,
)
data class DataEpisode(
@JsonProperty("id_series_episodios") val id_series_episodios: Int? = null,
@JsonProperty("n_episodio") val n_episodio: String? = null,
@JsonProperty("titulo_episodio") val titulo_episodio: String? = null,
@JsonProperty("sinopse_episodio") val sinopse_episodio: String? = null,
@JsonProperty("generate_id") val generate_id: String? = null,
@JsonProperty("anime") val anime: HashMap<String, String>? = null,
)
data class LoadAnime(
@JsonProperty("data") val data: ArrayList<DataEpisode>? = arrayListOf()
)
data class DataAnime(
@JsonProperty("titulo") val titulo: String? = null,
@JsonProperty("generate_id") val generate_id: String? = null,
@JsonProperty("slug_serie") val slug_serie: String? = null,
@JsonProperty("total_eps_anime") val total_eps_anime: Int? = null,
)
data class DataFilme(
@JsonProperty("nome_filme") val nome_filme: String? = null,
@JsonProperty("generate_id") val generate_id: String? = null,
@JsonProperty("slug_filme") val slug_filme: String? = null,
)
data class SearchAnime(
@JsonProperty("data_anime") val data_anime: ArrayList<DataAnime>? = arrayListOf(),
@JsonProperty("data_filme") val data_filme: ArrayList<DataFilme>? = arrayListOf(),
)
}

View file

@ -0,0 +1,14 @@
package com.hexated
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class AnrollPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(Anroll())
}
}