diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index e66787d2..19e309fd 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -42,6 +42,8 @@ object APIHolder { val allProviders by lazy { arrayListOf( // Movie providers + ElifilmsProvider(), + EstrenosDoramasProvider(), PelisplusProvider(), PelisplusHDProvider(), PeliSmartProvider(), @@ -106,6 +108,9 @@ object APIHolder { //ShiroProvider(), // v2 fucked me AnimeFlickProvider(), AnimeflvnetProvider(), + AnimefenixProvider(), + AnimeflvIOProvider(), + JKAnimeProvider(), TenshiProvider(), WcoProvider(), AnimePaheProvider(), @@ -115,6 +120,7 @@ object APIHolder { ZoroProvider(), DubbedAnimeProvider(), MonoschinosProvider(), + MundoDonghuaProvider(), KawaiifuProvider(), // disabled due to cloudflare NeonimeProvider(), KuramanimeProvider(), diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimefenixProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimefenixProvider.kt new file mode 100644 index 00000000..11eb01c2 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimefenixProvider.kt @@ -0,0 +1,248 @@ +package com.lagradost.cloudstream3.animeproviders + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.utils.AppUtils.parseJson +import org.jsoup.Jsoup +import java.util.* +import kotlin.collections.ArrayList + + +class AnimefenixProvider:MainAPI() { + + override var mainUrl = "https://animefenix.com" + override var name = "Animefenix" + override val lang = "es" + override val hasMainPage = true + override val hasChromecastSupport = true + override val hasDownloadSupport = true + override val supportedTypes = setOf( + TvType.AnimeMovie, + TvType.OVA, + TvType.Anime, + ) + + fun getDubStatus(title: String): DubStatus { + return if (title.contains("Latino") || title.contains("Castellano")) + DubStatus.Dubbed + else DubStatus.Subbed + } + + override suspend fun getMainPage(): HomePageResponse { + val urls = listOf( + Pair("$mainUrl/", "Animes"), + Pair("$mainUrl/animes?type[]=movie&order=default", "Peliculas", ), + Pair("$mainUrl/animes?type[]=ova&order=default", "OVA's", ), + ) + + val items = ArrayList() + + items.add( + HomePageList( + "Últimos episodios", + app.get(mainUrl).document.select(".capitulos-grid div.item").map { + val title = it.selectFirst("div.overtitle")?.text() + val poster = it.selectFirst("a img")?.attr("src") + val epRegex = Regex("(-(\\d+)\$|-(\\d+)\\.(\\d+))") + val url = it.selectFirst("a")?.attr("href")?.replace(epRegex,"") + ?.replace("/ver/","/") + val epNum = it.selectFirst(".is-size-7")?.text()?.replace("Episodio ","")?.toIntOrNull() + newAnimeSearchResponse(title!!, url!!) { + this.posterUrl = poster + addDubStatus(getDubStatus(title), epNum) + } + }) + ) + + urls.apmap { (url, name) -> + val response = app.get(url) + val soup = Jsoup.parse(response.text) + val home = soup.select(".list-series article").map { + val title = it.selectFirst("h3 a")?.text() + val poster = it.selectFirst("figure img")?.attr("src") + AnimeSearchResponse( + title!!, + it.selectFirst("a")?.attr("href") ?: "", + this.name, + TvType.Anime, + poster, + null, + if (title.contains("Latino")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of(DubStatus.Subbed), + ) + } + + items.add(HomePageList(name, home)) + } + + if (items.size <= 0) throw ErrorLoadingException() + return HomePageResponse(items) + } + + override suspend fun search(query: String): List { + return app.get("$mainUrl/animes?q=$query").document.select(".list-series article").map { + val title = it.selectFirst("h3 a")?.text() + val href = it.selectFirst("a")?.attr("href") + val image = it.selectFirst("figure img")?.attr("src") + AnimeSearchResponse( + title!!, + href!!, + this.name, + TvType.Anime, + fixUrl(image ?: ""), + null, + if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of( + DubStatus.Subbed), + ) + } + } + + override suspend fun load(url: String): LoadResponse { + val doc = Jsoup.parse(app.get(url, timeout = 120).text) + val poster = doc.selectFirst(".image > img")?.attr("src") + val title = doc.selectFirst("h1.title.has-text-orange")?.text() + val description = doc.selectFirst("p.has-text-light")?.text() + val genres = doc.select(".genres a").map { it.text() } + val status = when (doc.selectFirst(".is-narrow-desktop a.button")?.text()) { + "Emisión" -> ShowStatus.Ongoing + "Finalizado" -> ShowStatus.Completed + else -> null + } + val episodes = doc.select(".anime-page__episode-list li").map { + val name = it.selectFirst("span")?.text() + val link = it.selectFirst("a")?.attr("href") + Episode(link!!, name) + }.reversed() + val type = if (doc.selectFirst("ul.has-text-light")?.text() + !!.contains("Película") && episodes.size == 1 + ) TvType.AnimeMovie else TvType.Anime + return newAnimeLoadResponse(title!!, url, type) { + japName = null + engName = title + posterUrl = poster + addEpisodes(DubStatus.Subbed, episodes) + plot = description + tags = genres + showStatus = status + } + } + + private fun cleanStreamID(input: String): String = input.replace(Regex("player=.*&code=|&"),"") + + data class Amazon ( + @JsonProperty("file") var file : String? = null, + @JsonProperty("type") var type : String? = null, + @JsonProperty("label") var label : String? = null + ) + + private fun cleanExtractor( + source: String, + name: String, + url: String, + callback: (ExtractorLink) -> Unit + ): Boolean { + callback( + ExtractorLink( + source, + name, + url, + "", + Qualities.Unknown.value, + false + ) + ) + return true + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + val soup = app.get(data).document + val script = soup.selectFirst(".player-container script")?.data() + if (script!!.contains("var tabsArray =")) { + val sourcesRegex = Regex("player=.*&code(.*)&") + val test = sourcesRegex.findAll(script).toList() + test.apmap { + val codestream = it.value + val links = when { + codestream.contains("player=2&") -> "https://embedsito.com/v/"+cleanStreamID(codestream) + codestream.contains("player=3&") -> "https://www.mp4upload.com/embed-"+cleanStreamID(codestream)+".html" + codestream.contains("player=6&") -> "https://www.yourupload.com/embed/"+cleanStreamID(codestream) + codestream.contains("player=12&") -> "http://ok.ru/videoembed/"+cleanStreamID(codestream) + codestream.contains("player=4&") -> "https://sendvid.com/"+cleanStreamID(codestream) + codestream.contains("player=9&") -> "AmaNormal https://www.animefenix.com/stream/amz.php?v="+cleanStreamID(codestream) + codestream.contains("player=11&") -> "AmazonES https://www.animefenix.com/stream/amz.php?v="+cleanStreamID(codestream) + codestream.contains("player=22&") -> "Fireload https://www.animefenix.com/stream/fl.php?v="+cleanStreamID(codestream) + + else -> "" + } + loadExtractor(links, data, callback) + + argamap({ + if (links.contains("AmaNormal")) { + val doc = app.get(links.replace("AmaNormal ","")).document + doc.select("script").map { script -> + if (script.data().contains("sources: [{\"file\"")) { + val text = script.data().substringAfter("sources:").substringBefore("]").replace("[","") + val json = parseJson(text) + if (json.file != null) { + cleanExtractor( + "Amazon", + "Amazon ${json.label}", + json.file!!, + callback + ) + } + } + } + } + + if (links.contains("AmazonES")) { + val amazonES = links.replace("AmazonES ", "") + val doc = app.get("$amazonES&ext=es").document + doc.select("script").map { script -> + if (script.data().contains("sources: [{\"file\"")) { + val text = script.data().substringAfter("sources:").substringBefore("]").replace("[","") + val json = parseJson(text) + if (json.file != null) { + cleanExtractor( + "AmazonES", + "AmazonES ${json.label}", + json.file!!, + callback + ) + } + } + } + } + if (links.contains("Fireload")) { + val doc = app.get(links.replace("Fireload ", "")).document + doc.select("script").map { script -> + if (script.data().contains("sources: [{\"file\"")) { + val text = script.data().substringAfter("sources:").substringBefore("]").replace("[","") + val json = parseJson(text) + val testurl = if (json.file?.contains("fireload") == true) { + app.get("https://${json.file}").text + } else null + if (testurl?.contains("error") == true) { + // + } else if (json.file?.contains("fireload") == true) { + cleanExtractor( + "Fireload", + "Fireload ${json.label}", + "https://"+json.file!!, + callback + ) + } + } + } + } + }) + } + } + return true + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeflvIOProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeflvIOProvider.kt new file mode 100644 index 00000000..481c2b25 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeflvIOProvider.kt @@ -0,0 +1,227 @@ +package com.lagradost.cloudstream3.movieproviders + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.utils.AppUtils.parseJson +import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8 +import java.util.* +import kotlin.collections.ArrayList + +class AnimeflvIOProvider:MainAPI() { + override var mainUrl = "https://animeflv.io" //Also scrapes from animeid.to + override var name = "Animeflv.io" + override val lang = "es" + override val hasMainPage = true + override val hasChromecastSupport = true + override val hasDownloadSupport = true + override val supportedTypes = setOf( + TvType.AnimeMovie, + TvType.OVA, + TvType.Anime, + ) + override suspend fun getMainPage(): HomePageResponse { + val items = ArrayList() + val urls = listOf( + Pair("$mainUrl/series", "Series actualizadas",), + Pair("$mainUrl/peliculas", "Peliculas actualizadas"), + ) + items.add(HomePageList("Estrenos", app.get(mainUrl).document.select("div#owl-demo-premiere-movies .pull-left").map{ + val title = it.selectFirst("p")?.text() ?: "" + AnimeSearchResponse( + title, + fixUrl(it.selectFirst("a")?.attr("href") ?: ""), + this.name, + TvType.Anime, + it.selectFirst("img")?.attr("src"), + it.selectFirst("span.year").toString().toIntOrNull(), + EnumSet.of(DubStatus.Subbed), + ) + })) + urls.apmap { (url, name) -> + val soup = app.get(url).document + val home = soup.select("div.item-pelicula").map { + val title = it.selectFirst(".item-detail p")?.text() ?: "" + val poster = it.selectFirst("figure img")?.attr("src") + AnimeSearchResponse( + title, + fixUrl(it.selectFirst("a")?.attr("href") ?: ""), + this.name, + TvType.Anime, + poster, + null, + if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of(DubStatus.Subbed), + ) + } + + items.add(HomePageList(name, home)) + } + + if (items.size <= 0) throw ErrorLoadingException() + return HomePageResponse(items) + } + + override suspend fun search(query: String): List { + val headers = mapOf( + "Host" to "animeflv.io", + "User-Agent" to USER_AGENT, + "X-Requested-With" to "XMLHttpRequest", + "DNT" to "1", + "Alt-Used" to "animeflv.io", + "Connection" to "keep-alive", + "Referer" to "https://animeflv.io", + ) + val url = "$mainUrl/search.html?keyword=$query" + val document = app.get( + url, + headers = headers + ).document + return document.select(".item-pelicula.pull-left").map { + val title = it.selectFirst("div.item-detail p")?.text() ?: "" + val href = fixUrl(it.selectFirst("a")?.attr("href") ?: "") + var image = it.selectFirst("figure img")?.attr("src") ?: "" + val isMovie = href.contains("/pelicula/") + if (image.contains("/static/img/picture.png")) { image = ""} + if (isMovie) { + MovieSearchResponse( + title, + href, + this.name, + TvType.AnimeMovie, + image, + null + ) + } else { + AnimeSearchResponse( + title, + href, + this.name, + TvType.Anime, + image, + null, + EnumSet.of(DubStatus.Subbed), + ) + } + } + } + + override suspend fun load(url: String): LoadResponse? { + // Gets the url returned from searching. + val soup = app.get(url).document + val title = soup.selectFirst(".info-content h1")?.text() + val description = soup.selectFirst("span.sinopsis")?.text()?.trim() + val poster: String? = soup.selectFirst(".poster img")?.attr("src") + val episodes = soup.select(".item-season-episodes a").map { li -> + val href = fixUrl(li.selectFirst("a")?.attr("href") ?: "") + val name = li.selectFirst("a")?.text() ?: "" + Episode( + href, name, + ) + }.reversed() + + val year = Regex("(\\d*)").find(soup.select(".info-half").text()) + + val tvType = if (url.contains("/pelicula/")) TvType.AnimeMovie else TvType.Anime + val genre = soup.select(".content-type-a a") + .map { it?.text()?.trim().toString().replace(", ","") } + val duration = Regex("""(\d*)""").find( + soup.select("p.info-half:nth-child(4)").text()) + + return when (tvType) { + TvType.Anime -> { + return newAnimeLoadResponse(title ?: "", url, tvType) { + japName = null + engName = title + posterUrl = poster + this.year = null + addEpisodes(DubStatus.Subbed, episodes) + plot = description + tags = genre + + showStatus = null + } + } + TvType.AnimeMovie -> { + MovieLoadResponse( + title ?: "", + url, + this.name, + tvType, + url, + poster, + year.toString().toIntOrNull(), + description, + null, + genre, + duration.toString().toIntOrNull(), + ) + } + else -> null + } + } + + data class MainJson ( + @JsonProperty("source") val source: List, + @JsonProperty("source_bk") val sourceBk: String?, + @JsonProperty("track") val track: List?, + @JsonProperty("advertising") val advertising: List?, + @JsonProperty("linkiframe") val linkiframe: String? + ) + + data class Source ( + @JsonProperty("file") val file: String, + @JsonProperty("label") val label: String, + @JsonProperty("default") val default: String, + @JsonProperty("type") val type: String + ) + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + app.get(data).document.select("li.tab-video").apmap { + val url = fixUrl(it.attr("data-video")) + if (url.contains("animeid")) { + val ajaxurl = url.replace("streaming.php","ajax.php") + val ajaxurltext = app.get(ajaxurl).text + val json = parseJson(ajaxurltext) + json.source.forEach { source -> + if (source.file.contains("m3u8")) { + generateM3u8( + "Animeflv.io", + source.file, + "https://animeid.to", + headers = mapOf("Referer" to "https://animeid.to") + ).apmap { + callback( + ExtractorLink( + "Animeflv.io", + "Animeflv.io", + it.url, + "https://animeid.to", + getQualityFromName(it.quality.toString()), + it.url.contains("m3u8") + ) + ) + } + } else { + callback( + ExtractorLink( + name, + "$name ${source.label}", + source.file, + "https://animeid.to", + Qualities.Unknown.value, + isM3u8 = source.file.contains("m3u8") + ) + ) + } + } + } + loadExtractor(url, data, callback) + } + return true + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/JKAnimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/JKAnimeProvider.kt new file mode 100644 index 00000000..d6e215a9 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/JKAnimeProvider.kt @@ -0,0 +1,276 @@ +package com.lagradost.cloudstream3.animeproviders + + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.utils.AppUtils.parseJson +import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8 +import java.util.* +import kotlin.collections.ArrayList +import kotlin.collections.List + + +class JKAnimeProvider : MainAPI() { + companion object { + fun getType(t: String): TvType { + return if (t.contains("OVA") || t.contains("Especial")) TvType.OVA + else if (t.contains("Pelicula")) TvType.AnimeMovie + else TvType.Anime + } + } + + override var mainUrl = "https://jkanime.net" + override var name = "JKAnime" + override val lang = "es" + override val hasMainPage = true + override val hasChromecastSupport = true + override val hasDownloadSupport = true + override val supportedTypes = setOf( + TvType.AnimeMovie, + TvType.OVA, + TvType.Anime, + ) + + override suspend fun getMainPage(): HomePageResponse { + val urls = listOf( + Pair("$mainUrl/directorio/?filtro=fecha&tipo=TV&estado=1&fecha=none&temporada=none&orden=desc", "En emisión"), + Pair("$mainUrl/directorio/?filtro=fecha&tipo=none&estado=none&fecha=none&temporada=none&orden=none", "Animes"), + Pair("$mainUrl/directorio/?filtro=fecha&tipo=Movie&estado=none&fecha=none&temporada=none&orden=none", "Películas"), + ) + + val items = ArrayList() + + items.add( + HomePageList( + "Últimos episodios", + app.get(mainUrl).document.select(".listadoanime-home a.bloqq").map { + val title = it.selectFirst("h5")?.text() + val dubstat =if (title!!.contains("Latino") || title.contains("Castellano")) + DubStatus.Dubbed else DubStatus.Subbed + val poster = it.selectFirst(".anime__sidebar__comment__item__pic img")?.attr("src") ?: "" + val epRegex = Regex("/(\\d+)/|/especial/|/ova/") + val url = it.attr("href").replace(epRegex, "") + val epNum = it.selectFirst("h6")?.text()?.replace("Episodio ", "")?.toIntOrNull() + newAnimeSearchResponse(title, url) { + this.posterUrl = poster + addDubStatus(dubstat, epNum) + } + }) + ) + urls.apmap { (url, name) -> + val soup = app.get(url).document + val home = soup.select(".g-0").map { + val title = it.selectFirst("h5 a")?.text() + val poster = it.selectFirst("img")?.attr("src") ?: "" + AnimeSearchResponse( + title!!, + fixUrl(it.selectFirst("a")?.attr("href") ?: ""), + this.name, + TvType.Anime, + fixUrl(poster), + null, + if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of( + DubStatus.Dubbed + ) else EnumSet.of(DubStatus.Subbed), + ) + } + items.add(HomePageList(name, home)) + } + + if (items.size <= 0) throw ErrorLoadingException() + return HomePageResponse(items) + } + + data class MainSearch ( + @JsonProperty("animes") val animes: List, + @JsonProperty("anime_types") val animeTypes: AnimeTypes + ) + + data class Animes ( + @JsonProperty("id") val id: String, + @JsonProperty("slug") val slug: String, + @JsonProperty("title") val title: String, + @JsonProperty("image") val image: String, + @JsonProperty("synopsis") val synopsis: String, + @JsonProperty("type") val type: String, + @JsonProperty("status") val status: String, + @JsonProperty("thumbnail") val thumbnail: String + ) + + data class AnimeTypes ( + @JsonProperty("TV") val TV: String, + @JsonProperty("OVA") val OVA: String, + @JsonProperty("Movie") val Movie: String, + @JsonProperty("Special") val Special: String, + @JsonProperty("ONA") val ONA: String, + @JsonProperty("Music") val Music: String + ) + + override suspend fun search(query: String): List { + val main = app.get("$mainUrl/ajax/ajax_search/?q=$query").text + val json = parseJson(main) + return json.animes.map { + val title = it.title + val href = "$mainUrl/${it.slug}" + val image = "https://cdn.jkanime.net/assets/images/animes/image/${it.slug}.jpg" + AnimeSearchResponse( + title, + href, + this.name, + TvType.Anime, + image, + null, + if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of( + DubStatus.Dubbed + ) else EnumSet.of(DubStatus.Subbed), + ) + } + } + + override suspend fun load(url: String): LoadResponse { + val doc = app.get(url, timeout = 120).document + val poster = doc.selectFirst(".set-bg")?.attr("data-setbg") + val title = doc.selectFirst(".anime__details__title > h3")?.text() + val type = doc.selectFirst(".anime__details__text")?.text() + val description = doc.selectFirst(".anime__details__text > p")?.text() + val genres = doc.select("div.col-lg-6:nth-child(1) > ul:nth-child(1) > li:nth-child(2) > a").map { it.text() } + val status = when (doc.selectFirst("span.enemision")?.text()) { + "En emisión" -> ShowStatus.Ongoing + "Concluido" -> ShowStatus.Completed + else -> null + } + val animeID = doc.selectFirst("div.ml-2")?.attr("data-anime")?.toInt() + val animeeps = "$mainUrl/ajax/last_episode/$animeID/" + val jsoneps = app.get(animeeps).text + val lastepnum = jsoneps.substringAfter("{\"number\":\"").substringBefore("\",\"title\"").toInt() + val episodes = (1..lastepnum).map { + val link = "${url.removeSuffix("/")}/$it" + Episode(link) + } + + return newAnimeLoadResponse(title!!, url, getType(type!!)) { + posterUrl = poster + addEpisodes(DubStatus.Subbed, episodes) + showStatus = status + plot = description + tags = genres + } + } + + data class Nozomi ( + @JsonProperty("file") val file: String? + ) + + private fun streamClean( + name: String, + url: String, + referer: String, + quality: String?, + callback: (ExtractorLink) -> Unit, + m3u8: Boolean + ): Boolean { + callback( + ExtractorLink( + name, + name, + url, + referer, + getQualityFromName(quality), + m3u8 + ) + ) + return true + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + app.get(data).document.select("script").apmap { script -> + if (script.data().contains("var video = []")) { + val videos = script.data().replace("\\/", "/") + fetchUrls(videos).map { + it.replace("$mainUrl/jkfembed.php?u=","https://embedsito.com/v/") + .replace("$mainUrl/jkokru.php?u=","http://ok.ru/videoembed/") + .replace("$mainUrl/jkvmixdrop.php?u=","https://mixdrop.co/e/") + .replace("$mainUrl/jk.php?u=","$mainUrl/") + }.apmap { link -> + loadExtractor(link, data, callback) + if (link.contains("um2.php")) { + val doc = app.get(link, referer = data).document + val gsplaykey = doc.select("form input[value]").attr("value") + val postgsplay = app.post("$mainUrl/gsplay/redirect_post.php", + headers = mapOf( + "Host" to "jkanime.net", + "User-Agent" to USER_AGENT, + "Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", + "Accept-Language" to "en-US,en;q=0.5", + "Referer" to link, + "Content-Type" to "application/x-www-form-urlencoded", + "Origin" to "https://jkanime.net", + "DNT" to "1", + "Connection" to "keep-alive", + "Upgrade-Insecure-Requests" to "1", + "Sec-Fetch-Dest" to "iframe", + "Sec-Fetch-Mode" to "navigate", + "Sec-Fetch-Site" to "same-origin", + "TE" to "trailers", + "Pragma" to "no-cache", + "Cache-Control" to "no-cache",), + data = mapOf(Pair("data",gsplaykey)), + allowRedirects = false).okhttpResponse.headers.values("location").apmap { loc -> + val postkey = loc.replace("/gsplay/player.html#","") + val nozomitext = app.post("$mainUrl/gsplay/api.php", + headers = mapOf( + "Host" to "jkanime.net", + "User-Agent" to USER_AGENT, + "Accept" to "application/json, text/javascript, */*; q=0.01", + "Accept-Language" to "en-US,en;q=0.5", + "Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8", + "X-Requested-With" to "XMLHttpRequest", + "Origin" to "https://jkanime.net", + "DNT" to "1", + "Connection" to "keep-alive", + "Sec-Fetch-Dest" to "empty", + "Sec-Fetch-Mode" to "cors", + "Sec-Fetch-Site" to "same-origin",), + data = mapOf(Pair("v",postkey)), + allowRedirects = false + ).text + val json = parseJson(nozomitext) + val nozomiurl = listOf(json.file) + if (nozomiurl.isEmpty()) null else + nozomiurl.forEach { url -> + val nozominame = "Nozomi" + streamClean(nozominame, url!!, "", null, callback, url.contains(".m3u8")) + } + } + } + if (link.contains("um.php")) { + val desutext = app.get(link, referer = data).text + val desuRegex = Regex("((https:|http:)\\/\\/.*\\.m3u8)") + val file = desuRegex.find(desutext)?.value + val namedesu = "Desu" + generateM3u8( + namedesu, + file!!, + mainUrl, + ).forEach { desurl -> + streamClean(namedesu, desurl.url, mainUrl, desurl.quality.toString(), callback, true) + } + } + if (link.contains("jkmedia")) { + app.get(link, referer = data, allowRedirects = false).okhttpResponse.headers.values("location").apmap { xtremeurl -> + val namex = "Xtreme S" + streamClean(namex, xtremeurl, "", null, callback, xtremeurl.contains(".m3u8")) + } + } + } + } + } + return true + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/MundoDonghuaProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/MundoDonghuaProvider.kt new file mode 100644 index 00000000..19520ae6 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/MundoDonghuaProvider.kt @@ -0,0 +1,217 @@ +package com.lagradost.cloudstream3.animeproviders + +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.module.kotlin.readValue +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.utils.AppUtils.parseJson +import com.lagradost.cloudstream3.utils.AppUtils.toJson +import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8 +import java.util.* +import kotlin.collections.ArrayList + + +class MundoDonghuaProvider : MainAPI() { + + override var mainUrl = "https://www.mundodonghua.com" + override var name = "MundoDonghua" + override val lang = "es" + override val hasMainPage = true + override val hasChromecastSupport = true + override val hasDownloadSupport = true + override val supportedTypes = setOf( + TvType.Anime, + ) + + override suspend fun getMainPage(): HomePageResponse { + val urls = listOf( + Pair("$mainUrl/lista-donghuas", "Donghuas"), + ) + + val items = ArrayList() + items.add( + HomePageList( + "Últimos episodios", + app.get(mainUrl, timeout = 120).document.select("div.row .col-xs-4").map { + val title = it.selectFirst("h5")?.text() ?: "" + val poster = it.selectFirst(".fit-1 img")?.attr("src") + val epRegex = Regex("(\\/(\\d+)\$)") + val url = it.selectFirst("a")?.attr("href")?.replace(epRegex,"")?.replace("/ver/","/donghua/") + val epnumRegex = Regex("((\\d+)$)") + val epNum = epnumRegex.find(title)?.value?.toIntOrNull() + val dubstat = if (title.contains("Latino") || title.contains("Castellano")) DubStatus.Dubbed else DubStatus.Subbed + newAnimeSearchResponse(title.replace(Regex("Episodio|(\\d+)"),"").trim(), fixUrl(url ?: "")) { + this.posterUrl = fixUrl(poster ?: "") + addDubStatus(dubstat, epNum) + } + }) + ) + + urls.apmap { (url, name) -> + val home = app.get(url, timeout = 120).document.select(".col-xs-4").map { + val title = it.selectFirst(".fs-14")?.text() ?: "" + val poster = it.selectFirst(".fit-1 img")?.attr("src") ?: "" + AnimeSearchResponse( + title, + fixUrl(it.selectFirst("a")?.attr("href") ?: ""), + this.name, + TvType.Anime, + fixUrl(poster), + null, + if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of( + DubStatus.Dubbed + ) else EnumSet.of(DubStatus.Subbed), + ) + } + + items.add(HomePageList(name, home)) + } + + if (items.size <= 0) throw ErrorLoadingException() + return HomePageResponse(items) + } + + override suspend fun search(query: String): List { + return app.get("$mainUrl/busquedas/$query", timeout = 120).document.select(".col-xs-4").map { + val title = it.selectFirst(".fs-14")?.text() ?: "" + val href = fixUrl(it.selectFirst("a")?.attr("href") ?: "") + val image = it.selectFirst(".fit-1 img")?.attr("src") + AnimeSearchResponse( + title, + href, + this.name, + TvType.Anime, + fixUrl(image ?: ""), + null, + if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of( + DubStatus.Dubbed + ) else EnumSet.of(DubStatus.Subbed), + ) + } + } + + override suspend fun load(url: String): LoadResponse { + val doc = app.get(url, timeout = 120).document + val poster = doc.selectFirst("head meta[property=og:image]")?.attr("content") ?: "" + val title = doc.selectFirst(".ls-title-serie")?.text() ?: "" + val description = doc.selectFirst("p.text-justify.fc-dark")?.text() ?: "" + val genres = doc.select("span.label.label-primary.f-bold").map { it.text() } + val status = when (doc.selectFirst("div.col-md-6.col-xs-6.align-center.bg-white.pt-10.pr-15.pb-0.pl-15 p span.badge.bg-default")?.text()) { + "En Emisión" -> ShowStatus.Ongoing + "Finalizada" -> ShowStatus.Completed + else -> null + } + val episodes = doc.select("ul.donghua-list a").map { + val name = it.selectFirst(".fs-16")?.text() + val link = it.attr("href") + Episode(fixUrl(link), name) + }.reversed() + val typeinfo = doc.select("div.row div.col-md-6.pl-15 p.fc-dark").text() + val tvType = if (typeinfo.contains(Regex("Tipo.*Pel.cula"))) TvType.AnimeMovie else TvType.Anime + return newAnimeLoadResponse(title, url, tvType) { + posterUrl = poster + addEpisodes(DubStatus.Subbed, episodes) + showStatus = status + plot = description + tags = genres + } + } + data class Protea ( + @JsonProperty("source") val source: List, + @JsonProperty("poster") val poster: String? + ) + + data class Source ( + @JsonProperty("file") val file: String, + @JsonProperty("label") val label: String?, + @JsonProperty("type") val type: String?, + @JsonProperty("default") val default: String? + ) + + private fun cleanStream( + name: String, + url: String, + qualityString: String?, + callback: (ExtractorLink) -> Unit, + isM3U8: Boolean + ): Boolean { + callback( + ExtractorLink( + name, + name, + url, + "", + getQualityFromName(qualityString), + isM3U8 + ) + ) + return true + } + + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + app.get(data).document.select("script").apmap { script -> + if (script.data().contains("eval(function(p,a,c,k,e")) { + val packedRegex = Regex("eval\\(function\\(p,a,c,k,e,.*\\)\\)") + packedRegex.findAll(script.data()).map { + it.value + }.toList().apmap { + val unpack = getAndUnpack(it).replace("diasfem","embedsito") + fetchUrls(unpack).apmap { url -> + loadExtractor(url, data, callback) + } + if (unpack.contains("protea_tab")) { + val protearegex = Regex("(protea_tab.*slug.*,type)") + val slug = protearegex.findAll(unpack).map { + it.value.replace(Regex("(protea_tab.*slug\":\")"),"").replace("\"},type","") + }.first() + val requestlink = "$mainUrl/api_donghua.php?slug=$slug" + val response = app.get(requestlink, headers = + mapOf("Host" to "www.mundodonghua.com", + "User-Agent" to USER_AGENT, + "Accept" to "*/*", + "Accept-Language" to "en-US,en;q=0.5", + "Referer" to data, + "X-Requested-With" to "XMLHttpRequest", + "DNT" to "1", + "Connection" to "keep-alive", + "Sec-Fetch-Dest" to "empty", + "Sec-Fetch-Mode" to "no-cors", + "Sec-Fetch-Site" to "same-origin", + "TE" to "trailers", + "Pragma" to "no-cache", + "Cache-Control" to "no-cache",) + ).text.removePrefix("[").removeSuffix("]") + val json = parseJson(response) + json.source.forEach { source -> + val protename = "Protea" + cleanStream(protename, fixUrl(source.file), source.label, callback, false) + } + } + if (unpack.contains("asura_player")) { + val asuraRegex = Regex("(asura_player.*type)") + asuraRegex.findAll(unpack).map { + it.value + }.toList().apmap { protea -> + val asuraname = "Asura" + val file = protea.substringAfter("{file:\"").substringBefore("\"") + generateM3u8( + asuraname, + file, + "" + ).forEach { + cleanStream(asuraname, it.url, it.quality.toString(), callback, true) + } + } + } + } + } + } + return true + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/DoramasYTProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/DoramasYTProvider.kt index 6d2bde57..ab907aba 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/DoramasYTProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/DoramasYTProvider.kt @@ -28,8 +28,7 @@ class DoramasYTProvider : MainAPI() { override val hasChromecastSupport = true override val hasDownloadSupport = true override val supportedTypes = setOf( - TvType.TvSeries, - TvType.Movie, + TvType.AsianDrama, ) override suspend fun getMainPage(): HomePageResponse { diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/ElifilmsProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/ElifilmsProvider.kt new file mode 100644 index 00000000..58167214 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/ElifilmsProvider.kt @@ -0,0 +1,90 @@ +package com.lagradost.cloudstream3.movieproviders + +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* +import kotlin.collections.ArrayList + +class ElifilmsProvider:MainAPI() { + override var mainUrl: String = "https://elifilms.net" + override var name: String = "Elifilms" + override val lang = "es" + override val hasMainPage = true + override val hasChromecastSupport = true + override val hasDownloadSupport = true + override val supportedTypes = setOf( + TvType.Movie, + ) + override suspend fun getMainPage(): HomePageResponse { + val items = ArrayList() + val newest = app.get(mainUrl).document.selectFirst("a.fav_link.premiera")?.attr("href") + val urls = listOf( + Pair(mainUrl, "Películas recientes"), + Pair("$mainUrl/4k-peliculas/", "Películas en 4k"), + Pair(newest, "Últimos estrenos"), + ) + urls.apmap { (url, name) -> + val soup = app.get(url ?: "").document + val home = soup.select("article.shortstory.cf").map { + val title = it.selectFirst(".short_header")?.text() ?: "" + val link = it.selectFirst("div a")?.attr("href") ?: "" + TvSeriesSearchResponse( + title, + link, + this.name, + TvType.Movie, + it.selectFirst("a.ah-imagge img")?.attr("data-src"), + null, + null, + ) + } + items.add(HomePageList(name, home)) + } + if (items.size <= 0) throw ErrorLoadingException() + return HomePageResponse(items) + } + override suspend fun search(query: String): List { + val url = "$mainUrl/?s=$query" + val doc = app.get(url).document + return doc.select("article.cf").map { + val href = it.selectFirst("div.short_content a")?.attr("href") ?: "" + val poster = it.selectFirst("a.ah-imagge img")?.attr("data-src") + val name = it.selectFirst(".short_header")?.text() ?: "" + (MovieSearchResponse(name, href, this.name, TvType.Movie, poster, null)) + } + } + override suspend fun load(url: String): LoadResponse { + val document = app.get(url, timeout = 120).document + val title = document.selectFirst(".post_title h1")?.text() ?: "" + val rating = document.select("span.imdb.rki").toString().toIntOrNull() + val poster = document.selectFirst(".poster img")?.attr("src") + val desc = document.selectFirst("div.notext .actors p")?.text() + val tags = document.select("td.notext a") + .map { it?.text()?.trim().toString() } + return MovieLoadResponse( + title, + url, + this.name, + TvType.Movie, + url, + poster, + null, + desc, + rating, + tags + ) + } + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + app.get(data).document.select("li.change-server a").apmap { + val encodedurl = it.attr("data-id") + val urlDecoded = base64Decode(encodedurl) + val url = fixUrl(urlDecoded) + loadExtractor(url, data, callback) + } + return true + } +} diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/EstrenosDoramasProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/EstrenosDoramasProvider.kt new file mode 100644 index 00000000..b02f88fa --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/EstrenosDoramasProvider.kt @@ -0,0 +1,286 @@ +package com.lagradost.cloudstream3.movieproviders + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.network.WebViewResolver +import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.utils.AppUtils.parseJson +import java.util.* +import kotlin.collections.ArrayList + + +class EstrenosDoramasProvider : MainAPI() { + companion object { + fun getType(t: String): TvType { + return if (t.contains("OVA") || t.contains("Especial")) TvType.OVA + else if (t.contains("Pelicula")) TvType.Movie + else TvType.TvSeries + } + } + + override var mainUrl = "https://www23.estrenosdoramas.net" + override var name = "EstrenosDoramas" + override val lang = "es" + override val hasMainPage = true + override val hasChromecastSupport = true + override val hasDownloadSupport = true + override val supportedTypes = setOf( + TvType.AsianDrama, + ) + + override suspend fun getMainPage(): HomePageResponse { + val urls = listOf( + Pair(mainUrl, "Últimas series"), + Pair("$mainUrl/category/peliculas", "Películas"), + ) + + val items = ArrayList() + + urls.apmap { (url, name) -> + val home = app.get(url, timeout = 120).document.select("div.clearfix").map { + val title = cleanTitle(it.selectFirst("h3 a")?.text()!!) + val poster = it.selectFirst("img.cate_thumb")?.attr("src") + AnimeSearchResponse( + title, + it.selectFirst("a")?.attr("href")!!, + this.name, + TvType.AsianDrama, + poster, + null, + if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of( + DubStatus.Dubbed + ) else EnumSet.of(DubStatus.Subbed), + ) + } + items.add(HomePageList(name, home)) + } + + if (items.size <= 0) throw ErrorLoadingException() + return HomePageResponse(items) + } + + override suspend fun search(query: String): List { + val searchob = ArrayList() + val search = + app.get("$mainUrl/?s=$query", timeout = 120).document.select("div.clearfix").map { + val title = cleanTitle(it.selectFirst("h3 a")?.text()!!) + val href = it.selectFirst("a")?.attr("href") + val image = it.selectFirst("img.cate_thumb")?.attr("src") + val lists = + AnimeSearchResponse( + title, + href!!, + this.name, + TvType.AsianDrama, + image, + null, + if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of( + DubStatus.Dubbed + ) else EnumSet.of(DubStatus.Subbed), + ) + if (href.contains("capitulo")) { + //nothing + } + else { + searchob.add(lists) + } + } + return searchob + } + + override suspend fun load(url: String): LoadResponse? { + val doc = app.get(url, timeout = 120).document + val poster = doc.selectFirst("head meta[property]")?.attr("content") + val title = doc.selectFirst("h1.titulo")?.text() + val description = try { + doc.selectFirst("div.post div.highlight div.font")?.text() + } catch (e:Exception){ + null + } + val finaldesc = description?.substringAfter("Sinopsis")?.replace(": ", "")?.trim() + val epi = ArrayList() + val episodes = doc.select("div.post .lcp_catlist a").map { + val name = it.selectFirst("a")?.text() + val link = it.selectFirst("a")?.attr("href") + val test = Episode(link!!, name) + if (!link.equals(url)) { + epi.add(test) + } + }.reversed() + return when (val type = if (episodes.isEmpty()) TvType.Movie else TvType.AsianDrama) { + TvType.AsianDrama -> { + return newAnimeLoadResponse(title!!, url, type) { + japName = null + engName = title.replace(Regex("[Pp]elicula |[Pp]elicula"),"") + posterUrl = poster + addEpisodes(DubStatus.Subbed, epi.reversed()) + plot = finaldesc + } + } + TvType.Movie -> { + MovieLoadResponse( + cleanTitle(title!!), + url, + this.name, + TvType.Movie, + url, + poster, + null, + finaldesc, + null, + null, + ) + } + else -> null + } + + } + + + + data class ReproDoramas ( + @JsonProperty("link") val link: String, + @JsonProperty("time") val time: Int + ) + + private fun cleanTitle(title: String): String = title.replace(Regex("[Pp]elicula |[Pp]elicula"),"") + + private fun cleanExtractor( + source: String, + name: String, + url: String, + referer: String, + m3u8: Boolean, + callback: (ExtractorLink) -> Unit + ): Boolean { + callback( + ExtractorLink( + source, + name, + url, + referer, + Qualities.Unknown.value, + m3u8 + ) + ) + return true + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + val headers = mapOf("Host" to "repro3.estrenosdoramas.us", + "User-Agent" to USER_AGENT, + "Accept" to "*/*", + "Accept-Language" to "en-US,en;q=0.5", + "Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8", + "X-Requested-With" to "XMLHttpRequest", + "Origin" to "https://repro3.estrenosdoramas.us", + "DNT" to "1", + "Connection" to "keep-alive", + "Sec-Fetch-Dest" to "empty", + "Sec-Fetch-Mode" to "cors", + "Sec-Fetch-Site" to "same-origin", + "Cache-Control" to "max-age=0",) + + val document = app.get(data).document + document.select("div.tab_container iframe").apmap { container -> + val directlink = fixUrl(container.attr("src")) + loadExtractor(directlink, data, callback) + + if (directlink.contains("/repro/amz/")) { + val amzregex = Regex("https:\\/\\/repro3\\.estrenosdoramas\\.us\\/repro\\/amz\\/examples\\/.*\\.php\\?key=.*\$") + amzregex.findAll(directlink).map { + it.value.replace(Regex("https:\\/\\/repro3\\.estrenosdoramas\\.us\\/repro\\/amz\\/examples\\/.*\\.php\\?key="),"") + }.toList().apmap { key -> + val response = app.post("https://repro3.estrenosdoramas.us/repro/amz/examples/player/api/indexDCA.php", + headers = headers, + data = mapOf( + Pair("key",key), + Pair("token","MDAwMDAwMDAwMA=="), + ), + allowRedirects = false + ).text + val reprojson = parseJson(response) + val decodeurl = base64Decode(reprojson.link) + if (decodeurl.contains("m3u8")) + + cleanExtractor( + name, + name, + decodeurl, + "https://repro3.estrenosdoramas.us", + decodeurl.contains(".m3u8"), + callback + ) + } + } + + + if (directlink.contains("reproducir14")) { + val regex = Regex("(https:\\/\\/repro.\\.estrenosdoramas\\.us\\/repro\\/reproducir14\\.php\\?key=[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)") + regex.findAll(directlink).map { + it.value + }.toList().apmap { + val doc = app.get(it).text + val videoid = doc.substringAfter("vid=\"").substringBefore("\" n") + val token = doc.substringAfter("name=\"").substringBefore("\" s") + val acctkn = doc.substringAfter("{ acc: \"").substringBefore("\", id:") + val link = app.post("https://repro3.estrenosdoramas.us/repro/proto4.php", + headers = headers, + data = mapOf( + Pair("acc",acctkn), + Pair("id",videoid), + Pair("tk",token)), + allowRedirects = false + ).text + val extracteklink = link.substringAfter("\"urlremoto\":\"").substringBefore("\"}") + .replace("\\/", "/").replace("//ok.ru/","http://ok.ru/") + loadExtractor(extracteklink, data, callback) + } + } + + if (directlink.contains("reproducir120")) { + val regex = Regex("(https:\\/\\/repro3.estrenosdoramas.us\\/repro\\/reproducir120\\.php\\?\\nkey=[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)") + regex.findAll(directlink).map { + it.value + }.toList().apmap { + val doc = app.get(it).text + val videoid = doc.substringAfter("var videoid = '").substringBefore("';") + val token = doc.substringAfter("var tokens = '").substringBefore("';") + val acctkn = doc.substringAfter("{ acc: \"").substringBefore("\", id:") + val link = app.post("https://repro3.estrenosdoramas.us/repro/api3.php", + headers = headers, + data = mapOf( + Pair("acc",acctkn), + Pair("id",videoid), + Pair("tk",token)), + allowRedirects = false + ).text + val extractedlink = link.substringAfter("\"{file:'").substringBefore("',label:") + .replace("\\/", "/") + val quality = link.substringAfter(",label:'").substringBefore("',type:") + val type = link.substringAfter("type: '").substringBefore("'}\"") + if (extractedlink.isNotBlank()) + if (quality.contains("File not found", ignoreCase = true)) { + //Nothing + } else { + cleanExtractor( + "Movil", + "Movil $quality", + extractedlink, + "", + !type.contains("mp4"), + callback + ) + } + } + } + } + + return true + } +} diff --git a/docs/providers.json b/docs/providers.json index 1ed54baa..1331d58e 100644 --- a/docs/providers.json +++ b/docs/providers.json @@ -41,6 +41,18 @@ "status": 1, "url": "https://www.animeworld.tv" }, + "AnimefenixProvider": { + "language": "es", + "name": "Animefenix", + "status": 1, + "url": "https://animefenix.com" + }, + "AnimeflvIOProvider": { + "language": "es", + "name": "Animeflv.io", + "status": 1, + "url": "https://animeflv.io" + }, "AnimeflvnetProvider": { "language": "es", "name": "Animeflv.net", @@ -137,6 +149,12 @@ "status": 1, "url": "https://www.egy.best" }, + "ElifilmsProvider": { + "language": "es", + "name": "Elifilms", + "status": 1, + "url": "https://elifilms.net" + }, "EntrePeliculasySeriesProvider": { "name": "EntrePeliculasySeries", "status": 1, @@ -148,6 +166,12 @@ "status": 1, "url": "https://entrepeliculasyseries.nu" }, + "EstrenosDoramasProvider": { + "language": "es", + "name": "EstrenosDoramas", + "status": 1, + "url": "https://www23.estrenosdoramas.net" + }, "FaselHDProvider": { "language": "ar", "name": "FaselHD", @@ -202,6 +226,12 @@ "status": 1, "url": "https://ihavenotv.com" }, + "JKAnimeProvider": { + "language": "es", + "name": "JKAnime", + "status": 1, + "url": "https://jkanime.net" + }, "KawaiifuProvider": { "language": "en", "name": "Kawaiifu", @@ -254,6 +284,12 @@ "status": 1, "url": "https://monoschinos2.com" }, + "MundoDonghuaProvider": { + "language": "es", + "name": "MundoDonghua", + "status": 1, + "url": "https://www.mundodonghua.com" + }, "MyCimaProvider": { "language": "ar", "name": "MyCima",