forked from recloudstream/cloudstream
Added AnimeWorld provider (#756)
This commit is contained in:
parent
705901f50d
commit
306af16883
2 changed files with 202 additions and 0 deletions
|
@ -87,6 +87,7 @@ object APIHolder {
|
||||||
AkwamProvider(),
|
AkwamProvider(),
|
||||||
AnimePaheProvider(),
|
AnimePaheProvider(),
|
||||||
NineAnimeProvider(),
|
NineAnimeProvider(),
|
||||||
|
AnimeWorldProvider(),
|
||||||
|
|
||||||
CrossTmdbProvider(),
|
CrossTmdbProvider(),
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,201 @@
|
||||||
|
package com.lagradost.cloudstream3.animeproviders
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
import org.json.JSONObject
|
||||||
|
import org.jsoup.nodes.Element
|
||||||
|
import com.lagradost.cloudstream3.*
|
||||||
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
|
import com.lagradost.cloudstream3.utils.Qualities
|
||||||
|
|
||||||
|
class AnimeWorldProvider : MainAPI() {
|
||||||
|
override val mainUrl = "https://www.animeworld.tv"
|
||||||
|
override val name = "AnimeWorld"
|
||||||
|
override val lang = "it"
|
||||||
|
override val hasMainPage = true
|
||||||
|
|
||||||
|
override val supportedTypes = setOf(
|
||||||
|
TvType.Anime,
|
||||||
|
TvType.AnimeMovie,
|
||||||
|
TvType.OVA
|
||||||
|
)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun getType(t: String?): TvType {
|
||||||
|
return when (t?.lowercase()) {
|
||||||
|
"movie" -> TvType.AnimeMovie
|
||||||
|
"ova" -> TvType.OVA
|
||||||
|
else -> TvType.Anime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun getStatus(t: String?): ShowStatus? {
|
||||||
|
return when (t?.lowercase()) {
|
||||||
|
"finito" -> ShowStatus.Completed
|
||||||
|
"in corso" -> ShowStatus.Ongoing
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Element.toSearchResult(showEpisode: Boolean = true): AnimeSearchResponse {
|
||||||
|
fun String.parseHref(): String {
|
||||||
|
val h = this.split('.').toMutableList()
|
||||||
|
h[1] = h[1].substringBeforeLast('/')
|
||||||
|
return h.joinToString(".")
|
||||||
|
}
|
||||||
|
|
||||||
|
val title = this.select("a.name").text().removeSuffix(" (ITA)")
|
||||||
|
val otherTitle = this.select("a.name").attr("data-jtitle").removeSuffix(" (ITA)")
|
||||||
|
val url = fixUrl(this.select("a.name").attr("href").parseHref())
|
||||||
|
val poster = this.select("a.poster img").attr("src")
|
||||||
|
|
||||||
|
val statusElement = this.select("div.status") // .first()
|
||||||
|
val dub = statusElement.select(".dub").isNotEmpty()
|
||||||
|
val dubStatus = if (dub) EnumSet.of(DubStatus.Dubbed) else EnumSet.of(DubStatus.Subbed)
|
||||||
|
val episode = statusElement.select(".ep").text().split(' ').last().toIntOrNull()
|
||||||
|
val type = when {
|
||||||
|
statusElement.select(".movie").isNotEmpty() -> TvType.AnimeMovie
|
||||||
|
statusElement.select(".ova").isNotEmpty() -> TvType.OVA
|
||||||
|
else -> TvType.Anime
|
||||||
|
}
|
||||||
|
|
||||||
|
return AnimeSearchResponse(
|
||||||
|
title,
|
||||||
|
url,
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
poster,
|
||||||
|
dubStatus = dubStatus,
|
||||||
|
otherName = if (otherTitle != title) otherTitle else null,
|
||||||
|
dubEpisodes = if (showEpisode && type != TvType.AnimeMovie && dub) episode else null,
|
||||||
|
subEpisodes = if (showEpisode && type != TvType.AnimeMovie && !dub) episode else null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getMainPage(): HomePageResponse {
|
||||||
|
val document = app.get(mainUrl).document
|
||||||
|
val list = ArrayList<HomePageList>()
|
||||||
|
|
||||||
|
val widget = document.select(".widget.hotnew")
|
||||||
|
widget.select(".tabs [data-name=\"sub\"], .tabs [data-name=\"dub\"]").forEach { tab ->
|
||||||
|
val tabId = tab.attr("data-name")
|
||||||
|
val tabName = tab.text().removeSuffix("-ITA")
|
||||||
|
val animeList = widget.select("[data-name=\"$tabId\"] .film-list .item").map {
|
||||||
|
it.toSearchResult()
|
||||||
|
}
|
||||||
|
list.add(HomePageList(tabName, animeList))
|
||||||
|
}
|
||||||
|
widget.select(".tabs [data-name=\"trending\"]").forEach { tab ->
|
||||||
|
val tabId = tab.attr("data-name")
|
||||||
|
val tabName = tab.text()
|
||||||
|
val animeList = widget.select("[data-name=\"$tabId\"] .film-list .item").map {
|
||||||
|
it.toSearchResult(showEpisode = false)
|
||||||
|
}.distinctBy { it.url }
|
||||||
|
list.add(HomePageList(tabName, animeList))
|
||||||
|
}
|
||||||
|
return HomePageResponse(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun search(query: String): List<SearchResponse> {
|
||||||
|
val document = app.get("$mainUrl/search?keyword=$query").document
|
||||||
|
return document.select(".film-list > .item").map {
|
||||||
|
it.toSearchResult(showEpisode = false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun load(url: String): LoadResponse {
|
||||||
|
fun String.parseDuration(): Int? {
|
||||||
|
val arr = this.split(" e ")
|
||||||
|
return if (arr.size == 1)
|
||||||
|
arr[0].split(' ')[0].toIntOrNull()
|
||||||
|
else
|
||||||
|
arr[1].split(' ')[0].toIntOrNull()?.let {
|
||||||
|
arr[0].removeSuffix("h").toIntOrNull()?.times(60)!!.plus(it) }
|
||||||
|
}
|
||||||
|
val document = app.get(url).document
|
||||||
|
|
||||||
|
val widget = document.select("div.widget.info")
|
||||||
|
val title = widget.select(".info .title").text().removeSuffix(" (ITA)")
|
||||||
|
val otherTitle = widget.select(".info .title").attr("data-jtitle").removeSuffix(" (ITA)")
|
||||||
|
val description = widget.select(".desc .long").first()?.text() ?: widget.select(".desc").text()
|
||||||
|
val poster = document.select(".thumb img").attr("src")
|
||||||
|
|
||||||
|
val type: TvType = getType(widget.select("dd").first()?.text())
|
||||||
|
val genres = widget.select(".meta").select("a[href*=\"/genre/\"]").map { it.text() }
|
||||||
|
val rating: Int? = widget.select("#average-vote").text().toFloatOrNull()?.times(1000)?.toInt()
|
||||||
|
|
||||||
|
val trailerUrl = document.select(".trailer[data-url]").attr("data-url")
|
||||||
|
val malId = document.select("#mal-button").attr("href")
|
||||||
|
.split('/').last().toIntOrNull()
|
||||||
|
val anlId = document.select("#anilist-button").attr("href")
|
||||||
|
.split('/').last().toIntOrNull()
|
||||||
|
|
||||||
|
var dub = false
|
||||||
|
var year: Int? = null
|
||||||
|
var status: ShowStatus? = null
|
||||||
|
var duration: Int? = null
|
||||||
|
|
||||||
|
for (meta in document.select(".meta dt, .meta dd")) {
|
||||||
|
val text = meta.text()
|
||||||
|
if (text.contains("Audio"))
|
||||||
|
dub = meta.nextElementSibling()?.text() == "Italiano"
|
||||||
|
else if (year == null && text.contains("Data"))
|
||||||
|
year = meta.nextElementSibling()?.text()?.split(' ')?.last()?.toIntOrNull()
|
||||||
|
else if (status == null && text.contains("Stato"))
|
||||||
|
status = getStatus(meta.nextElementSibling()?.text())
|
||||||
|
else if (status == null && text.contains("Durata"))
|
||||||
|
duration = meta.nextElementSibling()?.text()?.parseDuration()
|
||||||
|
}
|
||||||
|
|
||||||
|
val servers = document.select(".widget.servers")
|
||||||
|
val episodes = servers.select(".server[data-name=\"9\"] .episode").map {
|
||||||
|
val id = it.select("a").attr("data-id")
|
||||||
|
val number = it.select("a").attr("data-episode-num").toIntOrNull()
|
||||||
|
AnimeEpisode(
|
||||||
|
fixUrl("$mainUrl/api/episode/info?id=$id"),
|
||||||
|
episode = number
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val recommendations = document.select(".film-list.interesting .item").map {
|
||||||
|
it.toSearchResult(showEpisode = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newAnimeLoadResponse(title, url, type) {
|
||||||
|
engName = title
|
||||||
|
japName = otherTitle
|
||||||
|
posterUrl = poster
|
||||||
|
this.year = year
|
||||||
|
addEpisodes(if (dub) DubStatus.Dubbed else DubStatus.Subbed, episodes)
|
||||||
|
showStatus = status
|
||||||
|
plot = description
|
||||||
|
tags = genres
|
||||||
|
this.malId = malId
|
||||||
|
this.anilistId = anlId
|
||||||
|
this.rating = rating
|
||||||
|
this.duration = duration
|
||||||
|
this.trailerUrl = trailerUrl
|
||||||
|
this.recommendations = recommendations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun loadLinks(
|
||||||
|
data: String,
|
||||||
|
isCasting: Boolean,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
): Boolean {
|
||||||
|
val url = JSONObject(
|
||||||
|
app.get(data).text
|
||||||
|
).getString("grabber")
|
||||||
|
callback.invoke(
|
||||||
|
ExtractorLink(
|
||||||
|
name,
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
referer = mainUrl,
|
||||||
|
quality = Qualities.Unknown.value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue