diff --git a/AnimefenixProvider/build.gradle.kts b/AnimefenixProvider/build.gradle.kts
new file mode 100644
index 0000000..0266db8
--- /dev/null
+++ b/AnimefenixProvider/build.gradle.kts
@@ -0,0 +1,26 @@
+// use an integer for version numbers
+version = 1
+
+
+cloudstream {
+ // All of these properties are optional, you can safely remove them
+
+ // description = "Lorem Ipsum"
+ // authors = listOf("Cloudburst")
+
+ /**
+ * Status int as the following:
+ * 0: Down
+ * 1: Ok
+ * 2: Slow
+ * 3: Beta only
+ * */
+ status = 1 // will be 3 if unspecified
+ tvTypes = listOf(
+ "AnimeMovie",
+ "OVA",
+ "Anime",
+ )
+
+ iconUrl = "https://www.google.com/s2/favicons?domain=animefenix.com&sz=24"
+}
\ No newline at end of file
diff --git a/AnimefenixProvider/src/main/AndroidManifest.xml b/AnimefenixProvider/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..29aec9d
--- /dev/null
+++ b/AnimefenixProvider/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/AnimefenixProvider/src/main/kotlin/com/lagradost/AnimefenixProvider.kt b/AnimefenixProvider/src/main/kotlin/com/lagradost/AnimefenixProvider.kt
new file mode 100644
index 0000000..203fa8d
--- /dev/null
+++ b/AnimefenixProvider/src/main/kotlin/com/lagradost/AnimefenixProvider.kt
@@ -0,0 +1,249 @@
+package com.lagradost
+
+import com.fasterxml.jackson.annotation.JsonProperty
+import com.lagradost.cloudstream3.*
+import com.lagradost.cloudstream3.utils.AppUtils.parseJson
+import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.Qualities
+import com.lagradost.cloudstream3.utils.loadExtractor
+import org.jsoup.Jsoup
+import java.util.*
+
+
+class AnimefenixProvider:MainAPI() {
+
+ override var mainUrl = "https://animefenix.com"
+ override var name = "Animefenix"
+ override var 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(page: Int, request : MainPageRequest): 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, subtitleCallback, 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/AnimefenixProvider/src/main/kotlin/com/lagradost/AnimefenixProviderPlugin.kt b/AnimefenixProvider/src/main/kotlin/com/lagradost/AnimefenixProviderPlugin.kt
new file mode 100644
index 0000000..459bd29
--- /dev/null
+++ b/AnimefenixProvider/src/main/kotlin/com/lagradost/AnimefenixProviderPlugin.kt
@@ -0,0 +1,14 @@
+
+package com.lagradost
+
+import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
+import com.lagradost.cloudstream3.plugins.Plugin
+import android.content.Context
+
+@CloudstreamPlugin
+class AnimefenixProviderPlugin: Plugin() {
+ override fun load(context: Context) {
+ // All providers should be added in this manner. Please don't edit the providers list directly.
+ registerMainAPI(AnimefenixProvider())
+ }
+}
\ No newline at end of file
diff --git a/AnimeflvIOProvider/build.gradle.kts b/AnimeflvIOProvider/build.gradle.kts
new file mode 100644
index 0000000..67fe108
--- /dev/null
+++ b/AnimeflvIOProvider/build.gradle.kts
@@ -0,0 +1,26 @@
+// use an integer for version numbers
+version = 1
+
+
+cloudstream {
+ // All of these properties are optional, you can safely remove them
+
+ // description = "Lorem Ipsum"
+ // authors = listOf("Cloudburst")
+
+ /**
+ * Status int as the following:
+ * 0: Down
+ * 1: Ok
+ * 2: Slow
+ * 3: Beta only
+ * */
+ status = 1 // will be 3 if unspecified
+ tvTypes = listOf(
+ "AnimeMovie",
+ "Anime",
+ "OVA",
+ )
+
+ iconUrl = "https://www.google.com/s2/favicons?domain=animeflv.io&sz=24"
+}
\ No newline at end of file
diff --git a/AnimeflvIOProvider/src/main/AndroidManifest.xml b/AnimeflvIOProvider/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..29aec9d
--- /dev/null
+++ b/AnimeflvIOProvider/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/AnimeflvIOProvider/src/main/kotlin/com/lagradost/AnimeflvIOProvider.kt b/AnimeflvIOProvider/src/main/kotlin/com/lagradost/AnimeflvIOProvider.kt
new file mode 100644
index 0000000..5b74053
--- /dev/null
+++ b/AnimeflvIOProvider/src/main/kotlin/com/lagradost/AnimeflvIOProvider.kt
@@ -0,0 +1,239 @@
+package com.lagradost
+
+import com.fasterxml.jackson.annotation.JsonProperty
+import com.lagradost.cloudstream3.*
+import com.lagradost.cloudstream3.utils.AppUtils.parseJson
+import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8
+import com.lagradost.cloudstream3.utils.Qualities
+import com.lagradost.cloudstream3.utils.getQualityFromName
+import com.lagradost.cloudstream3.utils.loadExtractor
+import java.util.*
+
+class AnimeflvIOProvider : MainAPI() {
+ override var mainUrl = "https://animeflv.io" //Also scrapes from animeid.to
+ override var name = "Animeflv.io"
+ override var 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(page: Int, request: MainPageRequest): 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