package com.hexated import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.LoadResponse.Companion.addActors import com.lagradost.cloudstream3.extractors.DoodLaExtractor import com.lagradost.cloudstream3.extractors.Voe import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.M3u8Helper import com.lagradost.cloudstream3.utils.loadExtractor import org.jsoup.nodes.Document import org.jsoup.nodes.Element import java.net.URI open class Aniworld : MainAPI() { override var mainUrl = "https://aniworld.to" override var name = "Aniworld" override val hasMainPage = true override var lang = "de" override val supportedTypes = setOf( TvType.Anime, TvType.AnimeMovie, TvType.OVA ) companion object { fun getType(t: String): TvType { return when { t.contains("Anime Ova") -> TvType.OVA t.contains("Anime Movie") -> TvType.AnimeMovie else -> TvType.Anime } } fun getStatus(t: String): ShowStatus { return when { t.contains("/complete", true) -> ShowStatus.Completed t.contains("/running", true) -> ShowStatus.Ongoing else -> ShowStatus.Completed } } } override suspend fun getMainPage( page: Int, request: MainPageRequest ): HomePageResponse { val document = app.get(mainUrl).document val item = arrayListOf() document.select("div.carousel").map { ele -> val header = ele.selectFirst("h2")?.text() ?: return@map val home = ele.select("div.coverListItem").mapNotNull { it.toSearchResult() } if (home.isNotEmpty()) item.add(HomePageList(header, home)) } return HomePageResponse(item) } override suspend fun search(query: String): List { val json = app.post( "$mainUrl/ajax/search", data = mapOf("keyword" to query), referer = "$mainUrl/search", headers = mapOf( "x-requested-with" to "XMLHttpRequest" ) ) return tryParseJson>(json.text)?.filter { !it.link.contains("episode-") && it.link.contains( "/stream" ) }?.map { newAnimeSearchResponse( it.title?.replace(Regex(""), "") ?: "", fixUrl(it.link), TvType.Anime ) { } } ?: throw ErrorLoadingException() } override suspend fun load(url: String): LoadResponse? { val document = app.get(url).document val title = document.selectFirst("div.series-title span")?.text() ?: return null val poster = fixUrlNull(document.selectFirst("div.seriesCoverBox img")?.attr("data-src")) val tags = document.select("div.genres li a").map { it.text() } val year = document.selectFirst("span[itemprop=startDate] a")?.text()?.toIntOrNull() val description = document.select("p.seri_des").text() val actor = document.select("li:contains(Schauspieler:) ul li a").map { it.select("span").text() } val episodes = mutableListOf() document.select("div#stream > ul:first-child li").map { ele -> val page = ele.selectFirst("a") val epsDocument = app.get(fixUrl(page?.attr("href") ?: return@map)).document epsDocument.select("div#stream > ul:nth-child(4) li").mapNotNull { eps -> episodes.add( Episode( fixUrl(eps.selectFirst("a")?.attr("href") ?: return@mapNotNull null), episode = eps.selectFirst("a")?.text()?.toIntOrNull(), season = page.text().toIntOrNull() ) ) } } return newAnimeLoadResponse( title, url, TvType.Anime ) { engName = title posterUrl = poster this.year = year addEpisodes( DubStatus.Subbed, episodes ) addActors(actor) plot = description this.tags = tags } } override suspend fun loadLinks( data: String, isCasting: Boolean, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ): Boolean { val document = app.get(data).document document.select("div.hosterSiteVideo ul li").map { Triple( it.attr("data-lang-key"), it.attr("data-link-target"), it.select("h4").text() ) }.filter { it.third != "Vidoza" }.apmap { val redirectUrl = app.get(fixUrl(it.second)).url val lang = it.first.getLanguage(document) if (it.third == "VOE") { invokeVoe(redirectUrl, lang, data, callback) } else { loadExtractor(redirectUrl, data, subtitleCallback) { link -> val name = "${link.name} [${lang}]" callback.invoke( ExtractorLink( name, name, link.url, link.referer, link.quality, link.isM3u8, link.headers, link.extractorData ) ) } } } return true } private suspend fun invokeVoe( url: String, lang: String?, referer: String, callback: (ExtractorLink) -> Unit, ) { val name = "Voe [${lang}]" val request = app.get(url, referer = referer) val baseUrl = getBaseUrl(request.url) val res = request.document val script = res.select("script").find { it.data().contains("sources =") }?.data() val link = Regex("[\"']hls[\"']:\\s*[\"'](.*)[\"']").find(script ?: return)?.groupValues?.get(1) M3u8Helper.generateM3u8( name, link ?: return, "$baseUrl/", headers = mapOf("Origin" to "$baseUrl/") ).forEach(callback) } private fun Element.toSearchResult(): AnimeSearchResponse? { val href = fixUrlNull(this.selectFirst("a")?.attr("href")) ?: return null val title = this.selectFirst("h3")?.text() ?: return null val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("data-src")) return newAnimeSearchResponse(title, href, TvType.Anime) { this.posterUrl = posterUrl } } private fun String.getLanguage(document: Document): String? { return document.selectFirst("div.changeLanguageBox img[data-lang-key=$this]")?.attr("title") ?.removePrefix("mit")?.trim() } private fun getBaseUrl(url: String): String { return URI(url).let { "${it.scheme}://${it.host}" } } private data class AnimeSearch( @JsonProperty("link") val link: String, @JsonProperty("title") val title: String? = null, ) } class Dooood : DoodLaExtractor() { override var mainUrl = "https://urochsunloath.com" } class Simpulumlamerop : Voe() { override var mainUrl = "https://simpulumlamerop.com" } class Urochsunloath : Voe() { override var mainUrl = "https://urochsunloath.com" }