package com.hexated import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.loadExtractor import org.jsoup.Jsoup import org.jsoup.nodes.Element class NontonAnimeIDProvider : MainAPI() { override var mainUrl = "https://nontonanimeid.best" override var name = "NontonAnimeID" override val hasQuickSearch = false override val hasMainPage = true override var lang = "id" override val hasDownloadSupport = true override val supportedTypes = setOf( TvType.Anime, TvType.AnimeMovie, TvType.OVA ) companion object { fun getType(t: String): TvType { return when { t.contains("TV",true) -> TvType.Anime t.contains("Movie",true) -> TvType.AnimeMovie else -> TvType.OVA } } fun getStatus(t: String): ShowStatus { return when (t) { "Finished Airing" -> ShowStatus.Completed "Currently Airing" -> ShowStatus.Ongoing else -> ShowStatus.Completed } } } override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { val document = app.get(mainUrl).document val homePageList = ArrayList() document.select("section#postbaru").forEach { block -> val header = block.selectFirst("h2")!!.text().trim() val animes = block.select("article.animeseries").mapNotNull { it.toSearchResult() } if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes)) } document.select("aside#sidebar_right > div.side").forEach { block -> val header = block.selectFirst("h3")!!.ownText().trim() val animes = block.select("ul li.fullwdth").mapNotNull { it.toSearchResultPopular() } if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes)) } return HomePageResponse(homePageList) } private fun Element.toSearchResult(): AnimeSearchResponse? { val href = fixUrl(this.selectFirst("a")!!.attr("href")) val title = this.selectFirst("h3.title")?.text() ?: return null val posterUrl = fixUrl(this.select("img").attr("data-src")) return newAnimeSearchResponse(title, href, TvType.Anime) { this.posterUrl = posterUrl addDubStatus(dubExist = false, subExist = true) } } private fun Element.toSearchResultPopular(): AnimeSearchResponse? { val href = fixUrl(this.selectFirst("a")!!.attr("href")) val title = this.selectFirst("h4")?.text()?.trim() ?: return null val posterUrl = fixUrl(this.select("img").attr("data-src")) return newAnimeSearchResponse(title, href, TvType.Anime) { this.posterUrl = posterUrl addDubStatus(dubExist = false, subExist = true) } } override suspend fun search(query: String): List { val link = "$mainUrl/?s=$query" val document = app.get(link).document return document.select(".result > ul > li").mapNotNull { val title = it.selectFirst("h2")!!.text().trim() val poster = it.selectFirst("img")!!.attr("src") val tvType = getType( it.selectFirst(".boxinfores > span.typeseries")!!.text().toString() ) val href = fixUrl(it.selectFirst("a")!!.attr("href")) newAnimeSearchResponse(title, href, tvType) { this.posterUrl = poster addDubStatus(dubExist = false, subExist = true) } } } override suspend fun load(url: String): LoadResponse? { val fixUrl = if (url.contains("/anime/")) { url } else { app.get(url).document.selectFirst("div.nvs.nvsc a")?.attr("href") } val document = app.get(fixUrl ?: return null).document val title = document.selectFirst("h1.entry-title.cs")!!.text().removeSurrounding("Nonton Anime", "Sub Indo").trim() val poster = document.selectFirst(".poster > img")?.attr("data-src") val tags = document.select(".tagline > a").map { it.text() } val year = Regex("\\d, (\\d*)").find( document.select(".bottomtitle > span:nth-child(5)").text() )?.groupValues?.get(1)?.toIntOrNull() val status = getStatus( document.select("span.statusseries").text().trim() ) val type = document.select("span.typeseries").text().trim().lowercase() val rating = document.select("span.nilaiseries").text().trim().toIntOrNull() val description = document.select(".entry-content.seriesdesc > p").text().trim() val trailer = document.selectFirst("a.trailerbutton")?.attr("href") val (malId, anilistId, image, cover) = getTracker(title, type, year) val episodes = if (document.select("button.buttfilter").isNotEmpty()) { val id = document.select("input[name=series_id]").attr("value") val numEp = document.selectFirst(".latestepisode > a")?.text()?.replace(Regex("\\D"), "") .toString() Jsoup.parse( app.post( url = "$mainUrl/wp-admin/admin-ajax.php", data = mapOf( "misha_number_of_results" to numEp, "misha_order_by" to "date-DESC", "action" to "mishafilter", "series_id" to id ) ).parsed().content ).select("li").map { val episode = Regex("Episode\\s?(\\d+)").find( it.selectFirst("a")?.text().toString() )?.groupValues?.getOrNull(0) ?: it.selectFirst("a")?.text() val link = fixUrl(it.selectFirst("a")!!.attr("href")) Episode(link, episode = episode?.toIntOrNull()) }.reversed() } else { document.select("ul.misha_posts_wrap2 > li").map { val episode = Regex("Episode\\s?(\\d+)").find( it.selectFirst("a")?.text().toString() )?.groupValues?.getOrNull(0) ?: it.selectFirst("a")?.text() val link = it.select("a").attr("href") Episode(link, episode = episode?.toIntOrNull()) }.reversed() } val recommendations = document.select(".result > li").mapNotNull { val epHref = it.selectFirst("a")!!.attr("href") val epTitle = it.selectFirst("h3")!!.text() val epPoster = it.select(".top > img").attr("data-src") newAnimeSearchResponse(epTitle, epHref, TvType.Anime) { this.posterUrl = epPoster addDubStatus(dubExist = false, subExist = true) } } return newAnimeLoadResponse(title, url, getType(type)) { engName = title posterUrl = image ?: poster backgroundPosterUrl = cover ?: image ?: poster this.year = year addEpisodes(DubStatus.Subbed, episodes) showStatus = status this.rating = rating plot = description addMalId(malId) addAniListId(anilistId?.toIntOrNull()) addTrailer(trailer) this.tags = tags this.recommendations = recommendations } } override suspend fun loadLinks( data: String, isCasting: Boolean, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ): Boolean { val document = app.get(data).document val sources = ArrayList() document.select(".container1 > ul > li:not(.boxtab)").apmap { val dataPost = it.attr("data-post") val dataNume = it.attr("data-nume") val dataType = it.attr("data-type") val iframe = app.post( url = "$mainUrl/wp-admin/admin-ajax.php", data = mapOf( "action" to "player_ajax", "post" to dataPost, "nume" to dataNume, "type" to dataType ), referer = data, headers = mapOf("X-Requested-With" to "XMLHttpRequest") ).document.select("iframe").attr("src") sources.add(fixUrl(iframe)) } sources.apmap { loadExtractor(it, "$mainUrl/", subtitleCallback, callback) } return true } private suspend fun getTracker(title: String?, type: String?, year: Int?): Tracker { val res = app.get("https://api.consumet.org/meta/anilist/$title") .parsedSafe()?.results?.find { media -> (media.title?.english.equals(title, true) || media.title?.romaji.equals( title, true )) || (media.type.equals(type, true) && media.releaseDate == year) } return Tracker(res?.malId, res?.aniId, res?.image, res?.cover) } data class Tracker( val malId: Int? = null, val aniId: String? = null, val image: String? = null, val cover: String? = null, ) data class Title( @JsonProperty("romaji") val romaji: String? = null, @JsonProperty("english") val english: String? = null, ) data class Results( @JsonProperty("id") val aniId: String? = null, @JsonProperty("malId") val malId: Int? = null, @JsonProperty("title") val title: Title? = null, @JsonProperty("releaseDate") val releaseDate: Int? = null, @JsonProperty("type") val type: String? = null, @JsonProperty("image") val image: String? = null, @JsonProperty("cover") val cover: String? = null, ) data class AniSearch( @JsonProperty("results") val results: java.util.ArrayList? = arrayListOf(), ) private data class EpResponse( @JsonProperty("posts") val posts: String?, @JsonProperty("max_page") val max_page: Int?, @JsonProperty("found_posts") val found_posts: Int?, @JsonProperty("content") val content: String ) }