mirror of
				https://github.com/recloudstream/cloudstream.git
				synced 2024-08-15 01:53:11 +00:00 
			
		
		
		
	Added 3 Indonesian Provider (#1045)
* Add files via upload * Add files via upload * Update MainAPI.kt * - Add NontonAnimeID and Kuramanime - refactoring code * add GomunimeProvider, add some source, fix minor code * add sources * add KuronimeProvider, add sources * small fix, (ready to merge..)
This commit is contained in:
		
							parent
							
								
									f3e73b1e3c
								
							
						
					
					
						commit
						7cdb902f80
					
				
					 8 changed files with 1620 additions and 0 deletions
				
			
		| 
						 | 
				
			
			@ -76,6 +76,7 @@ object APIHolder {
 | 
			
		|||
            TwoEmbedProvider(),
 | 
			
		||||
            DramaSeeProvider(),
 | 
			
		||||
            WatchAsianProvider(),
 | 
			
		||||
	        DramaidProvider(),
 | 
			
		||||
            KdramaHoodProvider(),
 | 
			
		||||
            AkwamProvider(),
 | 
			
		||||
            MyCimaProvider(),
 | 
			
		||||
| 
						 | 
				
			
			@ -111,6 +112,12 @@ object APIHolder {
 | 
			
		|||
            DubbedAnimeProvider(),
 | 
			
		||||
            MonoschinosProvider(),
 | 
			
		||||
            KawaiifuProvider(), // disabled due to cloudflare
 | 
			
		||||
	        NeonimeProvider(),
 | 
			
		||||
            KuramanimeProvider(),
 | 
			
		||||
            OploverzProvider(),
 | 
			
		||||
            GomunimeProvider(),
 | 
			
		||||
            NontonAnimeIDProvider(),
 | 
			
		||||
            KuronimeProvider(),
 | 
			
		||||
            //MultiAnimeProvider(),
 | 
			
		||||
	        NginxProvider(),
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,247 @@
 | 
			
		|||
package com.lagradost.cloudstream3.animeproviders
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.annotation.JsonProperty
 | 
			
		||||
import com.lagradost.cloudstream3.*
 | 
			
		||||
import org.jsoup.Jsoup
 | 
			
		||||
import org.jsoup.nodes.Element
 | 
			
		||||
import java.util.*
 | 
			
		||||
import com.fasterxml.jackson.module.kotlin.readValue
 | 
			
		||||
import com.lagradost.cloudstream3.movieproviders.SflixProvider
 | 
			
		||||
import com.lagradost.cloudstream3.movieproviders.SflixProvider.Companion.extractRabbitStream
 | 
			
		||||
import com.lagradost.cloudstream3.movieproviders.SflixProvider.Companion.toExtractorLink
 | 
			
		||||
import com.lagradost.cloudstream3.mvvm.logError
 | 
			
		||||
import com.lagradost.cloudstream3.mvvm.safeApiCall
 | 
			
		||||
import com.lagradost.cloudstream3.utils.*
 | 
			
		||||
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
 | 
			
		||||
import com.lagradost.nicehttp.Requests.Companion.await
 | 
			
		||||
import kotlinx.coroutines.runBlocking
 | 
			
		||||
import okhttp3.Interceptor
 | 
			
		||||
import java.net.URI
 | 
			
		||||
 | 
			
		||||
class GomunimeProvider : MainAPI() {
 | 
			
		||||
    override var mainUrl = "https://185.231.223.76"
 | 
			
		||||
    override var name = "Gomunime"
 | 
			
		||||
    override val hasQuickSearch = false
 | 
			
		||||
    override val hasMainPage = true
 | 
			
		||||
    override val lang = "id"
 | 
			
		||||
    override val hasDownloadSupport = true
 | 
			
		||||
 | 
			
		||||
    override val supportedTypes = setOf(
 | 
			
		||||
        TvType.Anime,
 | 
			
		||||
        TvType.AnimeMovie,
 | 
			
		||||
        TvType.OVA
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        fun getType(t: String): TvType {
 | 
			
		||||
            return if (t.contains("OVA") || t.contains("Special")) TvType.OVA
 | 
			
		||||
            else if (t.contains("Movie")) TvType.AnimeMovie
 | 
			
		||||
            else TvType.Anime
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun getStatus(t: String): ShowStatus {
 | 
			
		||||
            return when (t) {
 | 
			
		||||
                "Completed" -> ShowStatus.Completed
 | 
			
		||||
                "Ongoing" -> ShowStatus.Ongoing
 | 
			
		||||
                else -> ShowStatus.Completed
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private data class Response(
 | 
			
		||||
        @JsonProperty("status") val status: Boolean,
 | 
			
		||||
        @JsonProperty("html") val html: String
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    override suspend fun getMainPage(): HomePageResponse {
 | 
			
		||||
        val urls = listOf(
 | 
			
		||||
            Pair("e", "Episode Baru"),
 | 
			
		||||
            Pair("c", "Completed"),
 | 
			
		||||
            Pair("la", "Live Action"),
 | 
			
		||||
            Pair("t", "Trending"),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        val items = ArrayList<HomePageList>()
 | 
			
		||||
 | 
			
		||||
        for ((payload, name) in urls) {
 | 
			
		||||
            try {
 | 
			
		||||
                val home = Jsoup.parse(
 | 
			
		||||
                    parseJson<Response>(
 | 
			
		||||
                        app.post(
 | 
			
		||||
                            url = "$mainUrl/wp-admin/admin-ajax.php/wp-admin/admin-ajax.php",
 | 
			
		||||
                            headers = mapOf("Referer" to mainUrl),
 | 
			
		||||
                            data = mapOf("action" to "home_ajax", "fungsi" to payload, "pag" to "1")
 | 
			
		||||
                        ).text
 | 
			
		||||
                    ).html
 | 
			
		||||
                ).select("li").map {
 | 
			
		||||
                    val title = it.selectFirst("a.name")!!.text().trim()
 | 
			
		||||
                    val href = getProperAnimeLink(it.selectFirst("a")!!.attr("href"))
 | 
			
		||||
                    val posterUrl = it.selectFirst("img")!!.attr("src")
 | 
			
		||||
                    val type = getType(it.selectFirst(".taglist > span")!!.text().trim())
 | 
			
		||||
                    val epNum = it.select(".tag.ep").text().replace(Regex("[^0-9]"), "").trim()
 | 
			
		||||
                        .toIntOrNull()
 | 
			
		||||
                    newAnimeSearchResponse(title, href, type) {
 | 
			
		||||
                        this.posterUrl = posterUrl
 | 
			
		||||
                        addDubStatus(dubExist = false, subExist = true, subEpisodes = epNum)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                items.add(HomePageList(name, home))
 | 
			
		||||
            } catch (e: Exception) {
 | 
			
		||||
                logError(e)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (items.size <= 0) throw ErrorLoadingException()
 | 
			
		||||
        return HomePageResponse(items)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getProperAnimeLink(uri: String): String {
 | 
			
		||||
        return if (uri.contains("-episode")) {
 | 
			
		||||
            val href =
 | 
			
		||||
                "$mainUrl/anime/" + Regex("\\w\\d/(.*)-episode.*").find(uri)?.groupValues?.get(1)
 | 
			
		||||
                    .toString()
 | 
			
		||||
            when {
 | 
			
		||||
                href.contains("pokemon") -> href.replace(Regex("-[0-9]+"), "")
 | 
			
		||||
                else -> href
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            uri
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun search(query: String): List<SearchResponse> {
 | 
			
		||||
        val link = "$mainUrl/?s=$query"
 | 
			
		||||
        val document = app.get(link).document
 | 
			
		||||
 | 
			
		||||
        return document.select(".anime-list > li").map {
 | 
			
		||||
            val title = it.selectFirst("a.name")!!.text()
 | 
			
		||||
            val poster = it.selectFirst("img")!!.attr("src")
 | 
			
		||||
            val tvType = getType(it.selectFirst(".taglist > span")?.text().toString())
 | 
			
		||||
            val href = fixUrl(it.selectFirst("a.name")!!.attr("href"))
 | 
			
		||||
 | 
			
		||||
            newAnimeSearchResponse(title, href, tvType) {
 | 
			
		||||
                this.posterUrl = poster
 | 
			
		||||
                addDubStatus(dubExist = false, subExist = true)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private data class EpisodeElement(
 | 
			
		||||
        @JsonProperty("data-index") val dataIndex: Long?,
 | 
			
		||||
        @JsonProperty("ep-num") val epNum: String?,
 | 
			
		||||
        @JsonProperty("ep-title") val epTitle: String?,
 | 
			
		||||
        @JsonProperty("ep-link") val epLink: String,
 | 
			
		||||
        @JsonProperty("ep-date") val epDate: String?
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    override suspend fun load(url: String): LoadResponse {
 | 
			
		||||
        val document = app.get(url).document
 | 
			
		||||
 | 
			
		||||
        val title = document.selectFirst(".entry-title")?.text().toString()
 | 
			
		||||
        val poster = document.selectFirst(".thumbposter > img")?.attr("data-lazy-src")
 | 
			
		||||
        val tags = document.select(".genxed > a").map { it.text() }
 | 
			
		||||
 | 
			
		||||
        val year = Regex("\\d, ([0-9]*)").find(
 | 
			
		||||
            document.select("time[itemprop = datePublished]").text()
 | 
			
		||||
        )?.groupValues?.get(1)?.toIntOrNull()
 | 
			
		||||
        val status = getStatus(document.selectFirst(".spe > span")!!.ownText())
 | 
			
		||||
        val description = document.select("div[itemprop = description] > p").text()
 | 
			
		||||
 | 
			
		||||
        val episodes = parseJson<List<EpisodeElement>>(
 | 
			
		||||
            Regex("var episodelist = (\\[.*])").find(
 | 
			
		||||
                document.select(".bixbox.bxcl.epcheck > script").toString().trim()
 | 
			
		||||
            )?.groupValues?.get(1).toString().replace(Regex("""\\"""), "").trim()
 | 
			
		||||
        ).map {
 | 
			
		||||
            val name = it.epTitle
 | 
			
		||||
            val link = it.epLink
 | 
			
		||||
            Episode(link, name)
 | 
			
		||||
        }.reversed()
 | 
			
		||||
 | 
			
		||||
        return newAnimeLoadResponse(title, url, TvType.Anime) {
 | 
			
		||||
            engName = title
 | 
			
		||||
            posterUrl = poster
 | 
			
		||||
            this.year = year
 | 
			
		||||
            addEpisodes(DubStatus.Subbed, episodes)
 | 
			
		||||
            showStatus = status
 | 
			
		||||
            plot = description
 | 
			
		||||
            this.tags = tags
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun invokeSource(
 | 
			
		||||
        source: String,
 | 
			
		||||
        sourceCallback: (ExtractorLink) -> Unit
 | 
			
		||||
    ) {
 | 
			
		||||
        M3u8Helper.generateM3u8(
 | 
			
		||||
            source = this.name,
 | 
			
		||||
            streamUrl = source,
 | 
			
		||||
            referer = "$mainUrl/",
 | 
			
		||||
            name = this.name,
 | 
			
		||||
        ).forEach(sourceCallback)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    data class MobiSource(
 | 
			
		||||
        @JsonProperty("file") val file: String,
 | 
			
		||||
        @JsonProperty("label") val label: String,
 | 
			
		||||
        @JsonProperty("type") val type: String
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    override suspend fun loadLinks(
 | 
			
		||||
        data: String,
 | 
			
		||||
        isCasting: Boolean,
 | 
			
		||||
        subtitleCallback: (SubtitleFile) -> Unit,
 | 
			
		||||
        callback: (ExtractorLink) -> Unit
 | 
			
		||||
    ): Boolean {
 | 
			
		||||
        val document = app.get(data).document
 | 
			
		||||
 | 
			
		||||
        val scriptData = document.select("aside.sidebar > script").dataNodes().toString()
 | 
			
		||||
        val key = scriptData.substringAfter("var a_ray = '").substringBefore("';")
 | 
			
		||||
        val title = scriptData.substringAfter("var judul_postingan = \"").substringBefore("\";")
 | 
			
		||||
 | 
			
		||||
        val sources: List<Pair<String, String>> = app.post(
 | 
			
		||||
            url = "https://path.gomuni.me/app/vapi.php",
 | 
			
		||||
            data = mapOf("data" to key, "judul" to title, "func" to "mirror")
 | 
			
		||||
        ).document.select("div.gomunime-server-mirror").map {
 | 
			
		||||
            Pair(
 | 
			
		||||
                it.attr("data-vhash"),
 | 
			
		||||
                it.attr("data-type")
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        sources.apmap {
 | 
			
		||||
            safeApiCall {
 | 
			
		||||
                when {
 | 
			
		||||
                    it.second.contains("frame") -> {
 | 
			
		||||
                        loadExtractor(it.first, data, callback)
 | 
			
		||||
                    }
 | 
			
		||||
                    it.second.contains("hls") -> {
 | 
			
		||||
                        app.post(
 | 
			
		||||
                            url = "https://path.gomuni.me/app/vapi.php",
 | 
			
		||||
                            data = mapOf("fid" to it.first, "func" to "hls")
 | 
			
		||||
                        ).text.let { link ->
 | 
			
		||||
                            invokeSource(link, callback)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else -> {
 | 
			
		||||
                        app.post(
 | 
			
		||||
                            url = "https://path.gomuni.me/app/vapi.php",
 | 
			
		||||
                            data = mapOf("data" to it.first, "func" to "blogs")
 | 
			
		||||
                        ).parsed<List<MobiSource>>().map {
 | 
			
		||||
                            callback(
 | 
			
		||||
                                ExtractorLink(
 | 
			
		||||
                                    source = name,
 | 
			
		||||
                                    name = "Mobi SD",
 | 
			
		||||
                                    url = it.file,
 | 
			
		||||
                                    referer = "$mainUrl/",
 | 
			
		||||
                                    quality = Qualities.P360.value
 | 
			
		||||
                                )
 | 
			
		||||
                            )
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,211 @@
 | 
			
		|||
package com.lagradost.cloudstream3.animeproviders
 | 
			
		||||
 | 
			
		||||
import com.lagradost.cloudstream3.*
 | 
			
		||||
import com.lagradost.cloudstream3.network.DdosGuardKiller
 | 
			
		||||
import com.lagradost.cloudstream3.utils.ExtractorLink
 | 
			
		||||
import org.jsoup.Jsoup
 | 
			
		||||
import org.jsoup.nodes.Element
 | 
			
		||||
import java.util.*
 | 
			
		||||
 | 
			
		||||
class KuramanimeProvider : MainAPI() {
 | 
			
		||||
    override var mainUrl = "https://kuramanime.com"
 | 
			
		||||
    override var name = "Kuramanime"
 | 
			
		||||
    override val hasQuickSearch = false
 | 
			
		||||
    override val hasMainPage = true
 | 
			
		||||
    override val lang = "id"
 | 
			
		||||
    override val hasDownloadSupport = true
 | 
			
		||||
 | 
			
		||||
    override val supportedTypes = setOf(
 | 
			
		||||
        TvType.Anime,
 | 
			
		||||
        TvType.AnimeMovie,
 | 
			
		||||
        TvType.OVA
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        fun getType(t: String): TvType {
 | 
			
		||||
            return if (t.contains("OVA") || t.contains("Special")) TvType.OVA
 | 
			
		||||
            else if (t.contains("Movie")) TvType.AnimeMovie
 | 
			
		||||
            else TvType.Anime
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun getStatus(t: String): ShowStatus {
 | 
			
		||||
            return when (t) {
 | 
			
		||||
                "Selesai Tayang" -> ShowStatus.Completed
 | 
			
		||||
                "Sedang Tayang" -> ShowStatus.Ongoing
 | 
			
		||||
                else -> ShowStatus.Completed
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun getMainPage(): HomePageResponse {
 | 
			
		||||
        val document = app.get(mainUrl).document
 | 
			
		||||
 | 
			
		||||
        val homePageList = ArrayList<HomePageList>()
 | 
			
		||||
 | 
			
		||||
        document.select("div[class*=__product]").forEach { block ->
 | 
			
		||||
            val header = block.select(".section-title > h4").text()
 | 
			
		||||
            val animes = block.select("div.col-lg-4.col-md-6.col-sm-6").mapNotNull {
 | 
			
		||||
                it.toSearchResult()
 | 
			
		||||
            }
 | 
			
		||||
            if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        document.select("#topAnimesSection").forEach { block ->
 | 
			
		||||
            val header = block.previousElementSibling()!!.select("h5").text().trim()
 | 
			
		||||
            val animes = block.select("a").mapNotNull {
 | 
			
		||||
                it.toSearchResultView()
 | 
			
		||||
            }
 | 
			
		||||
            if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        document.select("#latestCommentSection").forEach { block ->
 | 
			
		||||
            val header = block.previousElementSibling()!!.select("h5").text().trim()
 | 
			
		||||
            val animes = block.select(".product__sidebar__comment__item").mapNotNull {
 | 
			
		||||
                it.toSearchResultComment()
 | 
			
		||||
            }
 | 
			
		||||
            if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return HomePageResponse(homePageList)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getProperAnimeLink(uri: String): String {
 | 
			
		||||
        return if (uri.contains("/episode")) {
 | 
			
		||||
            Regex("(.*)/episode/.+").find(uri)?.groupValues?.get(1).toString() + "/"
 | 
			
		||||
        } else {
 | 
			
		||||
            uri
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun Element.toSearchResult(): SearchResponse {
 | 
			
		||||
        val href = getProperAnimeLink(fixUrl(this.selectFirst("a")!!.attr("href")))
 | 
			
		||||
        val title = this.select(".product__item__text > h5 > a").text()
 | 
			
		||||
        val posterUrl = fixUrl(this.select(".product__item__pic.set-bg").attr("data-setbg"))
 | 
			
		||||
        val type = getType(this.selectFirst(".product__item__text > ul > li")!!.text())
 | 
			
		||||
 | 
			
		||||
        return newAnimeSearchResponse(title, href, type) {
 | 
			
		||||
            this.posterUrl = posterUrl
 | 
			
		||||
            addDubStatus(dubExist = false, subExist = true)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun Element.toSearchResultView(): SearchResponse {
 | 
			
		||||
        val href = getProperAnimeLink(fixUrl(this.attr("href")))
 | 
			
		||||
        val title = this.selectFirst("h5")!!.text().trim()
 | 
			
		||||
        val posterUrl =
 | 
			
		||||
            fixUrl(this.select(".product__sidebar__view__item.set-bg").attr("data-setbg"))
 | 
			
		||||
 | 
			
		||||
        return newAnimeSearchResponse(title, href, TvType.Anime) {
 | 
			
		||||
            this.posterUrl = posterUrl
 | 
			
		||||
            addDubStatus(dubExist = false, subExist = true)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun Element.toSearchResultComment(): SearchResponse {
 | 
			
		||||
        val href = getProperAnimeLink(fixUrl(this.selectFirst("a")!!.attr("href")))
 | 
			
		||||
        val title = this.selectFirst("h5")!!.text()
 | 
			
		||||
        val posterUrl = fixUrl(this.select("img").attr("src"))
 | 
			
		||||
        val type = getType(this.selectFirst("ul > li")!!.text())
 | 
			
		||||
 | 
			
		||||
        return newAnimeSearchResponse(title, href, type) {
 | 
			
		||||
            this.posterUrl = posterUrl
 | 
			
		||||
            addDubStatus(dubExist = false, subExist = true)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun search(query: String): List<SearchResponse> {
 | 
			
		||||
        val link = "$mainUrl/anime?search=$query&order_by=oldest"
 | 
			
		||||
        val document = app.get(link).document
 | 
			
		||||
 | 
			
		||||
        return document.select(".product__item").mapNotNull {
 | 
			
		||||
            val title = it.selectFirst("div.product__item__text > h5")!!.text().trim()
 | 
			
		||||
            val poster = it.selectFirst("a > div")!!.attr("data-setbg")
 | 
			
		||||
            val tvType =
 | 
			
		||||
                getType(it.selectFirst(".product__item__text > ul > li")!!.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 document = app.get(url).document
 | 
			
		||||
 | 
			
		||||
        val title = document.selectFirst(".anime__details__title > h3")!!.text().trim()
 | 
			
		||||
        val poster = document.selectFirst(".anime__details__pic")?.attr("data-setbg")
 | 
			
		||||
        val tags =
 | 
			
		||||
            document.select("div.anime__details__widget > div > div:nth-child(2) > ul > li:nth-child(1)")
 | 
			
		||||
                .text().trim().replace("Genre: ", "").split(", ")
 | 
			
		||||
 | 
			
		||||
        val year = Regex("[^0-9]").replace(
 | 
			
		||||
            document.select("div.anime__details__widget > div > div:nth-child(1) > ul > li:nth-child(5)")
 | 
			
		||||
                .text().trim().replace("Musim: ", ""), ""
 | 
			
		||||
        ).toIntOrNull()
 | 
			
		||||
        val status = getStatus(
 | 
			
		||||
            document.select("div.anime__details__widget > div > div:nth-child(1) > ul > li:nth-child(3)")
 | 
			
		||||
                .text().trim().replace("Status: ", "")
 | 
			
		||||
        )
 | 
			
		||||
        val description = document.select(".anime__details__text > p").text().trim()
 | 
			
		||||
 | 
			
		||||
        val episodes =
 | 
			
		||||
            Jsoup.parse(document.select("#episodeLists").attr("data-content")).select("a").map {
 | 
			
		||||
                val name = it.text().trim()
 | 
			
		||||
                val link = it.attr("href")
 | 
			
		||||
                Episode(link, name)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        val recommendations = document.select("div#randomList > a").mapNotNull {
 | 
			
		||||
            val epHref = it.attr("href")
 | 
			
		||||
            val epTitle = it.select("h5.sidebar-title-h5.px-2.py-2").text()
 | 
			
		||||
            val epPoster = it.select(".product__sidebar__view__item.set-bg").attr("data-setbg")
 | 
			
		||||
 | 
			
		||||
            newAnimeSearchResponse(epTitle, epHref, TvType.Anime) {
 | 
			
		||||
                this.posterUrl = epPoster
 | 
			
		||||
                addDubStatus(dubExist = false, subExist = true)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return newAnimeLoadResponse(title, url, TvType.Anime) {
 | 
			
		||||
            engName = title
 | 
			
		||||
            posterUrl = poster
 | 
			
		||||
            this.year = year
 | 
			
		||||
            addEpisodes(DubStatus.Subbed, episodes)
 | 
			
		||||
            showStatus = status
 | 
			
		||||
            plot = description
 | 
			
		||||
            this.tags = tags
 | 
			
		||||
            this.recommendations = recommendations
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun loadLinks(
 | 
			
		||||
        data: String,
 | 
			
		||||
        isCasting: Boolean,
 | 
			
		||||
        subtitleCallback: (SubtitleFile) -> Unit,
 | 
			
		||||
        callback: (ExtractorLink) -> Unit
 | 
			
		||||
    ): Boolean {
 | 
			
		||||
        val servers = app.get(data, interceptor = DdosGuardKiller(true)).document
 | 
			
		||||
        servers.select("video#player > source").map {
 | 
			
		||||
            val url = it.attr("src")
 | 
			
		||||
            val quality = it.attr("size").toInt()
 | 
			
		||||
            callback.invoke(
 | 
			
		||||
                ExtractorLink(
 | 
			
		||||
                    name,
 | 
			
		||||
                    name,
 | 
			
		||||
                    url,
 | 
			
		||||
                    referer = "$mainUrl/",
 | 
			
		||||
                    quality = quality
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,195 @@
 | 
			
		|||
package com.lagradost.cloudstream3.animeproviders
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.annotation.JsonProperty
 | 
			
		||||
import com.lagradost.cloudstream3.*
 | 
			
		||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
 | 
			
		||||
import com.lagradost.cloudstream3.mvvm.safeApiCall
 | 
			
		||||
import com.lagradost.cloudstream3.utils.*
 | 
			
		||||
import org.jsoup.Jsoup
 | 
			
		||||
import org.jsoup.nodes.Element
 | 
			
		||||
import java.util.ArrayList
 | 
			
		||||
 | 
			
		||||
class KuronimeProvider : MainAPI() {
 | 
			
		||||
    override var mainUrl = "https://185.231.223.254"
 | 
			
		||||
    override var name = "Kuronime"
 | 
			
		||||
    override val hasQuickSearch = false
 | 
			
		||||
    override val hasMainPage = true
 | 
			
		||||
    override val lang = "id"
 | 
			
		||||
    override val hasDownloadSupport = true
 | 
			
		||||
 | 
			
		||||
    override val supportedTypes = setOf(
 | 
			
		||||
        TvType.Anime,
 | 
			
		||||
        TvType.AnimeMovie,
 | 
			
		||||
        TvType.OVA
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        fun getType(t: String): TvType {
 | 
			
		||||
            return if (t.contains("OVA") || t.contains("Special")) TvType.OVA
 | 
			
		||||
            else if (t.contains("Movie")) TvType.AnimeMovie
 | 
			
		||||
            else TvType.Anime
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun getStatus(t: String): ShowStatus {
 | 
			
		||||
            return when (t) {
 | 
			
		||||
                "Completed"  -> ShowStatus.Completed
 | 
			
		||||
                "Ongoing" -> ShowStatus.Ongoing
 | 
			
		||||
                else -> ShowStatus.Completed
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun getMainPage(): HomePageResponse {
 | 
			
		||||
        val document = app.get(mainUrl).document
 | 
			
		||||
 | 
			
		||||
        val homePageList = ArrayList<HomePageList>()
 | 
			
		||||
 | 
			
		||||
        document.select(".bixbox").forEach { block ->
 | 
			
		||||
            val header = block.select(".releases > h3").text().trim()
 | 
			
		||||
            val animes = block.select("article").mapNotNull {
 | 
			
		||||
                it.toSearchResult()
 | 
			
		||||
            }
 | 
			
		||||
            if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return HomePageResponse(homePageList)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getProperAnimeLink(uri: String): String {
 | 
			
		||||
        return if (uri.contains("/anime/")) {
 | 
			
		||||
            uri
 | 
			
		||||
        } else {
 | 
			
		||||
            var title = uri.substringAfter("$mainUrl/")
 | 
			
		||||
            title = when {
 | 
			
		||||
                (title.contains("-episode")) && !(title.contains("-movie")) -> Regex("nonton-(.+)-episode").find(title)?.groupValues?.get(1).toString()
 | 
			
		||||
                (title.contains("-movie")) -> Regex("nonton-(.+)-movie").find(title)?.groupValues?.get(1).toString()
 | 
			
		||||
                else -> title
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            "$mainUrl/anime/$title"
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun Element.toSearchResult(): SearchResponse {
 | 
			
		||||
        val href = getProperAnimeLink(fixUrl(this.select("a").attr("href")))
 | 
			
		||||
        val title = this.select(".bsuxtt, .tt > h4").text().trim()
 | 
			
		||||
        val posterUrl = fixUrl(this.select("img").attr("src"))
 | 
			
		||||
        val epNum = this.select(".ep").text().replace(Regex("[^0-9]"), "").trim().toIntOrNull()
 | 
			
		||||
 | 
			
		||||
        return newAnimeSearchResponse(title, href, TvType.Anime) {
 | 
			
		||||
            this.posterUrl = posterUrl
 | 
			
		||||
            addDubStatus(dubExist = false, subExist = true, subEpisodes = epNum)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun search(query: String): List<SearchResponse> {
 | 
			
		||||
        val link = "$mainUrl/?s=$query"
 | 
			
		||||
        val document = app.get(link).document
 | 
			
		||||
 | 
			
		||||
        return document.select("article.bs").mapNotNull {
 | 
			
		||||
            val title = it.selectFirst(".tt > h4")!!.text().trim()
 | 
			
		||||
            val poster = it.select("img").attr("src")
 | 
			
		||||
            val tvType = getType(it.selectFirst(".bt > span")?.text().toString())
 | 
			
		||||
            val href = getProperAnimeLink(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 document = app.get(url).document
 | 
			
		||||
 | 
			
		||||
        val title = document.selectFirst(".entry-title")?.text().toString().trim()
 | 
			
		||||
        val poster = document.select("div[itemprop=image]").joinToString {
 | 
			
		||||
            it.select("img").attr("src")
 | 
			
		||||
        }
 | 
			
		||||
        val tags = document.select(".infodetail > ul > li:nth-child(2) > a").map { it.text() }
 | 
			
		||||
        val type = getType(document.selectFirst(".infodetail > ul > li:nth-child(7)")?.ownText()?.trim().toString())
 | 
			
		||||
        val trailer = document.select("iframe.entered.lazyloaded").attr("src")
 | 
			
		||||
        val year = Regex("\\d, ([0-9]*)").find(
 | 
			
		||||
            document.select(".infodetail > ul > li:nth-child(5)").text()
 | 
			
		||||
        )?.groupValues?.get(1)?.toIntOrNull()
 | 
			
		||||
        val status = getStatus(document.selectFirst(".infodetail > ul > li:nth-child(3)")!!.ownText().replace(Regex("\\W"), ""))
 | 
			
		||||
        val description = document.select("span.const > p").text()
 | 
			
		||||
 | 
			
		||||
        val episodes = document.select("div.bixbox.bxcl > ul > li").map {
 | 
			
		||||
            val name = it.selectFirst("a")?.text()?.trim()?.replace("Episode", title)
 | 
			
		||||
            val link = it.selectFirst("a")!!.attr("href")
 | 
			
		||||
            Episode(link, name)
 | 
			
		||||
        }.reversed()
 | 
			
		||||
 | 
			
		||||
        return newAnimeLoadResponse(title, url, type) {
 | 
			
		||||
            engName = title
 | 
			
		||||
            posterUrl = poster
 | 
			
		||||
            this.year = year
 | 
			
		||||
            addEpisodes(DubStatus.Subbed, episodes)
 | 
			
		||||
            showStatus = status
 | 
			
		||||
            plot = description
 | 
			
		||||
            addTrailer(trailer)
 | 
			
		||||
            this.tags = tags
 | 
			
		||||
            trailers = listOf(trailer)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private suspend fun invokeKuroSource(
 | 
			
		||||
        url: String,
 | 
			
		||||
        sourceCallback: (ExtractorLink) -> Unit
 | 
			
		||||
    ) {
 | 
			
		||||
        val doc = app.get(url).document
 | 
			
		||||
 | 
			
		||||
        doc.select("script").map { script ->
 | 
			
		||||
            if (script.data().contains("function jalankan_jwp() {")) {
 | 
			
		||||
                val data = script.data()
 | 
			
		||||
                val doma = data.substringAfter("var doma = \"").substringBefore("\";")
 | 
			
		||||
                val token = data.substringAfter("var token = \"").substringBefore("\";")
 | 
			
		||||
                val pat = data.substringAfter("var pat = \"").substringBefore("\";")
 | 
			
		||||
                val link = "$doma$token$pat/index.m3u8"
 | 
			
		||||
 | 
			
		||||
                sourceCallback.invoke(
 | 
			
		||||
                    ExtractorLink(
 | 
			
		||||
                        this.name,
 | 
			
		||||
                        this.name,
 | 
			
		||||
                        link,
 | 
			
		||||
                        referer = "https://animeku.org/",
 | 
			
		||||
                        quality = Qualities.Unknown.value,
 | 
			
		||||
                        isM3u8 = link.contains(".m3u8")
 | 
			
		||||
                    )
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun loadLinks(
 | 
			
		||||
        data: String,
 | 
			
		||||
        isCasting: Boolean,
 | 
			
		||||
        subtitleCallback: (SubtitleFile) -> Unit,
 | 
			
		||||
        callback: (ExtractorLink) -> Unit
 | 
			
		||||
    ): Boolean {
 | 
			
		||||
        val document = app.get(data).document
 | 
			
		||||
        val iframeLink = document.select(".mobius > .mirror > option").mapNotNull {
 | 
			
		||||
            fixUrl(Jsoup.parse(base64Decode(it.attr("value"))).select("iframe").attr("src"))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        iframeLink.map {
 | 
			
		||||
            it.replace("https://ok.ru", "http://ok.ru")
 | 
			
		||||
        }.apmap {
 | 
			
		||||
            safeApiCall {
 | 
			
		||||
                when {
 | 
			
		||||
                    it.contains("hxfile.co") -> invokeLocalSource(
 | 
			
		||||
                        it,
 | 
			
		||||
                        this.name,
 | 
			
		||||
                        sourceCallback = callback
 | 
			
		||||
                    )
 | 
			
		||||
//                    it.contains("animeku.org") -> invokeKuroSource(it, callback)
 | 
			
		||||
                    else -> loadExtractor(it, mainUrl, callback)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,190 @@
 | 
			
		|||
package com.lagradost.cloudstream3.animeproviders
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.annotation.JsonProperty
 | 
			
		||||
import com.lagradost.cloudstream3.*
 | 
			
		||||
import com.lagradost.cloudstream3.mvvm.safeApiCall
 | 
			
		||||
import com.lagradost.cloudstream3.utils.ExtractorLink
 | 
			
		||||
import com.lagradost.cloudstream3.utils.loadExtractor
 | 
			
		||||
import org.jsoup.nodes.Element
 | 
			
		||||
import java.util.*
 | 
			
		||||
 | 
			
		||||
class NeonimeProvider : MainAPI() {
 | 
			
		||||
    override var mainUrl = "https://neonime.watch"
 | 
			
		||||
    override var name = "Neonime"
 | 
			
		||||
    override val hasQuickSearch = false
 | 
			
		||||
    override val hasMainPage = true
 | 
			
		||||
    override val lang = "id"
 | 
			
		||||
    override val hasDownloadSupport = true
 | 
			
		||||
 | 
			
		||||
    override val supportedTypes = setOf(
 | 
			
		||||
        TvType.Anime,
 | 
			
		||||
        TvType.AnimeMovie,
 | 
			
		||||
        TvType.OVA
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        fun getType(t: String): TvType {
 | 
			
		||||
            return if (t.contains("OVA") || t.contains("Special")) TvType.OVA
 | 
			
		||||
            else if (t.contains("Movie")) TvType.AnimeMovie
 | 
			
		||||
            else TvType.Anime
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun getStatus(t: String): ShowStatus {
 | 
			
		||||
            return when (t) {
 | 
			
		||||
                "Ended"  -> ShowStatus.Completed
 | 
			
		||||
                "OnGoing" -> ShowStatus.Ongoing
 | 
			
		||||
                "In Production" -> ShowStatus.Ongoing
 | 
			
		||||
                "Returning Series" -> ShowStatus.Ongoing
 | 
			
		||||
                else -> ShowStatus.Completed
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun getMainPage(): HomePageResponse {
 | 
			
		||||
        val document = app.get(mainUrl).document
 | 
			
		||||
 | 
			
		||||
        val homePageList = ArrayList<HomePageList>()
 | 
			
		||||
 | 
			
		||||
        document.select("div.item_1.items").forEach { block ->
 | 
			
		||||
            val header = block.previousElementSibling()?.select("h1")!!.text()
 | 
			
		||||
            val animes = block.select("div.item").mapNotNull {
 | 
			
		||||
                it.toSearchResult()
 | 
			
		||||
            }
 | 
			
		||||
            if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return HomePageResponse(homePageList)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getProperAnimeLink(uri: String): String {
 | 
			
		||||
        return when {
 | 
			
		||||
            uri.contains("/episode") -> {
 | 
			
		||||
                val href = "$mainUrl/tvshows/" + Regex("episode/(.*)-\\d{1,2}x\\d+").find(uri)?.groupValues?.get(1).toString()
 | 
			
		||||
                when {
 | 
			
		||||
                    !href.contains("-subtitle-indonesia") -> "$href-subtitle-indonesia"
 | 
			
		||||
                    href.contains("-special") -> href.replace(Regex("-x\\d+"), "")
 | 
			
		||||
                    else -> href
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else -> uri
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun Element.toSearchResult(): SearchResponse {
 | 
			
		||||
        val href = getProperAnimeLink(fixUrl(this.select("a").attr("href")))
 | 
			
		||||
        val title = this.select("span.tt.title-episode,h2.title-episode-movie").text()
 | 
			
		||||
        val posterUrl = fixUrl(this.select("img").attr("data-src"))
 | 
			
		||||
        val epNum = this.select(".fixyear > h2.text-center").text().replace(Regex("[^0-9]"), "").trim().toIntOrNull()
 | 
			
		||||
 | 
			
		||||
        return newAnimeSearchResponse(title, href, TvType.Anime) {
 | 
			
		||||
            this.posterUrl = posterUrl
 | 
			
		||||
            addDubStatus(dubExist = false, subExist = true, subEpisodes = epNum)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun search(query: String): List<SearchResponse> {
 | 
			
		||||
        val link = "$mainUrl/?s=$query"
 | 
			
		||||
        val document = app.get(link).document
 | 
			
		||||
 | 
			
		||||
        return document.select("div.item.episode-home").mapNotNull {
 | 
			
		||||
            val title = it.selectFirst("div.judul-anime > span")!!.text()
 | 
			
		||||
            val poster = it.select("img").attr("data-src")
 | 
			
		||||
            val episodes = it.selectFirst("div.fixyear > h2.text-center")!!
 | 
			
		||||
                .text().replace(Regex("[^0-9]"), "").trim().toIntOrNull()
 | 
			
		||||
            val tvType = getType(it.selectFirst("span.calidad2.episode")?.text().toString())
 | 
			
		||||
            val href = getProperAnimeLink(fixUrl(it.selectFirst("a")!!.attr("href")))
 | 
			
		||||
 | 
			
		||||
            newAnimeSearchResponse(title, href, tvType) {
 | 
			
		||||
                this.posterUrl = poster
 | 
			
		||||
                addDubStatus(dubExist = false, subExist = true, subEpisodes = episodes)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    data class NeonimeSyncData(
 | 
			
		||||
        @JsonProperty("mal_id") val malId: String?,
 | 
			
		||||
        @JsonProperty("anilist_id") val aniListId: String?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    override suspend fun load(url: String): LoadResponse {
 | 
			
		||||
        val document = app.get(url).document
 | 
			
		||||
 | 
			
		||||
            if (url.contains("movie") || url.contains("live-action")) {
 | 
			
		||||
                val mTitle = document.selectFirst(".sbox > .data > h1[itemprop = name]")?.text().toString().trim()
 | 
			
		||||
                val mPoster =
 | 
			
		||||
                    document.selectFirst(".sbox > .imagen > .fix > img[itemprop = image]")?.attr("data-src")
 | 
			
		||||
                val mTags = document.select("p.meta_dd > a").map { it.text() }
 | 
			
		||||
                val mYear = document.selectFirst("a[href*=release-year]")!!.text().toIntOrNull()
 | 
			
		||||
                val mDescription = document.select("div[itemprop = description]").text().trim()
 | 
			
		||||
                val mRating = document.select("span[itemprop = ratingValue]").text().toIntOrNull()
 | 
			
		||||
 | 
			
		||||
                return MovieLoadResponse(
 | 
			
		||||
                    name = mTitle,
 | 
			
		||||
                    url = url,
 | 
			
		||||
                    this.name,
 | 
			
		||||
                    type = TvType.Movie,
 | 
			
		||||
                    dataUrl = url,
 | 
			
		||||
                    posterUrl = mPoster,
 | 
			
		||||
                    year = mYear,
 | 
			
		||||
                    plot = mDescription,
 | 
			
		||||
                    rating = mRating,
 | 
			
		||||
                    tags = mTags
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                val title = document.select("h1[itemprop = name]").text().trim()
 | 
			
		||||
                val poster = document.selectFirst(".imagen > img")?.attr("data-src")
 | 
			
		||||
                val tags = document.select("#info a[href*=\"-genre/\"]").map { it.text() }
 | 
			
		||||
                val year = document.select("#info a[href*=\"-year/\"]").text().toIntOrNull()
 | 
			
		||||
                val status = getStatus(document.select("div.metadatac > span").last()!!.text().trim())
 | 
			
		||||
                val description = document.select("div[itemprop = description] > p").text().trim()
 | 
			
		||||
 | 
			
		||||
                val episodes = document.select("ul.episodios > li").mapNotNull {
 | 
			
		||||
                    val name = it.selectFirst(".episodiotitle > a")!!.ownText().trim()
 | 
			
		||||
                    val link = fixUrl(it.selectFirst(".episodiotitle > a")!!.attr("href"))
 | 
			
		||||
                    Episode(link, name)
 | 
			
		||||
                }.reversed()
 | 
			
		||||
 | 
			
		||||
                return newAnimeLoadResponse(title, url, TvType.Anime) {
 | 
			
		||||
                    engName = title
 | 
			
		||||
                    posterUrl = poster
 | 
			
		||||
                    this.year = year
 | 
			
		||||
                    addEpisodes(DubStatus.Subbed, episodes)
 | 
			
		||||
                    showStatus = status
 | 
			
		||||
                    plot = description
 | 
			
		||||
                    this.tags = tags
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun loadLinks(
 | 
			
		||||
        data: String,
 | 
			
		||||
        isCasting: Boolean,
 | 
			
		||||
        subtitleCallback: (SubtitleFile) -> Unit,
 | 
			
		||||
        callback: (ExtractorLink) -> Unit
 | 
			
		||||
    ): Boolean {
 | 
			
		||||
        val source = if(data.contains("movie") || data.contains("live-action")) {
 | 
			
		||||
            app.get(data).document.select("#player2-1 > div[id*=div]").mapNotNull {
 | 
			
		||||
                fixUrl(it.select("iframe").attr("data-src"))
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            app.get(data).document.select(".player2 > .embed2 > div[id*=player]").mapNotNull {
 | 
			
		||||
                fixUrl(it.select("iframe").attr("data-src"))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        source.map {
 | 
			
		||||
            it.replace("https://ok.ru", "http://ok.ru")
 | 
			
		||||
        }.apmap {
 | 
			
		||||
                when {
 | 
			
		||||
                    it.contains("blogger.com") -> invokeBloggerSource(it, callback)
 | 
			
		||||
                    it.contains("7njctn.neonime.watch") || it.contains("8njctn.neonime.net") -> invokeLocalSource(it, mainUrl, redirect = false, callback)
 | 
			
		||||
                    else -> loadExtractor(it, data, callback)
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,347 @@
 | 
			
		|||
package com.lagradost.cloudstream3.animeproviders
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.annotation.JsonProperty
 | 
			
		||||
import com.lagradost.cloudstream3.*
 | 
			
		||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
 | 
			
		||||
import com.lagradost.cloudstream3.utils.*
 | 
			
		||||
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
 | 
			
		||||
import org.jsoup.Jsoup
 | 
			
		||||
import org.jsoup.nodes.Element
 | 
			
		||||
import java.net.URI
 | 
			
		||||
import java.util.ArrayList
 | 
			
		||||
 | 
			
		||||
class NontonAnimeIDProvider : MainAPI() {
 | 
			
		||||
    override var mainUrl = "https://75.119.159.228"
 | 
			
		||||
    override var name = "NontonAnimeID"
 | 
			
		||||
    override val hasQuickSearch = false
 | 
			
		||||
    override val hasMainPage = true
 | 
			
		||||
    override val 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") -> TvType.Anime
 | 
			
		||||
                t.contains("Movie") -> 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(): HomePageResponse {
 | 
			
		||||
        val document = app.get(mainUrl).document
 | 
			
		||||
 | 
			
		||||
        val homePageList = ArrayList<HomePageList>()
 | 
			
		||||
 | 
			
		||||
        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:nth-child(2)").forEach { block ->
 | 
			
		||||
            val header = block.selectFirst("h3")!!.ownText().trim()
 | 
			
		||||
            val animes = block.select("li.fullwdth").mapNotNull {
 | 
			
		||||
                it.toSearchResultPopular()
 | 
			
		||||
            }
 | 
			
		||||
            if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return HomePageResponse(homePageList)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getProperAnimeLink(uri: String): String {
 | 
			
		||||
        return if (uri.contains("/anime/")) {
 | 
			
		||||
            uri
 | 
			
		||||
        } else {
 | 
			
		||||
            val name = Regex("$mainUrl/(.*)-episode.*").find(uri)?.groupValues?.get(1).toString()
 | 
			
		||||
            if (name.contains("movie")) {
 | 
			
		||||
                return "$mainUrl/anime/" + name.replace("-movie", "")
 | 
			
		||||
            } else {
 | 
			
		||||
                "$mainUrl/anime/$name"
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun Element.toSearchResult(): SearchResponse {
 | 
			
		||||
        val href = getProperAnimeLink(fixUrl(this.selectFirst("a")!!.attr("href")))
 | 
			
		||||
        val title = this.selectFirst("h3.title")!!.text()
 | 
			
		||||
        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(): SearchResponse {
 | 
			
		||||
        val href = getProperAnimeLink(fixUrl(this.selectFirst("a")!!.attr("href")))
 | 
			
		||||
        val title = this.select("h4").text().trim()
 | 
			
		||||
        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<SearchResponse> {
 | 
			
		||||
        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)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    override suspend fun load(url: String): LoadResponse {
 | 
			
		||||
        val document = app.get(url).document
 | 
			
		||||
 | 
			
		||||
        val title = document.selectFirst("h1.entry-title.cs")!!.text().trim()
 | 
			
		||||
        val poster = document.selectFirst(".poster > img")?.attr("data-src")
 | 
			
		||||
        val tags = document.select(".tagline > a").map { it.text() }
 | 
			
		||||
 | 
			
		||||
        val year = Regex("\\d, ([0-9]*)").find(
 | 
			
		||||
            document.select(".bottomtitle > span:nth-child(5)").text()
 | 
			
		||||
        )?.groupValues?.get(1)?.toIntOrNull()
 | 
			
		||||
        val status = getStatus(
 | 
			
		||||
            document.select("span.statusseries").text().trim()
 | 
			
		||||
        )
 | 
			
		||||
        val type = getType(document.select("span.typeseries").text().trim())
 | 
			
		||||
        val rating = document.select("span.nilaiseries").text().trim().toIntOrNull()
 | 
			
		||||
        val description = document.select(".entry-content.seriesdesc > p").text().trim()
 | 
			
		||||
        val trailer = document.select("a.ytp-impression-link").attr("href")
 | 
			
		||||
 | 
			
		||||
        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("[^0-9]"), "")
 | 
			
		||||
                    .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<EpResponse>().content
 | 
			
		||||
            ).select("li").map {
 | 
			
		||||
                val engName =
 | 
			
		||||
                    document.selectFirst("div.bottomtitle:nth-child(4) > span:nth-child(1)")
 | 
			
		||||
                        ?.ownText()
 | 
			
		||||
                val name = it.selectFirst("span.t1")!!.text().trim().replace("Episode", "$engName")
 | 
			
		||||
                val link = it.selectFirst("a")!!.attr("href")
 | 
			
		||||
                Episode(link, name)
 | 
			
		||||
            }.reversed()
 | 
			
		||||
        } else {
 | 
			
		||||
            document.select("ul.misha_posts_wrap2 > li").map {
 | 
			
		||||
                val name = it.select("span.t1").text().trim()
 | 
			
		||||
                val link = it.select("a").attr("href")
 | 
			
		||||
                Episode(link, name)
 | 
			
		||||
            }.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, type) {
 | 
			
		||||
            engName = title
 | 
			
		||||
            posterUrl = poster
 | 
			
		||||
            this.year = year
 | 
			
		||||
            addEpisodes(DubStatus.Subbed, episodes)
 | 
			
		||||
            showStatus = status
 | 
			
		||||
            this.rating = rating
 | 
			
		||||
            plot = description
 | 
			
		||||
            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<String>()
 | 
			
		||||
 | 
			
		||||
        document.select(".container1 > ul > li").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
 | 
			
		||||
                )
 | 
			
		||||
            ).document.select("iframe").attr("src")
 | 
			
		||||
 | 
			
		||||
            sources.add(fixUrl(iframe))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        sources.map {
 | 
			
		||||
            it.replace("https://ok.ru", "http://ok.ru")
 | 
			
		||||
        }.apmap {
 | 
			
		||||
            when {
 | 
			
		||||
                it.contains("blogger.com") -> invokeBloggerSource(it, callback)
 | 
			
		||||
                it.contains("kotakanimeid.com") -> invokeLocalSource(
 | 
			
		||||
                    it,
 | 
			
		||||
                    this.name,
 | 
			
		||||
                    sourceCallback = callback
 | 
			
		||||
                )
 | 
			
		||||
                else -> loadExtractor(it, data, callback)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// re-use as extractorApis
 | 
			
		||||
 | 
			
		||||
suspend fun invokeBloggerSource(
 | 
			
		||||
    url: String,
 | 
			
		||||
    sourceCallback: (ExtractorLink) -> Unit
 | 
			
		||||
) {
 | 
			
		||||
    val doc = app.get(url).document
 | 
			
		||||
    val sourceName = Regex("[^w{3}]\\.?(.+)\\.").find(URI(url).host)?.groupValues?.get(1).toString()
 | 
			
		||||
        .replace(Regex("\\w+\\."), "").replaceFirstChar { it.uppercase() }
 | 
			
		||||
 | 
			
		||||
    val server =
 | 
			
		||||
        doc.selectFirst("script")?.data()!!.substringAfter("\"streams\":[").substringBefore("]")
 | 
			
		||||
    tryParseJson<List<BloggerSource>>("[$server]")?.map {
 | 
			
		||||
        sourceCallback.invoke(
 | 
			
		||||
            ExtractorLink(
 | 
			
		||||
                sourceName,
 | 
			
		||||
                sourceName,
 | 
			
		||||
                it.play_url,
 | 
			
		||||
                referer = "https://www.youtube.com/",
 | 
			
		||||
                quality = when (it.format_id) {
 | 
			
		||||
                    18 -> 360
 | 
			
		||||
                    22 -> 720
 | 
			
		||||
                    else -> Qualities.Unknown.value
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
data class BloggerSource(
 | 
			
		||||
    @JsonProperty("play_url") val play_url: String,
 | 
			
		||||
    @JsonProperty("format_id") val format_id: Int
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
suspend fun invokeLocalSource(
 | 
			
		||||
    source: String,
 | 
			
		||||
    ref: String,
 | 
			
		||||
    redirect: Boolean = true,
 | 
			
		||||
    sourceCallback: (ExtractorLink) -> Unit
 | 
			
		||||
) {
 | 
			
		||||
    val doc = app.get(source, allowRedirects = redirect).document
 | 
			
		||||
    val sourceName =
 | 
			
		||||
        Regex("[^w{3}]\\.?(.+)\\.").find(URI(source).host)?.groupValues?.get(1).toString()
 | 
			
		||||
            .replace(Regex("\\w+\\."), "").replaceFirstChar { it.uppercase() }
 | 
			
		||||
 | 
			
		||||
    doc.select("script").map { script ->
 | 
			
		||||
        if (script.data().contains("eval(function(p,a,c,k,e,d)")) {
 | 
			
		||||
            val data = getAndUnpack(script.data())
 | 
			
		||||
            val server = data.substringAfter("sources:[").substringBefore("]")
 | 
			
		||||
            tryParseJson<List<ResponseSource>>("[$server]")?.map {
 | 
			
		||||
                sourceCallback.invoke(
 | 
			
		||||
                    ExtractorLink(
 | 
			
		||||
                        sourceName,
 | 
			
		||||
                        sourceName,
 | 
			
		||||
                        it.file,
 | 
			
		||||
                        referer = ref,
 | 
			
		||||
                        quality = when {
 | 
			
		||||
                            source.contains("hxfile.co") -> getQualityFromName(
 | 
			
		||||
                                Regex("\\d\\.(.*?).mp4").find(
 | 
			
		||||
                                    doc.select("title").text()
 | 
			
		||||
                                )?.groupValues?.get(1).toString()
 | 
			
		||||
                            )
 | 
			
		||||
                            else -> getQualityFromName(it.label)
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                    )
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if (script.data().contains("\"sources\":[")) {
 | 
			
		||||
                val server = script.data().substringAfter("\"sources\":[").substringBefore("]")
 | 
			
		||||
                tryParseJson<List<ResponseSource>>("[$server]")?.map {
 | 
			
		||||
                    sourceCallback.invoke(
 | 
			
		||||
                        ExtractorLink(
 | 
			
		||||
                            sourceName,
 | 
			
		||||
                            sourceName,
 | 
			
		||||
                            it.file,
 | 
			
		||||
                            referer = ref,
 | 
			
		||||
                            quality = getQualityFromName(it.label)
 | 
			
		||||
                        )
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                // skip for now
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
data class ResponseSource(
 | 
			
		||||
    @JsonProperty("file") val file: String,
 | 
			
		||||
    @JsonProperty("type") val type: String?,
 | 
			
		||||
    @JsonProperty("label") val label: String?
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,206 @@
 | 
			
		|||
package com.lagradost.cloudstream3.animeproviders
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.annotation.JsonProperty
 | 
			
		||||
import com.lagradost.cloudstream3.*
 | 
			
		||||
import com.lagradost.cloudstream3.utils.ExtractorLink
 | 
			
		||||
import com.lagradost.cloudstream3.utils.loadExtractor
 | 
			
		||||
import org.jsoup.Jsoup
 | 
			
		||||
import org.jsoup.nodes.Element
 | 
			
		||||
import java.util.ArrayList
 | 
			
		||||
 | 
			
		||||
class OploverzProvider : MainAPI() {
 | 
			
		||||
    override var mainUrl = "https://oploverz.asia"
 | 
			
		||||
    override var name = "Oploverz"
 | 
			
		||||
    override val hasQuickSearch = false
 | 
			
		||||
    override val hasMainPage = true
 | 
			
		||||
    override val 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") -> TvType.Anime
 | 
			
		||||
                t.contains("Movie") -> TvType.AnimeMovie
 | 
			
		||||
                else -> TvType.OVA
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun getStatus(t: String): ShowStatus {
 | 
			
		||||
            return when (t) {
 | 
			
		||||
                "Completed" -> ShowStatus.Completed
 | 
			
		||||
                "Ongoing" -> ShowStatus.Ongoing
 | 
			
		||||
                else -> ShowStatus.Completed
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun getMainPage(): HomePageResponse {
 | 
			
		||||
        val document = app.get(mainUrl).document
 | 
			
		||||
 | 
			
		||||
        val homePageList = ArrayList<HomePageList>()
 | 
			
		||||
 | 
			
		||||
        document.select(".bixbox.bbnofrm").forEach { block ->
 | 
			
		||||
            val header = block.selectFirst("h3")!!.text().trim()
 | 
			
		||||
            val animes = block.select("article[itemscope=itemscope]").mapNotNull {
 | 
			
		||||
                it.toSearchResult()
 | 
			
		||||
            }
 | 
			
		||||
            if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return HomePageResponse(homePageList)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getProperAnimeLink(uri: String): String {
 | 
			
		||||
 | 
			
		||||
        return if (uri.contains("/anime/")) {
 | 
			
		||||
            uri
 | 
			
		||||
        } else {
 | 
			
		||||
            var title = uri.substringAfter("$mainUrl/")
 | 
			
		||||
            title = when {
 | 
			
		||||
                (title.contains("-episode")) && !(title.contains("-ova")) -> Regex("(.+)-episode").find(
 | 
			
		||||
                    title
 | 
			
		||||
                )?.groupValues?.get(1).toString()
 | 
			
		||||
                (title.contains("-ova")) -> Regex("(.+)-ova").find(title)?.groupValues?.get(1)
 | 
			
		||||
                    .toString()
 | 
			
		||||
                else -> Regex("(.+)-subtitle").find(title)?.groupValues?.get(1).toString()
 | 
			
		||||
                    .replace(Regex("-\\d+"), "")
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            when {
 | 
			
		||||
                title.contains("overlord") -> {
 | 
			
		||||
                    title = title.replace("s", "season-")
 | 
			
		||||
                }
 | 
			
		||||
                title.contains("kaguya-sama") -> {
 | 
			
		||||
                    title = title.replace("s3", "ultra-romantic")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            "$mainUrl/anime/$title"
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun Element.toSearchResult(): SearchResponse {
 | 
			
		||||
        val href = getProperAnimeLink(this.selectFirst("a.tip")!!.attr("href"))
 | 
			
		||||
        val title = this.selectFirst("h2[itemprop=headline]")!!.text().trim()
 | 
			
		||||
        val posterUrl = fixUrl(this.selectFirst("img")!!.attr("src"))
 | 
			
		||||
        val type = getType(this.selectFirst(".eggtype, .typez")!!.text().trim())
 | 
			
		||||
        val epNum =
 | 
			
		||||
            this.selectFirst(".eggepisode, span.epx")!!.text().replace(Regex("[^0-9]"), "").trim()
 | 
			
		||||
                .toIntOrNull()
 | 
			
		||||
 | 
			
		||||
        return newAnimeSearchResponse(title, href, type) {
 | 
			
		||||
            this.posterUrl = posterUrl
 | 
			
		||||
            addDubStatus(dubExist = false, subExist = true, subEpisodes = epNum)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun search(query: String): List<SearchResponse> {
 | 
			
		||||
        val link = "$mainUrl/?s=$query"
 | 
			
		||||
        val document = app.get(link).document
 | 
			
		||||
 | 
			
		||||
        return document.select("article[itemscope=itemscope]").map {
 | 
			
		||||
            val title = it.selectFirst(".tt")!!.ownText().trim()
 | 
			
		||||
            val poster = it.selectFirst("img")!!.attr("src")
 | 
			
		||||
            val tvType = getType(it.selectFirst(".typez")?.text().toString())
 | 
			
		||||
            val href = fixUrl(it.selectFirst("a.tip")!!.attr("href"))
 | 
			
		||||
 | 
			
		||||
            newAnimeSearchResponse(title, href, tvType) {
 | 
			
		||||
                this.posterUrl = poster
 | 
			
		||||
                addDubStatus(dubExist = false, subExist = true)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun load(url: String): LoadResponse {
 | 
			
		||||
        val document = app.get(url).document
 | 
			
		||||
 | 
			
		||||
        val title = document.selectFirst("h1.entry-title")!!.text().trim()
 | 
			
		||||
        val poster = document.select(".thumb > img").attr("src")
 | 
			
		||||
        val tags = document.select(".genxed > a").map { it.text() }
 | 
			
		||||
 | 
			
		||||
        val year = Regex("\\d, ([0-9]*)").find(
 | 
			
		||||
            document.selectFirst(".info-content > .spe > span > time")!!.text().trim()
 | 
			
		||||
        )?.groupValues?.get(1).toString().toIntOrNull()
 | 
			
		||||
        val status = getStatus(
 | 
			
		||||
            document.select(".info-content > .spe > span:nth-child(1)")
 | 
			
		||||
                .text().trim().replace("Status: ", "")
 | 
			
		||||
        )
 | 
			
		||||
        val typeCheck =
 | 
			
		||||
            when {
 | 
			
		||||
                document.select(".info-content > .spe > span:nth-child(5), .info-content > .spe > span")
 | 
			
		||||
                    .text().trim().contains("TV") -> "TV"
 | 
			
		||||
                document.select(".info-content > .spe > span:nth-child(5), .info-content > .spe > span")
 | 
			
		||||
                    .text().trim().contains("TV") -> "Movie"
 | 
			
		||||
                else -> "OVA"
 | 
			
		||||
            }
 | 
			
		||||
        val type = getType(typeCheck)
 | 
			
		||||
        val description = document.select(".entry-content > p").text().trim()
 | 
			
		||||
 | 
			
		||||
        val episodes = document.select(".eplister > ul > li").map {
 | 
			
		||||
            val name = it.select(".epl-title").text().trim()
 | 
			
		||||
            val link = fixUrl(it.select("a").attr("href"))
 | 
			
		||||
            Episode(link, name)
 | 
			
		||||
        }.reversed()
 | 
			
		||||
 | 
			
		||||
        val recommendations =
 | 
			
		||||
            document.select(".listupd > article[itemscope=itemscope]").mapNotNull { rec ->
 | 
			
		||||
                val epTitle = rec.selectFirst(".tt")!!.ownText().trim()
 | 
			
		||||
                val epPoster = rec.selectFirst("img")!!.attr("src")
 | 
			
		||||
                val epType = getType(rec.selectFirst(".typez")?.text().toString())
 | 
			
		||||
                val epHref = fixUrl(rec.selectFirst("a.tip")!!.attr("href"))
 | 
			
		||||
 | 
			
		||||
                newAnimeSearchResponse(epTitle, epHref, epType) {
 | 
			
		||||
                    this.posterUrl = epPoster
 | 
			
		||||
                    addDubStatus(dubExist = false, subExist = true)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        return newAnimeLoadResponse(title, url, type) {
 | 
			
		||||
            engName = title
 | 
			
		||||
            posterUrl = poster
 | 
			
		||||
            this.year = year
 | 
			
		||||
            addEpisodes(DubStatus.Subbed, episodes)
 | 
			
		||||
            showStatus = status
 | 
			
		||||
            plot = description
 | 
			
		||||
            this.tags = tags
 | 
			
		||||
            this.recommendations = recommendations
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    data class Source(
 | 
			
		||||
        @JsonProperty("play_url") val play_url: String,
 | 
			
		||||
        @JsonProperty("format_id") val format_id: Int
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    override suspend fun loadLinks(
 | 
			
		||||
        data: String,
 | 
			
		||||
        isCasting: Boolean,
 | 
			
		||||
        subtitleCallback: (SubtitleFile) -> Unit,
 | 
			
		||||
        callback: (ExtractorLink) -> Unit
 | 
			
		||||
    ): Boolean {
 | 
			
		||||
        val document = app.get(data).document
 | 
			
		||||
        val iframeLink = document.select(".mobius > .mirror > option").mapNotNull {
 | 
			
		||||
            fixUrl(Jsoup.parse(base64Decode(it.attr("value"))).select("iframe").attr("src"))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        iframeLink.map {
 | 
			
		||||
            it.replace("https://ok.ru", "http://ok.ru")
 | 
			
		||||
        }.apmap {
 | 
			
		||||
            when {
 | 
			
		||||
                it.contains("blogger.com") -> invokeBloggerSource(it, callback)
 | 
			
		||||
                else -> loadExtractor(it, data, callback)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,217 @@
 | 
			
		|||
package com.lagradost.cloudstream3.movieproviders
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.annotation.JsonProperty
 | 
			
		||||
import com.lagradost.cloudstream3.*
 | 
			
		||||
import com.lagradost.cloudstream3.animeproviders.invokeBloggerSource
 | 
			
		||||
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
 | 
			
		||||
import com.lagradost.cloudstream3.utils.ExtractorLink
 | 
			
		||||
import com.lagradost.cloudstream3.utils.getQualityFromName
 | 
			
		||||
import com.lagradost.cloudstream3.utils.loadExtractor
 | 
			
		||||
import org.jsoup.Jsoup
 | 
			
		||||
import org.jsoup.nodes.Element
 | 
			
		||||
import java.util.ArrayList
 | 
			
		||||
 | 
			
		||||
class DramaidProvider : MainAPI() {
 | 
			
		||||
    override var mainUrl = "https://185.224.83.103"
 | 
			
		||||
    override var name = "DramaId"
 | 
			
		||||
    override val hasQuickSearch = false
 | 
			
		||||
    override val hasMainPage = true
 | 
			
		||||
    override val lang = "id"
 | 
			
		||||
    override val hasDownloadSupport = true
 | 
			
		||||
    override val hasChromecastSupport = false
 | 
			
		||||
    override val supportedTypes = setOf(TvType.AsianDrama)
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        fun getStatus(t: String): ShowStatus {
 | 
			
		||||
            return when (t) {
 | 
			
		||||
                "Completed" -> ShowStatus.Completed
 | 
			
		||||
                "Ongoing" -> ShowStatus.Ongoing
 | 
			
		||||
                else -> ShowStatus.Completed
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun getMainPage(): HomePageResponse {
 | 
			
		||||
        val document = app.get(mainUrl).document
 | 
			
		||||
 | 
			
		||||
        val homePageList = ArrayList<HomePageList>()
 | 
			
		||||
 | 
			
		||||
        document.select(".bixbox").forEach { block ->
 | 
			
		||||
            val header = block.selectFirst(".releases > h3")!!.text().trim()
 | 
			
		||||
            val dramas = block.select("article[itemscope=itemscope]").mapNotNull {
 | 
			
		||||
                it.toSearchResult()
 | 
			
		||||
            }
 | 
			
		||||
            if (dramas.isNotEmpty()) homePageList.add(HomePageList(header, dramas))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return HomePageResponse(homePageList)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getProperDramaLink(uri: String): String {
 | 
			
		||||
        return if (uri.contains("/series/")) {
 | 
			
		||||
            uri
 | 
			
		||||
        } else {
 | 
			
		||||
            "$mainUrl/series/" + Regex("$mainUrl/(.+)-ep.+").find(uri)?.groupValues?.get(1)
 | 
			
		||||
                .toString()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun Element.toSearchResult(): SearchResponse {
 | 
			
		||||
        val href = getProperDramaLink(this.selectFirst("a.tip")!!.attr("href"))
 | 
			
		||||
        val title = this.selectFirst("h2[itemprop=headline]")!!.text().trim()
 | 
			
		||||
        val posterUrl = this.selectFirst(".limit > noscript > img")!!.attr("src")
 | 
			
		||||
 | 
			
		||||
        return newTvSeriesSearchResponse(title, href, TvType.AsianDrama) {
 | 
			
		||||
            this.posterUrl = posterUrl
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun search(query: String): List<SearchResponse> {
 | 
			
		||||
        val link = "$mainUrl/?s=$query"
 | 
			
		||||
        val document = app.get(link).document
 | 
			
		||||
 | 
			
		||||
        return document.select("article[itemscope=itemscope]").map {
 | 
			
		||||
            val title = it.selectFirst("h2[itemprop=headline]")!!.text().trim()
 | 
			
		||||
            val poster = it.selectFirst(".limit > noscript > img")!!.attr("src")
 | 
			
		||||
            val href = it.selectFirst("a.tip")!!.attr("href")
 | 
			
		||||
 | 
			
		||||
            newTvSeriesSearchResponse(title, href, TvType.AsianDrama) {
 | 
			
		||||
                this.posterUrl = poster
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun load(url: String): LoadResponse {
 | 
			
		||||
        val document = app.get(url).document
 | 
			
		||||
 | 
			
		||||
        val title = document.selectFirst("h1.entry-title")!!.text().trim()
 | 
			
		||||
        val poster = document.select(".thumb > noscript > img").attr("src")
 | 
			
		||||
        val tags = document.select(".genxed > a").map { it.text() }
 | 
			
		||||
 | 
			
		||||
        val year = Regex("\\d, ([0-9]*)").find(
 | 
			
		||||
            document.selectFirst(".info-content > .spe > span > time")!!.text().trim()
 | 
			
		||||
        )?.groupValues?.get(1).toString().toIntOrNull()
 | 
			
		||||
        val status = getStatus(
 | 
			
		||||
            document.select(".info-content > .spe > span:nth-child(1)")
 | 
			
		||||
                .text().trim().replace("Status: ", "")
 | 
			
		||||
        )
 | 
			
		||||
        val description = document.select(".entry-content > p").text().trim()
 | 
			
		||||
 | 
			
		||||
        val episodes = document.select(".eplister > ul > li").map {
 | 
			
		||||
            val name = it.selectFirst("a > .epl-title")!!.text().trim()
 | 
			
		||||
            val link = it.select("a").attr("href")
 | 
			
		||||
            val epNum = it.selectFirst("a > .epl-num")!!.text().trim().toIntOrNull()
 | 
			
		||||
            newEpisode(link) {
 | 
			
		||||
                this.name = name
 | 
			
		||||
                this.episode = epNum
 | 
			
		||||
            }
 | 
			
		||||
        }.reversed()
 | 
			
		||||
 | 
			
		||||
        val recommendations =
 | 
			
		||||
            document.select(".listupd > article[itemscope=itemscope]").map { rec ->
 | 
			
		||||
                val epTitle = rec.selectFirst("h2[itemprop=headline]")!!.text().trim()
 | 
			
		||||
                val epPoster = rec.selectFirst(".limit > noscript > img")!!.attr("src")
 | 
			
		||||
                val epHref = fixUrl(rec.selectFirst("a.tip")!!.attr("href"))
 | 
			
		||||
 | 
			
		||||
                newTvSeriesSearchResponse(epTitle, epHref, TvType.AsianDrama) {
 | 
			
		||||
                    this.posterUrl = epPoster
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        if (episodes.size == 1) {
 | 
			
		||||
            return newMovieLoadResponse(title, url, TvType.Movie, episodes[0].data) {
 | 
			
		||||
                posterUrl = poster
 | 
			
		||||
                this.year = year
 | 
			
		||||
                plot = description
 | 
			
		||||
                this.tags = tags
 | 
			
		||||
                this.recommendations = recommendations
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            return newTvSeriesLoadResponse(title, url, TvType.AsianDrama, episodes = episodes) {
 | 
			
		||||
                posterUrl = poster
 | 
			
		||||
                this.year = year
 | 
			
		||||
                showStatus = status
 | 
			
		||||
                plot = description
 | 
			
		||||
                this.tags = tags
 | 
			
		||||
                this.recommendations = recommendations
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private data class Sources(
 | 
			
		||||
        @JsonProperty("file") val file: String,
 | 
			
		||||
        @JsonProperty("label") val label: String,
 | 
			
		||||
        @JsonProperty("type") val type: String,
 | 
			
		||||
        @JsonProperty("default") val default: Boolean?
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    private data class Tracks(
 | 
			
		||||
        @JsonProperty("file") val file: String,
 | 
			
		||||
        @JsonProperty("label") val label: String,
 | 
			
		||||
        @JsonProperty("kind") val type: String,
 | 
			
		||||
        @JsonProperty("default") val default: Boolean?
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    private suspend fun invokeDriveSource(
 | 
			
		||||
        url: String,
 | 
			
		||||
        name: String,
 | 
			
		||||
        subCallback: (SubtitleFile) -> Unit,
 | 
			
		||||
        sourceCallback: (ExtractorLink) -> Unit
 | 
			
		||||
    ) {
 | 
			
		||||
        val server = app.get(url).document.selectFirst(".picasa")?.nextElementSibling()?.data()
 | 
			
		||||
 | 
			
		||||
        val source = "[${server!!.substringAfter("sources: [").substringBefore("],")}]".trimIndent()
 | 
			
		||||
        val trackers = server.substringAfter("tracks:[").substringBefore("],")
 | 
			
		||||
            .replace("//language", "")
 | 
			
		||||
            .replace("file", "\"file\"")
 | 
			
		||||
            .replace("label", "\"label\"")
 | 
			
		||||
            .replace("kind", "\"kind\"").trimIndent()
 | 
			
		||||
 | 
			
		||||
        tryParseJson<List<Sources>>(source)?.map {
 | 
			
		||||
            sourceCallback(
 | 
			
		||||
                ExtractorLink(
 | 
			
		||||
                    name,
 | 
			
		||||
                    "Drive",
 | 
			
		||||
                    fixUrl(it.file),
 | 
			
		||||
                    referer = "https://motonews.club/",
 | 
			
		||||
                    quality = getQualityFromName(it.label)
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        tryParseJson<Tracks>(trackers)?.let {
 | 
			
		||||
            subCallback(
 | 
			
		||||
                SubtitleFile(
 | 
			
		||||
                    if (it.label.contains("Indonesia")) "${it.label}n" else it.label,
 | 
			
		||||
                    it.file
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun loadLinks(
 | 
			
		||||
        data: String,
 | 
			
		||||
        isCasting: Boolean,
 | 
			
		||||
        subtitleCallback: (SubtitleFile) -> Unit,
 | 
			
		||||
        callback: (ExtractorLink) -> Unit
 | 
			
		||||
    ): Boolean {
 | 
			
		||||
        val document = app.get(data).document
 | 
			
		||||
        val iframeLink = document.select(".mobius > .mirror > option").mapNotNull {
 | 
			
		||||
            fixUrl(Jsoup.parse(base64Decode(it.attr("value"))).select("iframe").attr("src"))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        iframeLink.map {
 | 
			
		||||
            it.replace("https://ndrama.xyz", "https://www.fembed.com")
 | 
			
		||||
        }.apmap {
 | 
			
		||||
            when {
 | 
			
		||||
                it.contains("motonews.club") -> invokeDriveSource(it, this.name, subtitleCallback, callback)
 | 
			
		||||
                else -> loadExtractor(it, data, callback)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue