diff --git a/AnimeIndoProvider/build.gradle.kts b/AnimeIndoProvider/build.gradle.kts
new file mode 100644
index 00000000..22c29da9
--- /dev/null
+++ b/AnimeIndoProvider/build.gradle.kts
@@ -0,0 +1,27 @@
+// use an integer for version numbers
+version = 1
+
+
+cloudstream {
+ language = "id"
+ // All of these properties are optional, you can safely remove them
+
+ // description = "Lorem Ipsum"
+ authors = listOf("Hexated")
+
+ /**
+ * 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=animeindo.sbs&sz=%size%"
+}
\ No newline at end of file
diff --git a/AnimeIndoProvider/src/main/AndroidManifest.xml b/AnimeIndoProvider/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..874740e3
--- /dev/null
+++ b/AnimeIndoProvider/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/AnimeIndoProvider/src/main/kotlin/com/hexated/AnimeIndoProvider.kt b/AnimeIndoProvider/src/main/kotlin/com/hexated/AnimeIndoProvider.kt
new file mode 100644
index 00000000..6957da14
--- /dev/null
+++ b/AnimeIndoProvider/src/main/kotlin/com/hexated/AnimeIndoProvider.kt
@@ -0,0 +1,192 @@
+package com.hexated
+
+import com.lagradost.cloudstream3.*
+import com.lagradost.cloudstream3.APIHolder.getCaptchaToken
+import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
+import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.loadExtractor
+import com.lagradost.nicehttp.NiceResponse
+import org.jsoup.Jsoup
+import org.jsoup.nodes.Element
+
+class AnimeIndoProvider : MainAPI() {
+ override var mainUrl = "https://animeindo.sbs"
+ override var name = "AnimeIndo"
+ override val hasMainPage = true
+ override var lang = "id"
+ override val hasDownloadSupport = true
+
+ override val supportedTypes = setOf(
+ TvType.Anime,
+ TvType.AnimeMovie,
+ TvType.OVA
+ )
+
+ companion object {
+ fun getType(t: String): TvType {
+ return 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) {
+ "Finished Airing" -> ShowStatus.Completed
+ "Currently Airing" -> ShowStatus.Ongoing
+ else -> ShowStatus.Completed
+ }
+ }
+
+ private suspend fun request(url: String): NiceResponse {
+ val req = app.get(
+ url,
+ cookies = mapOf("recaptcha_cookie" to "#Asia/Jakarta#-420#win32#Windows#0,false,false#Google Inc. (Intel)~ANGLE (Intel, Intel(R) HD Graphics 400 Direct3D11 vs_5_0 ps_5_0)")
+ )
+ if (req.isSuccessful) {
+ return req
+ } else {
+ val document = app.get(url).document
+ val captchaKey =
+ document.select("script[src*=https://www.google.com/recaptcha/api.js?render=]")
+ .attr("src").substringAfter("render=").substringBefore("&")
+ val token = getCaptchaToken(url, captchaKey)
+ return app.post(
+ url,
+ data = mapOf(
+ "action" to "recaptcha_for_all",
+ "token" to "$token",
+ "sitekey" to captchaKey
+ )
+ )
+ }
+ }
+ }
+
+ override val mainPage = mainPageOf(
+ "$mainUrl/anime-terbaru/page/" to "Anime Terbaru",
+ "$mainUrl/donghua-terbaru/page/" to "Donghua Terbaru"
+ )
+
+ override suspend fun getMainPage(
+ page: Int,
+ request: MainPageRequest
+ ): HomePageResponse {
+ val document = request(request.data + page).document
+ val home = document.select("div.post-show > article").mapNotNull {
+ it.toSearchResult()
+ }
+ return newHomePageResponse(request.name, home)
+ }
+
+ 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("(.+)-episode").find(
+ title
+ )?.groupValues?.get(1).toString()
+ (title.contains("-movie")) -> Regex("(.+)-movie").find(title)?.groupValues?.get(
+ 1
+ ).toString()
+ else -> title
+ }
+ "$mainUrl/anime/$title"
+ }
+ }
+
+ private fun Element.toSearchResult(): AnimeSearchResponse? {
+ val title = this.selectFirst("div.title")?.text()?.trim() ?: return null
+ val href = getProperAnimeLink(this.selectFirst("a")!!.attr("href"))
+ val posterUrl = this.select("img[itemprop=image]").attr("src").toString()
+ val type = getType(this.select("div.type").text().trim())
+ val epNum =
+ this.selectFirst("span.episode")?.ownText()?.replace(Regex("[^0-9]"), "")?.trim()
+ ?.toIntOrNull()
+ return newAnimeSearchResponse(title, href, type) {
+ this.posterUrl = posterUrl
+ addSub(epNum)
+ }
+
+ }
+
+ override suspend fun search(query: String): List {
+ val link = "$mainUrl/?s=$query"
+ val document = request(link).document
+
+ return document.select(".site-main.relat > article").map {
+ val title = it.selectFirst("div.title > h2")!!.ownText().trim()
+ val href = it.selectFirst("a")!!.attr("href")
+ val posterUrl = it.selectFirst("img")!!.attr("src").toString()
+ val type = getType(it.select("div.type").text().trim())
+ newAnimeSearchResponse(title, href, type) {
+ this.posterUrl = posterUrl
+ }
+ }
+ }
+
+ override suspend fun load(url: String): LoadResponse {
+ val document = request(url).document
+
+ val title = document.selectFirst("h1.entry-title")?.text().toString().trim()
+ val poster = document.selectFirst("div.thumb > img[itemprop=image]")?.attr("src")
+ val tags = document.select("div.genxed > a").map { it.text() }
+ val type = getType(
+ document.selectFirst("div.info-content > div.spe > span:nth-child(6)")?.ownText()
+ .toString()
+ )
+ val year = Regex("\\d, ([0-9]*)").find(
+ document.select("div.info-content > div.spe > span:nth-child(9) > time").text()
+ )?.groupValues?.get(1)?.toIntOrNull()
+ val status = getStatus(
+ document.selectFirst("div.info-content > div.spe > span:nth-child(1)")!!.ownText()
+ .trim()
+ )
+ val description = document.select("div[itemprop=description] > p").text()
+ val trailer = document.selectFirst("div.player-embed iframe")?.attr("src")
+ val episodes = document.select("div.lstepsiode.listeps ul li").mapNotNull {
+ val header = it.selectFirst("span.lchx > a") ?: return@mapNotNull null
+ val name = header.text().trim()
+ val episode = header.text().trim().replace("Episode", "").trim().toIntOrNull()
+ val link = fixUrl(header.attr("href"))
+ Episode(link, name = name, episode = episode)
+ }.reversed()
+
+ return newAnimeLoadResponse(title, url, type) {
+ engName = title
+ posterUrl = poster
+ this.year = year
+ addEpisodes(DubStatus.Subbed, episodes)
+ showStatus = status
+ plot = description
+ this.tags = tags
+ addTrailer(trailer)
+ }
+ }
+
+ override suspend fun loadLinks(
+ data: String,
+ isCasting: Boolean,
+ subtitleCallback: (SubtitleFile) -> Unit,
+ callback: (ExtractorLink) -> Unit
+ ): Boolean {
+
+ val document = request(data).document
+ document.select("div.itemleft > .mirror > option").mapNotNull {
+ fixUrl(Jsoup.parse(base64Decode(it.attr("value"))).select("iframe").attr("src"))
+ }.apmap {
+ if (it.startsWith("https://uservideo.xyz")) {
+ app.get(it, referer = "$mainUrl/").document.select("iframe").attr("src")
+ } else {
+ it
+ }
+ }.apmap {
+ loadExtractor(it, data, subtitleCallback, callback)
+ }
+
+ return true
+ }
+
+
+}
\ No newline at end of file
diff --git a/AnimeIndoProvider/src/main/kotlin/com/hexated/AnimeIndoProviderPlugin.kt b/AnimeIndoProvider/src/main/kotlin/com/hexated/AnimeIndoProviderPlugin.kt
new file mode 100644
index 00000000..7f4d0a0d
--- /dev/null
+++ b/AnimeIndoProvider/src/main/kotlin/com/hexated/AnimeIndoProviderPlugin.kt
@@ -0,0 +1,14 @@
+
+package com.hexated
+
+import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
+import com.lagradost.cloudstream3.plugins.Plugin
+import android.content.Context
+
+@CloudstreamPlugin
+class AnimeIndoProviderPlugin: Plugin() {
+ override fun load(context: Context) {
+ // All providers should be added in this manner. Please don't edit the providers list directly.
+ registerMainAPI(AnimeIndoProvider())
+ }
+}
\ No newline at end of file
diff --git a/AnimeSailProvider/build.gradle.kts b/AnimeSailProvider/build.gradle.kts
new file mode 100644
index 00000000..7fcac9c3
--- /dev/null
+++ b/AnimeSailProvider/build.gradle.kts
@@ -0,0 +1,27 @@
+// use an integer for version numbers
+version = 2
+
+
+cloudstream {
+ language = "id"
+ // All of these properties are optional, you can safely remove them
+
+ // description = "Lorem Ipsum"
+ authors = listOf("Hexated")
+
+ /**
+ * 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=111.90.143.42&sz=%size%"
+}
\ No newline at end of file
diff --git a/AnimeSailProvider/src/main/AndroidManifest.xml b/AnimeSailProvider/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..874740e3
--- /dev/null
+++ b/AnimeSailProvider/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/AnimeSailProvider/src/main/kotlin/com/hexated/AnimeSailProvider.kt b/AnimeSailProvider/src/main/kotlin/com/hexated/AnimeSailProvider.kt
new file mode 100644
index 00000000..fc0e11ca
--- /dev/null
+++ b/AnimeSailProvider/src/main/kotlin/com/hexated/AnimeSailProvider.kt
@@ -0,0 +1,195 @@
+package com.hexated
+
+import com.lagradost.cloudstream3.*
+import com.lagradost.cloudstream3.mvvm.safeApiCall
+import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.Qualities
+import com.lagradost.cloudstream3.utils.loadExtractor
+import com.lagradost.nicehttp.NiceResponse
+import org.jsoup.Jsoup
+import org.jsoup.nodes.Element
+
+class AnimeSailProvider : MainAPI() {
+ override var mainUrl = "https://111.90.143.42"
+ override var name = "AnimeSail"
+ override val hasMainPage = true
+ override var lang = "id"
+ override val hasDownloadSupport = true
+
+ override val supportedTypes = setOf(
+ TvType.Anime,
+ TvType.AnimeMovie,
+ TvType.OVA
+ )
+
+ companion object {
+ fun getType(t: String): TvType {
+ return 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 suspend fun request(url: String, ref: String? = null): NiceResponse {
+ return app.get(
+ url,
+ headers = mapOf("Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"),
+ cookies = mapOf("_as_ipin_ct" to "ID"),
+ referer = ref
+ )
+ }
+
+ override val mainPage = mainPageOf(
+ "$mainUrl/page/" to "Episode Terbaru",
+ "$mainUrl/movie-terbaru/page/" to "Movie Terbaru",
+ "$mainUrl/genres/donghua/page/" to "Donghua"
+ )
+
+ override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
+ val document = request(request.data + page).document
+ val home = document.select("article").map {
+ it.toSearchResult()
+ }
+ return newHomePageResponse(request.name, home)
+ }
+
+ 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")) -> title.substringBefore(
+ "-episode"
+ )
+ (title.contains("-movie")) -> title.substringBefore("-movie")
+ else -> title
+ }
+
+ "$mainUrl/anime/$title"
+ }
+ }
+
+ private fun Element.toSearchResult(): AnimeSearchResponse {
+ val href = getProperAnimeLink(fixUrlNull(this.selectFirst("a")?.attr("href")).toString())
+ val title = this.select(".tt > h2").text().trim()
+ val posterUrl = fixUrlNull(this.selectFirst("div.limit img")?.attr("src"))
+ val epNum = this.selectFirst(".tt > h2")?.text()?.let {
+ Regex("Episode\\s?([0-9]+)").find(it)?.groupValues?.getOrNull(1)?.toIntOrNull()
+ }
+ return newAnimeSearchResponse(title, href, TvType.Anime) {
+ this.posterUrl = posterUrl
+ addSub(epNum)
+ }
+
+ }
+
+ override suspend fun search(query: String): List {
+ val link = "$mainUrl/?s=$query"
+ val document = request(link).document
+
+ return document.select("div.listupd article").map {
+ it.toSearchResult()
+ }
+ }
+
+ override suspend fun load(url: String): LoadResponse {
+ val document = request(url).document
+
+ val title = document.selectFirst("h1.entry-title")?.text().toString().trim()
+ val type = getType(
+ document.select("tbody th:contains(Tipe)").next().text()
+ )
+ val episodes = document.select("ul.daftar > li").map {
+ val header = it.select("a").text().trim()
+ val name =
+ Regex("(Episode\\s?[0-9]+)").find(header)?.groupValues?.getOrNull(0) ?: header
+ val link = fixUrl(it.select("a").attr("href"))
+ Episode(link, name = name)
+ }.reversed()
+
+ return newAnimeLoadResponse(title, url, type) {
+ posterUrl = document.selectFirst("div.entry-content > img")?.attr("src")
+ this.year =
+ document.select("tbody th:contains(Dirilis)").next().text().trim().toIntOrNull()
+ addEpisodes(DubStatus.Subbed, episodes)
+ showStatus =
+ getStatus(document.select("tbody th:contains(Status)").next().text().trim())
+ plot = document.selectFirst("div.entry-content > p")?.text()
+ this.tags =
+ document.select("tbody th:contains(Genre)").next().select("a").map { it.text() }
+ }
+ }
+
+ override suspend fun loadLinks(
+ data: String,
+ isCasting: Boolean,
+ subtitleCallback: (SubtitleFile) -> Unit,
+ callback: (ExtractorLink) -> Unit
+ ): Boolean {
+
+ val document = request(data).document
+
+ document.select(".mobius > .mirror > option").apmap {
+ safeApiCall {
+ val iframe = fixUrl(
+ Jsoup.parse(base64Decode(it.attr("data-em"))).select("iframe").attr("src")
+ ?: throw ErrorLoadingException("No iframe found")
+ )
+
+ when {
+ iframe.startsWith("$mainUrl/utils/player/arch/") || iframe.startsWith(
+ "$mainUrl/utils/player/race/"
+ ) -> request(iframe, ref = data).document.select("source").attr("src")
+ .let { link ->
+ val source =
+ when {
+ iframe.contains("/arch/") -> "Arch"
+ iframe.contains("/race/") -> "Race"
+ else -> this.name
+ }
+ val quality =
+ Regex("\\.([0-9]{3,4})\\.").find(link)?.groupValues?.get(1)
+ callback.invoke(
+ ExtractorLink(
+ source = source,
+ name = source,
+ url = link,
+ referer = mainUrl,
+ quality = quality?.toIntOrNull() ?: Qualities.Unknown.value
+ )
+ )
+ }
+// skip for now
+// iframe.startsWith("$mainUrl/utils/player/fichan/") -> ""
+// iframe.startsWith("$mainUrl/utils/player/blogger/") -> ""
+ iframe.startsWith("https://aghanim.xyz/tools/redirect/") -> {
+ val link = "https://rasa-cintaku-semakin-berantai.xyz/v/${iframe.substringAfter("id=").substringBefore("&token")}"
+ loadExtractor(link, mainUrl, subtitleCallback, callback)
+ }
+ iframe.startsWith("$mainUrl/utils/player/framezilla/") || iframe.startsWith("https://uservideo.xyz") -> {
+ request(iframe, ref = data).document.select("iframe").attr("src")
+ .let { link ->
+ loadExtractor(fixUrl(link), mainUrl, subtitleCallback, callback)
+ }
+ }
+ else -> {
+ loadExtractor(iframe, mainUrl, subtitleCallback, callback)
+ }
+ }
+ }
+ }
+
+ return true
+ }
+
+
+}
\ No newline at end of file
diff --git a/AnimeSailProvider/src/main/kotlin/com/hexated/AnimeSailProviderPlugin.kt b/AnimeSailProvider/src/main/kotlin/com/hexated/AnimeSailProviderPlugin.kt
new file mode 100644
index 00000000..dfdc4c0c
--- /dev/null
+++ b/AnimeSailProvider/src/main/kotlin/com/hexated/AnimeSailProviderPlugin.kt
@@ -0,0 +1,14 @@
+
+package com.hexated
+
+import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
+import com.lagradost.cloudstream3.plugins.Plugin
+import android.content.Context
+
+@CloudstreamPlugin
+class AnimeSailProviderPlugin: Plugin() {
+ override fun load(context: Context) {
+ // All providers should be added in this manner. Please don't edit the providers list directly.
+ registerMainAPI(AnimeSailProvider())
+ }
+}
\ No newline at end of file
diff --git a/Anizm/build.gradle.kts b/Anizm/build.gradle.kts
new file mode 100644
index 00000000..6c69805d
--- /dev/null
+++ b/Anizm/build.gradle.kts
@@ -0,0 +1,27 @@
+// use an integer for version numbers
+version = 1
+
+
+cloudstream {
+ language = "tr"
+ // All of these properties are optional, you can safely remove them
+
+ // description = "Lorem Ipsum"
+ authors = listOf("Hexated")
+
+ /**
+ * 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=anizm.net&sz=%size%"
+}
\ No newline at end of file
diff --git a/Anizm/src/main/AndroidManifest.xml b/Anizm/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..874740e3
--- /dev/null
+++ b/Anizm/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/Anizm/src/main/kotlin/com/hexated/Anizm.kt b/Anizm/src/main/kotlin/com/hexated/Anizm.kt
new file mode 100644
index 00000000..1d5fb8c0
--- /dev/null
+++ b/Anizm/src/main/kotlin/com/hexated/Anizm.kt
@@ -0,0 +1,195 @@
+package com.hexated
+
+import android.util.Log
+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
+
+
+class Anizm : MainAPI() {
+ override var mainUrl = "https://anizm.net"
+ override var name = "Anizm"
+ override val hasMainPage = true
+ override var lang = "tr"
+ override val hasDownloadSupport = true
+
+ override val supportedTypes = setOf(
+ TvType.Anime,
+ TvType.AnimeMovie,
+ TvType.OVA
+ )
+
+ companion object {
+ private const val mainServer = "https://anizmplayer.com"
+ }
+
+ override val mainPage = mainPageOf(
+ "$mainUrl/anime-izle?sayfa=" to "Son Eklenen Animeler",
+ )
+
+ override suspend fun getMainPage(
+ page: Int,
+ request: MainPageRequest
+ ): HomePageResponse {
+ val document = app.get(request.data + page).document
+ val home = document.select("div.restrictedWidth div#episodesMiddle").mapNotNull {
+ it.toSearchResult()
+ }
+ return newHomePageResponse(request.name, home)
+ }
+
+ private fun getProperAnimeLink(uri: String): String {
+ return if (uri.contains("-bolum")) {
+ "$mainUrl/${uri.substringAfter("$mainUrl/").replace(Regex("-[0-9]+-bolum.*"), "")}"
+ } else {
+ uri
+ }
+ }
+
+ private fun Element.toSearchResult(): AnimeSearchResponse? {
+ val href = getProperAnimeLink(this.selectFirst("a")!!.attr("href"))
+ val title = this.selectFirst("div.title, h5.animeTitle a")?.text() ?: return null
+ val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src"))
+ val episode = this.selectFirst("div.truncateText")?.text()?.let {
+ Regex("([0-9]+).\\s?Bölüm").find(it)?.groupValues?.getOrNull(1)?.toIntOrNull()
+ }
+
+ return newAnimeSearchResponse(title, href, TvType.Anime) {
+ this.posterUrl = posterUrl
+ addSub(episode)
+ }
+ }
+
+ override suspend fun search(query: String): List {
+ val document = app.get(
+ "$mainUrl/fullViewSearch?search=$query&skip=0",
+ headers = mapOf("X-Requested-With" to "XMLHttpRequest")
+ ).document
+
+ return document.select("div.searchResultItem").mapNotNull {
+ it.toSearchResult()
+ }
+ }
+
+ override suspend fun load(url: String): LoadResponse {
+ val document = app.get(url).document
+
+ val title = document.selectFirst("h2.anizm_pageTitle a")!!.text().trim()
+ val type =
+ if (document.select("div.ui.grid div.four.wide").size == 1) TvType.Movie else TvType.Anime
+ val trailer = document.select("div.yt-hd-thumbnail-inner-container iframe").attr("src")
+ val episodes = document.select("div.ui.grid div.four.wide").map {
+ val name = it.select("div.episodeBlock").text()
+ val link = fixUrl(it.selectFirst("a")?.attr("href").toString())
+ Episode(link, name)
+ }
+ return newAnimeLoadResponse(title, url, type) {
+ posterUrl = fixUrlNull(document.selectFirst("div.infoPosterImg > img")?.attr("src"))
+ this.year = document.select("div.infoSta ul li:first-child").text().trim().toIntOrNull()
+ addEpisodes(DubStatus.Subbed, episodes)
+ plot = document.select("div.infoDesc").text().trim()
+ this.tags = document.select("span.dataValue span.ui.label").map { it.text() }
+ addTrailer(trailer)
+ }
+ }
+
+ private suspend fun invokeLokalSource(
+ url: String,
+ translator: String,
+ sourceCallback: (ExtractorLink) -> Unit
+ ) {
+ app.get(url, referer = "$mainUrl/").document.select("script").find { script ->
+ script.data().contains("eval(function(p,a,c,k,e,d)")
+ }?.let {
+ val key = getAndUnpack(it.data()).substringAfter("FirePlayer(\"").substringBefore("\",")
+ val referer = "$mainServer/video/$key"
+ val link = "$mainServer/player/index.php?data=$key&do=getVideo"
+ Log.i("hexated", link)
+ app.post(
+ link,
+ data = mapOf("hash" to key, "r" to "$mainUrl/"),
+ referer = referer,
+ headers = mapOf(
+ "Accept" to "*/*",
+ "Origin" to mainServer,
+ "Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8",
+ "X-Requested-With" to "XMLHttpRequest"
+ )
+ ).parsedSafe()?.videoSource?.let { m3uLink ->
+ M3u8Helper.generateM3u8(
+ "${this.name} ($translator)",
+ m3uLink,
+ referer
+ ).forEach(sourceCallback)
+ }
+ }
+ }
+
+ override suspend fun loadLinks(
+ data: String,
+ isCasting: Boolean,
+ subtitleCallback: (SubtitleFile) -> Unit,
+ callback: (ExtractorLink) -> Unit
+ ): Boolean {
+ val document = app.get(data).document
+ document.select("div.episodeTranslators div#fansec").map {
+ Pair(it.select("a").attr("translator"), it.select("div.title").text())
+ }.apmap { (url, translator) ->
+ safeApiCall {
+ app.get(
+ url,
+ referer = data,
+ headers = mapOf(
+ "Accept" to "application/json, text/javascript, */*; q=0.01",
+ "X-Requested-With" to "XMLHttpRequest"
+ )
+ ).parsedSafe()?.data?.let {
+ Jsoup.parse(it).select("a").apmap { video ->
+ app.get(
+ video.attr("video"),
+ referer = data,
+ headers = mapOf(
+ "Accept" to "application/json, text/javascript, */*; q=0.01",
+ "X-Requested-With" to "XMLHttpRequest"
+ )
+ ).parsedSafe()?.player?.let { iframe ->
+ Jsoup.parse(iframe).select("iframe").attr("src").let { link ->
+ when {
+ link.startsWith(mainServer) -> {
+ invokeLokalSource(link, translator, callback)
+ }
+ else -> {
+ loadExtractor(
+ fixUrl(link),
+ "$mainUrl/",
+ subtitleCallback,
+ callback
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return true
+ }
+
+ data class Source(
+ @JsonProperty("videoSource") val videoSource: String?,
+ )
+
+ data class Videos(
+ @JsonProperty("player") val player: String?,
+ )
+
+ data class Translators(
+ @JsonProperty("data") val data: String?,
+ )
+
+}
\ No newline at end of file
diff --git a/ExampleProvider/src/main/kotlin/com/example/ExamplePlugin.kt b/Anizm/src/main/kotlin/com/hexated/AnizmPlugin.kt
similarity index 77%
rename from ExampleProvider/src/main/kotlin/com/example/ExamplePlugin.kt
rename to Anizm/src/main/kotlin/com/hexated/AnizmPlugin.kt
index 8ddce484..c6741426 100644
--- a/ExampleProvider/src/main/kotlin/com/example/ExamplePlugin.kt
+++ b/Anizm/src/main/kotlin/com/hexated/AnizmPlugin.kt
@@ -1,13 +1,14 @@
-package com.example
+
+package com.hexated
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
-class TestPlugin: Plugin() {
+class AnizmPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
- registerMainAPI(ExampleProvider())
+ registerMainAPI(Anizm())
}
}
\ No newline at end of file
diff --git a/DramaidProvider/build.gradle.kts b/DramaidProvider/build.gradle.kts
new file mode 100644
index 00000000..c2173913
--- /dev/null
+++ b/DramaidProvider/build.gradle.kts
@@ -0,0 +1,26 @@
+// use an integer for version numbers
+version = 2
+
+
+cloudstream {
+ language = "id"
+ // All of these properties are optional, you can safely remove them
+
+ // description = "Lorem Ipsum"
+ authors = listOf("Hexated")
+
+ /**
+ * Status int as the following:
+ * 0: Down
+ * 1: Ok
+ * 2: Slow
+ * 3: Beta only
+ * */
+ status = 1 // will be 3 if unspecified
+ tvTypes = listOf(
+ "AsianDrama",
+ "Movie",
+ )
+
+ iconUrl = "https://www.google.com/s2/favicons?domain=dramaid.asia&sz=%size%"
+}
\ No newline at end of file
diff --git a/ExampleProvider/src/main/AndroidManifest.xml b/DramaidProvider/src/main/AndroidManifest.xml
similarity index 54%
rename from ExampleProvider/src/main/AndroidManifest.xml
rename to DramaidProvider/src/main/AndroidManifest.xml
index 1863f02a..c98063f8 100644
--- a/ExampleProvider/src/main/AndroidManifest.xml
+++ b/DramaidProvider/src/main/AndroidManifest.xml
@@ -1,2 +1,2 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/DramaidProvider/src/main/kotlin/com/hexated/DramaidProvider.kt b/DramaidProvider/src/main/kotlin/com/hexated/DramaidProvider.kt
new file mode 100644
index 00000000..c6412f66
--- /dev/null
+++ b/DramaidProvider/src/main/kotlin/com/hexated/DramaidProvider.kt
@@ -0,0 +1,213 @@
+package com.hexated
+
+import com.fasterxml.jackson.annotation.JsonProperty
+import com.lagradost.cloudstream3.*
+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
+
+class DramaidProvider : MainAPI() {
+ override var mainUrl = "https://dramaid.asia"
+ override var name = "DramaId"
+ override val hasQuickSearch = false
+ override val hasMainPage = true
+ override var 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 val mainPage = mainPageOf(
+ "&status=&type=&order=update" to "Drama Terbaru",
+ "&order=latest" to "Baru Ditambahkan",
+ "&status=&type=&order=popular" to "Drama Popular",
+ )
+
+ override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
+ val document = app.get("$mainUrl/series/?page=$page${request.data}").document
+ val home = document.select("article[itemscope=itemscope]").mapNotNull {
+ it.toSearchResult()
+ }
+ return newHomePageResponse(request.name, home)
+ }
+
+ 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() ?: return null
+ val posterUrl = fixUrlNull(this.selectFirst(".limit > noscript > img")?.attr("src"))
+
+ return newTvSeriesSearchResponse(title, href, TvType.AsianDrama) {
+ this.posterUrl = posterUrl
+ }
+ }
+
+ override suspend fun search(query: String): List {
+ 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>(source)?.map {
+ sourceCallback(
+ ExtractorLink(
+ name,
+ "Drive",
+ fixUrl(it.file),
+ referer = "https://motonews.club/",
+ quality = getQualityFromName(it.label)
+ )
+ )
+ }
+
+ tryParseJson(trackers)?.let {
+ subCallback.invoke(
+ 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 sources = document.select(".mobius > .mirror > option").mapNotNull {
+ fixUrl(Jsoup.parse(base64Decode(it.attr("value"))).select("iframe").attr("src"))
+ }
+
+ sources.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, subtitleCallback, callback)
+ }
+ }
+
+ return true
+ }
+
+}
diff --git a/DramaidProvider/src/main/kotlin/com/hexated/DramaidProviderPlugin.kt b/DramaidProvider/src/main/kotlin/com/hexated/DramaidProviderPlugin.kt
new file mode 100644
index 00000000..25753bb4
--- /dev/null
+++ b/DramaidProvider/src/main/kotlin/com/hexated/DramaidProviderPlugin.kt
@@ -0,0 +1,14 @@
+
+package com.hexated
+
+import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
+import com.lagradost.cloudstream3.plugins.Plugin
+import android.content.Context
+
+@CloudstreamPlugin
+class DramaidProviderPlugin: Plugin() {
+ override fun load(context: Context) {
+ // All providers should be added in this manner. Please don't edit the providers list directly.
+ registerMainAPI(DramaidProvider())
+ }
+}
\ No newline at end of file
diff --git a/DubokuProvider/build.gradle.kts b/DubokuProvider/build.gradle.kts
new file mode 100644
index 00000000..c5826742
--- /dev/null
+++ b/DubokuProvider/build.gradle.kts
@@ -0,0 +1,27 @@
+// use an integer for version numbers
+version = 1
+
+
+cloudstream {
+ language = "zh"
+ // All of these properties are optional, you can safely remove them
+
+ // description = "Lorem Ipsum"
+ authors = listOf("Hexated")
+
+ /**
+ * Status int as the following:
+ * 0: Down
+ * 1: Ok
+ * 2: Slow
+ * 3: Beta only
+ * */
+ status = 1 // will be 3 if unspecified
+ tvTypes = listOf(
+ "AsianDrama",
+ "TvSeries",
+ "Movie",
+ )
+
+ iconUrl = "https://www.google.com/s2/favicons?domain=www.duboku.tv&sz=%size%"
+}
\ No newline at end of file
diff --git a/DubokuProvider/src/main/AndroidManifest.xml b/DubokuProvider/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..c98063f8
--- /dev/null
+++ b/DubokuProvider/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/DubokuProvider/src/main/kotlin/com/hexated/DubokuProvider.kt b/DubokuProvider/src/main/kotlin/com/hexated/DubokuProvider.kt
new file mode 100644
index 00000000..1262fb5d
--- /dev/null
+++ b/DubokuProvider/src/main/kotlin/com/hexated/DubokuProvider.kt
@@ -0,0 +1,133 @@
+package com.hexated
+
+import com.fasterxml.jackson.annotation.JsonProperty
+import com.lagradost.cloudstream3.*
+import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
+import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
+import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.M3u8Helper
+import org.jsoup.nodes.Element
+
+class DubokuProvider : MainAPI() {
+ override var mainUrl = "https://www.duboku.tv"
+ override var name = "Duboku"
+ override val hasMainPage = true
+ override var lang = "zh"
+ override val hasDownloadSupport = true
+ override val supportedTypes = setOf(
+ TvType.Movie,
+ TvType.TvSeries,
+ TvType.AsianDrama,
+ )
+
+ override val mainPage = mainPageOf(
+ "$mainUrl/vodshow/2--time------" to "连续剧 时间",
+ "$mainUrl/vodshow/2--hits------" to "连续剧 人气",
+ "$mainUrl/vodshow/13--time------" to "陆剧 时间",
+ "$mainUrl/vodshow/13--hits------" to "陆剧 人气",
+ "$mainUrl/vodshow/15--time------" to "日韩剧 时间",
+ "$mainUrl/vodshow/15--hits------" to "日韩剧 人气",
+ "$mainUrl/vodshow/21--time------" to "短剧 时间",
+ "$mainUrl/vodshow/21--hits------" to "短剧 人气",
+ "$mainUrl/vodshow/16--time------" to "英美剧 时间",
+ "$mainUrl/vodshow/16--hits------" to "英美剧 人气",
+ "$mainUrl/vodshow/14--time------" to "台泰剧 时间",
+ "$mainUrl/vodshow/14--hits------" to "台泰剧 人气",
+ "$mainUrl/vodshow/20--time------" to "港剧 时间",
+ "$mainUrl/vodshow/20--hits------" to "港剧 人气",
+ )
+
+ override suspend fun getMainPage(
+ page: Int,
+ request: MainPageRequest
+ ): HomePageResponse {
+ val document = app.get("${request.data}$page---.html").document
+ val home = document.select("ul.myui-vodlist.clearfix li").mapNotNull {
+ it.toSearchResult()
+ }
+ return newHomePageResponse(request.name, home)
+ }
+
+ private fun Element.toSearchResult(): SearchResponse? {
+ val title = this.selectFirst("h4.title a")?.text()?.trim() ?: return null
+ val href = fixUrl(this.selectFirst("a")?.attr("href").toString())
+ val posterUrl = fixUrlNull(this.selectFirst("a")?.attr("data-original"))
+ val episode = this.selectFirst("span.pic-text.text-right")?.text()?.filter { it.isDigit() }
+ ?.toIntOrNull()
+
+ return newAnimeSearchResponse(title, href, TvType.Movie) {
+ this.posterUrl = posterUrl
+ addSub(episode)
+ }
+ }
+
+ override suspend fun search(query: String): List {
+ val document = app.get("$mainUrl/vodsearch/-------------.html?wd=$query&submit=").document
+
+ return document.select("ul#searchList li").mapNotNull {
+ it.toSearchResult()
+ }
+ }
+
+ override suspend fun load(url: String): LoadResponse? {
+ val document = app.get(url).document
+
+ val title = document.selectFirst("h1.title")?.text()?.trim() ?: return null
+ val tvType = if (document.select("ul.myui-content__list li").size == 1
+ ) TvType.Movie else TvType.TvSeries
+ val actors = document.select("p.data")[2].select("a").map { it.text() }
+
+ val episodes = document.select("ul.myui-content__list li").map {
+ val href = fixUrl(it.select("a").attr("href"))
+ val name = it.select("a").text().trim()
+ Episode(
+ data = href,
+ name = name,
+ )
+ }
+ return newTvSeriesLoadResponse(title, url, tvType, episodes) {
+ this.posterUrl = fixUrlNull(
+ document.selectFirst("a.myui-vodlist__thumb.picture img")?.attr("data-original")
+ )
+ this.year =
+ document.select("p.data")[0].select("a").last()?.text()?.trim()?.toIntOrNull()
+ this.plot = document.selectFirst("span.sketch.content")?.text()?.trim()
+ this.tags = document.select("p.data")[0].select("a").map { it.text() }
+ this.rating = document.select("div#rating span.branch").text().toRatingInt()
+ addActors(actors)
+ }
+
+ }
+
+ override suspend fun loadLinks(
+ data: String,
+ isCasting: Boolean,
+ subtitleCallback: (SubtitleFile) -> Unit,
+ callback: (ExtractorLink) -> Unit
+ ): Boolean {
+
+ app.get(data).document.select("script").map { script ->
+ if (script.data().contains("var player_data={")) {
+ val dataJson =
+ script.data().substringAfter("var player_data={").substringBefore("}")
+ tryParseJson("{$dataJson}")?.let { source ->
+ M3u8Helper.generateM3u8(
+ this.name,
+ source.url ?: return@map,
+ referer = "https://w.duboku.io/",
+ headers = mapOf("Origin" to "https://w.duboku.io")
+ ).forEach(callback)
+ }
+ }
+ }
+
+
+ return true
+ }
+
+ data class Sources(
+ @JsonProperty("url") val url: String?,
+ )
+
+
+}
\ No newline at end of file
diff --git a/DubokuProvider/src/main/kotlin/com/hexated/DubokuProviderPlugin.kt b/DubokuProvider/src/main/kotlin/com/hexated/DubokuProviderPlugin.kt
new file mode 100644
index 00000000..b82c70b4
--- /dev/null
+++ b/DubokuProvider/src/main/kotlin/com/hexated/DubokuProviderPlugin.kt
@@ -0,0 +1,14 @@
+
+package com.hexated
+
+import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
+import com.lagradost.cloudstream3.plugins.Plugin
+import android.content.Context
+
+@CloudstreamPlugin
+class DubokuProviderPlugin: Plugin() {
+ override fun load(context: Context) {
+ // All providers should be added in this manner. Please don't edit the providers list directly.
+ registerMainAPI(DubokuProvider())
+ }
+}
\ No newline at end of file
diff --git a/ExampleProvider/build.gradle.kts b/ExampleProvider/build.gradle.kts
deleted file mode 100644
index 58645fe8..00000000
--- a/ExampleProvider/build.gradle.kts
+++ /dev/null
@@ -1,24 +0,0 @@
-// 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
-
- // List of video source types. Users are able to filter for extensions in a given category.
- // You can find a list of avaliable types here:
- // https://recloudstream.github.io/cloudstream/html/app/com.lagradost.cloudstream3/-tv-type/index.html
- tvTypes = listOf("Others")
-}
diff --git a/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt b/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt
deleted file mode 100644
index e44a21b7..00000000
--- a/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.example
-
-import com.lagradost.cloudstream3.TvType
-import com.lagradost.cloudstream3.MainAPI
-import com.lagradost.cloudstream3.SearchResponse
-
-class ExampleProvider : MainAPI() { // all providers must be an instance of MainAPI
- override var mainUrl = "https://example.com/"
- override var name = "Example provider"
- override val supportedTypes = setOf(TvType.Movie)
-
- override var lang = "en"
-
- // enable this when your provider has a main page
- override val hasMainPage = true
-
- // this function gets called when you search for something
- override suspend fun search(query: String): List {
- return listOf()
- }
-}
\ No newline at end of file
diff --git a/GomunimeProvider/build.gradle.kts b/GomunimeProvider/build.gradle.kts
new file mode 100644
index 00000000..96c05f73
--- /dev/null
+++ b/GomunimeProvider/build.gradle.kts
@@ -0,0 +1,27 @@
+// use an integer for version numbers
+version = 2
+
+
+cloudstream {
+ language = "id"
+ // All of these properties are optional, you can safely remove them
+
+// description = "Lorem Ipsum"
+ authors = listOf("Hexated")
+
+ /**
+ * Status int as the following:
+ * 0: Down
+ * 1: Ok
+ * 2: Slow
+ * 3: Beta only
+ * */
+ status = 0 // will be 3 if unspecified
+ tvTypes = listOf(
+ "AnimeMovie",
+ "Anime",
+ "OVA",
+ )
+
+ iconUrl = "https://www.google.com/s2/favicons?domain=185.231.223.76&sz=%size%"
+}
\ No newline at end of file
diff --git a/GomunimeProvider/src/main/AndroidManifest.xml b/GomunimeProvider/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..c98063f8
--- /dev/null
+++ b/GomunimeProvider/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/GomunimeProvider/src/main/kotlin/com/hexated/GomunimeProvider.kt b/GomunimeProvider/src/main/kotlin/com/hexated/GomunimeProvider.kt
new file mode 100644
index 00000000..d9fa137b
--- /dev/null
+++ b/GomunimeProvider/src/main/kotlin/com/hexated/GomunimeProvider.kt
@@ -0,0 +1,238 @@
+package com.hexated
+
+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.AppUtils.parseJson
+import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.M3u8Helper
+import com.lagradost.cloudstream3.utils.Qualities
+import com.lagradost.cloudstream3.utils.loadExtractor
+import org.jsoup.Jsoup
+
+class GomunimeProvider : MainAPI() {
+ override var mainUrl = "https://185.231.223.76"
+ override var name = "Gomunime"
+ override val hasMainPage = true
+ override var lang = "id"
+ override val hasDownloadSupport = true
+
+ override val supportedTypes = setOf(
+ TvType.Anime,
+ TvType.AnimeMovie,
+ TvType.OVA
+ )
+
+ companion object {
+
+ private const val mainServer = "https://path.onicdn.xyz/app/rapi.php"
+
+ 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 val mainPage = mainPageOf(
+ "e" to "Episode Baru",
+ "c" to "Completed",
+ "la" to "Live Action",
+ "t" to "Trending"
+ )
+
+ override suspend fun getMainPage(
+ page: Int,
+ request: MainPageRequest
+ ): HomePageResponse {
+ val home = Jsoup.parse(
+ (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 request.data,
+ "pag" to "$page"
+ )
+ ).parsedSafe()?.html ?: throw ErrorLoadingException("Invalid Json reponse"))
+ ).select("li").mapNotNull {
+ val title = it.selectFirst("a.name")?.text()?.trim() ?: return@mapNotNull null
+ 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
+ addSub(epNum)
+ }
+ }
+
+ return newHomePageResponse(request.name, home)
+ }
+
+ 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 {
+ 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)
+ }
+ }
+ }
+
+ 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 trailer = document.selectFirst("div.embed-responsive noscript iframe")?.attr("src")
+ val episodes = parseJson>(
+ Regex("var episodelist = (\\[.*])").find(
+ document.select(".bixbox.bxcl.epcheck > script").toString().trim()
+ )?.groupValues?.get(1).toString().replace(Regex("""\\"""), "").trim()
+ ).map {
+ val name =
+ Regex("(Episode\\s?[0-9]+)").find(it.epTitle.toString())?.groupValues?.getOrNull(0)
+ ?: 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
+ addTrailer(trailer)
+ }
+ }
+
+ 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("\";")
+ .replace(" ", "+")
+ val image = document.select("img#tempvid").last()?.attr("src").toString()
+
+ val sources: List> = app.post(
+ url = mainServer,
+ data = mapOf("data" to key, "gambar" to image, "judul" to title, "func" to "mirror"),
+ referer = "$mainUrl/"
+ ).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, mainUrl, subtitleCallback, callback)
+ }
+ // Skip for now
+// it.second.contains("hls") -> {
+// app.post(
+// url = mainServer,
+// data = mapOf("fid" to it.first, "func" to "hls")
+// ).text.let { link ->
+// M3u8Helper.generateM3u8(
+// this.name,
+// link,
+// "$mainUrl/",
+// headers = mapOf("Origin" to mainUrl)
+// ).forEach(callback)
+// }
+// }
+ it.second.contains("mp4") -> {
+ app.post(
+ url = mainServer,
+ data = mapOf("data" to it.first, "func" to "blogs")
+ ).parsed>().map {
+ callback.invoke(
+ ExtractorLink(
+ source = name,
+ name = "Mobi SD",
+ url = it.file,
+ referer = "$mainUrl/",
+ quality = Qualities.P360.value
+ )
+ )
+ }
+ }
+ else -> null
+ }
+ }
+ }
+
+ return true
+ }
+
+ private data class Response(
+ @JsonProperty("status") val status: Boolean,
+ @JsonProperty("html") val html: String
+ )
+
+ data class MobiSource(
+ @JsonProperty("file") val file: String,
+ @JsonProperty("label") val label: String,
+ @JsonProperty("type") val type: String
+ )
+
+ 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?
+ )
+
+}
\ No newline at end of file
diff --git a/GomunimeProvider/src/main/kotlin/com/hexated/GomunimeProviderPlugin.kt b/GomunimeProvider/src/main/kotlin/com/hexated/GomunimeProviderPlugin.kt
new file mode 100644
index 00000000..06fa91e8
--- /dev/null
+++ b/GomunimeProvider/src/main/kotlin/com/hexated/GomunimeProviderPlugin.kt
@@ -0,0 +1,14 @@
+
+package com.hexated
+
+import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
+import com.lagradost.cloudstream3.plugins.Plugin
+import android.content.Context
+
+@CloudstreamPlugin
+class GomunimeProviderPlugin: Plugin() {
+ override fun load(context: Context) {
+ // All providers should be added in this manner. Please don't edit the providers list directly.
+ registerMainAPI(GomunimeProvider())
+ }
+}
\ No newline at end of file
diff --git a/Gomunimeis/build.gradle.kts b/Gomunimeis/build.gradle.kts
new file mode 100644
index 00000000..b329c01b
--- /dev/null
+++ b/Gomunimeis/build.gradle.kts
@@ -0,0 +1,27 @@
+// use an integer for version numbers
+version = 1
+
+
+cloudstream {
+ language = "id"
+ // All of these properties are optional, you can safely remove them
+
+ // description = "Lorem Ipsum"
+ authors = listOf("Hexated")
+
+ /**
+ * 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=gomunime.is&sz=%size%"
+}
\ No newline at end of file
diff --git a/Gomunimeis/src/main/AndroidManifest.xml b/Gomunimeis/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..c98063f8
--- /dev/null
+++ b/Gomunimeis/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/Gomunimeis/src/main/kotlin/com/hexated/Gomunimeis.kt b/Gomunimeis/src/main/kotlin/com/hexated/Gomunimeis.kt
new file mode 100644
index 00000000..e9312963
--- /dev/null
+++ b/Gomunimeis/src/main/kotlin/com/hexated/Gomunimeis.kt
@@ -0,0 +1,162 @@
+package com.hexated
+
+import com.fasterxml.jackson.annotation.JsonProperty
+import com.lagradost.cloudstream3.*
+import com.lagradost.cloudstream3.utils.*
+import java.util.ArrayList
+
+
+class Gomunimeis : MainAPI() {
+ override var mainUrl = "https://gomunime.is"
+ override var name = "Gomunime.is"
+ override val hasMainPage = true
+ override var lang = "id"
+ override val hasQuickSearch = true
+ override val hasDownloadSupport = true
+
+ override val supportedTypes = setOf(
+ TvType.Anime,
+ TvType.AnimeMovie,
+ TvType.OVA
+ )
+
+ companion object {
+
+ private const val mainImageUrl = "https://upload.anoboy.live"
+
+ fun getType(t: String): TvType {
+ return if (t.contains("OVA", true) || t.contains("Special", true)) TvType.OVA
+ else if (t.contains("Movie", true)) TvType.AnimeMovie
+ else TvType.Anime
+ }
+
+ fun getStatus(t: String): ShowStatus {
+ return when (t) {
+ "Completed" -> ShowStatus.Completed
+ "Ongoing" -> ShowStatus.Ongoing
+ else -> ShowStatus.Completed
+ }
+ }
+ }
+
+ override val mainPage = mainPageOf(
+ "&limit=12&action=load_movie_last_update&status=Ongoing" to "Episode Baru",
+ "&limit=15&action=load_movie_last_update&status=Completed" to "Completed",
+ "&limit=15&action=load_movie_last_update&type=Live Action" to "Live Action",
+ "&limit=15&action=load_movie_trending" to "Trending"
+ )
+
+ override suspend fun getMainPage(
+ page: Int,
+ request: MainPageRequest
+ ): HomePageResponse {
+ val home = app.get(
+ "$mainUrl/my-ajax?page=$page${request.data}",
+ headers = mapOf("X-Requested-With" to "XMLHttpRequest")
+ )
+ .parsedSafe()?.data
+ ?.mapNotNull { media ->
+ media.toSearchResponse()
+ } ?: throw ErrorLoadingException("Invalid Json reponse")
+ return newHomePageResponse(request.name, home)
+ }
+
+ private fun Anime.toSearchResponse(): SearchResponse? {
+
+ return newAnimeSearchResponse(
+ postTitle ?: return null,
+ "$mainUrl/anime/$postName.$salt",
+ TvType.TvSeries,
+ ) {
+ this.posterUrl = "$mainImageUrl/$image"
+ addSub(totalEpisode?.toIntOrNull())
+ }
+ }
+
+ override suspend fun quickSearch(query: String): List = search(query)
+
+ override suspend fun search(query: String): List {
+ return app.get(
+ "$mainUrl/my-ajax?page=1&limit=10&action=load_search_movie&keyword=$query",
+ referer = "$mainUrl/search/?keyword=$query",
+ headers = mapOf("X-Requested-With" to "XMLHttpRequest")
+ ).parsedSafe()?.data
+ ?.mapNotNull { media ->
+ media.toSearchResponse()
+ } ?: throw ErrorLoadingException("Invalid Json reponse")
+ }
+
+ 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("src")
+ val tags = document.select(".genxed > a").map { it.text() }
+ val type = getType(document.selectFirst("div.info-content .spe span:last-child")?.ownText().toString())
+ val year = Regex("\\d, ([0-9]*)").find(
+ document.selectFirst("div.info-content .spe span.split")?.ownText().toString()
+ )?.groupValues?.get(1)?.toIntOrNull()
+ val status = getStatus(document.selectFirst(".spe > span")!!.ownText())
+ val description = document.select("div[itemprop = description] > p").text()
+
+ val episodes = document.select(".eplister > ul > li").map {
+ val header = it.select(".epl-title").text()
+ val name = Regex("(Episode\\s?[0-9]+)").find(header)?.groupValues?.getOrNull(0) ?: header
+ val link = it.select("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
+ this.tags = tags
+ }
+ }
+
+ override suspend fun loadLinks(
+ data: String,
+ isCasting: Boolean,
+ subtitleCallback: (SubtitleFile) -> Unit,
+ callback: (ExtractorLink) -> Unit
+ ): Boolean {
+
+ val document = app.get(data).document
+
+ document.select("div.player-container iframe").attr("src").substringAfter("html#").let { id ->
+ app.get("https://gomunimes.com/stream?id=$id").parsedSafe()?.server?.streamsb?.link?.let { link ->
+ loadExtractor(link, "https://vidgomunime.xyz/", subtitleCallback, callback)
+ }
+ }
+
+ return true
+ }
+
+ data class Streamsb(
+ @JsonProperty("link") val link: String?,
+ )
+
+ data class Server(
+ @JsonProperty("streamsb") val streamsb: Streamsb?,
+ )
+
+ data class Sources(
+ @JsonProperty("server") val server: Server?,
+ )
+
+ data class Responses(
+ @JsonProperty("data") val data: ArrayList? = arrayListOf(),
+ )
+
+ data class Anime(
+ @JsonProperty("post_title") val postTitle: String?,
+ @JsonProperty("post_name") val postName: String?,
+ @JsonProperty("image") val image: String?,
+ @JsonProperty("total_episode") val totalEpisode: String?,
+ @JsonProperty("salt") val salt: String?,
+ )
+
+}
\ No newline at end of file
diff --git a/Gomunimeis/src/main/kotlin/com/hexated/GomunimeisPlugin.kt b/Gomunimeis/src/main/kotlin/com/hexated/GomunimeisPlugin.kt
new file mode 100644
index 00000000..36a76324
--- /dev/null
+++ b/Gomunimeis/src/main/kotlin/com/hexated/GomunimeisPlugin.kt
@@ -0,0 +1,14 @@
+
+package com.hexated
+
+import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
+import com.lagradost.cloudstream3.plugins.Plugin
+import android.content.Context
+
+@CloudstreamPlugin
+class GomunimeisPlugin: Plugin() {
+ override fun load(context: Context) {
+ // All providers should be added in this manner. Please don't edit the providers list directly.
+ registerMainAPI(Gomunimeis())
+ }
+}
\ No newline at end of file
diff --git a/HDrezkaProvider/build.gradle.kts b/HDrezkaProvider/build.gradle.kts
new file mode 100644
index 00000000..4f6da247
--- /dev/null
+++ b/HDrezkaProvider/build.gradle.kts
@@ -0,0 +1,28 @@
+// use an integer for version numbers
+version = 2
+
+
+cloudstream {
+ language = "ru"
+ // All of these properties are optional, you can safely remove them
+
+ // description = "Lorem Ipsum"
+ authors = listOf("Hexated")
+
+ /**
+ * Status int as the following:
+ * 0: Down
+ * 1: Ok
+ * 2: Slow
+ * 3: Beta only
+ * */
+ status = 1 // will be 3 if unspecified
+ tvTypes = listOf(
+ "AsianDrama",
+ "Anime",
+ "TvSeries",
+ "Movie",
+ )
+
+ iconUrl = "https://www.google.com/s2/favicons?domain=hdrezka19139.org&sz=%size%"
+}
\ No newline at end of file
diff --git a/HDrezkaProvider/src/main/AndroidManifest.xml b/HDrezkaProvider/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..c98063f8
--- /dev/null
+++ b/HDrezkaProvider/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/HDrezkaProvider/src/main/kotlin/com/hexated/HDrezkaProvider.kt b/HDrezkaProvider/src/main/kotlin/com/hexated/HDrezkaProvider.kt
new file mode 100644
index 00000000..f3570c5e
--- /dev/null
+++ b/HDrezkaProvider/src/main/kotlin/com/hexated/HDrezkaProvider.kt
@@ -0,0 +1,407 @@
+package com.hexated
+
+import com.fasterxml.jackson.annotation.JsonProperty
+import com.lagradost.cloudstream3.*
+import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
+import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
+import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
+import com.lagradost.cloudstream3.utils.AppUtils.toJson
+import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
+import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.Qualities
+import com.lagradost.cloudstream3.utils.getQualityFromName
+import org.jsoup.Jsoup
+import org.jsoup.nodes.Element
+import java.util.*
+
+class HDrezkaProvider : MainAPI() {
+ override var mainUrl = "https://rezka.ag"
+ override var name = "HDrezka"
+ override val hasMainPage = true
+ override var lang = "ru"
+ override val hasDownloadSupport = true
+ override val supportedTypes = setOf(
+ TvType.Movie,
+ TvType.TvSeries,
+ TvType.Anime,
+ TvType.AsianDrama
+ )
+
+ override val mainPage = mainPageOf(
+ "$mainUrl/films/?filter=watching" to "фильмы",
+ "$mainUrl/series/?filter=watching" to "сериалы",
+ "$mainUrl/cartoons/?filter=watching" to "мультфильмы",
+ "$mainUrl/animation/?filter=watching" to "аниме",
+ )
+
+ override suspend fun getMainPage(
+ page: Int,
+ request: MainPageRequest
+ ): HomePageResponse {
+ val url = request.data.split("?")
+ val home = app.get("${url.first()}page/$page/?${url.last()}").document.select(
+ "div.b-content__inline_items div.b-content__inline_item"
+ ).map {
+ it.toSearchResult()
+ }
+
+ return newHomePageResponse(request.name, home)
+ }
+
+ private fun Element.toSearchResult(): SearchResponse {
+ val title =
+ this.selectFirst("div.b-content__inline_item-link > a")?.text()?.trim().toString()
+ val href = this.selectFirst("a")?.attr("href").toString()
+ val posterUrl = this.select("img").attr("src")
+ val type = if (this.select("span.info").isNotEmpty()) TvType.TvSeries else TvType.Movie
+ return if (type == TvType.Movie) {
+ newMovieSearchResponse(title, href, TvType.Movie) {
+ this.posterUrl = posterUrl
+ }
+ } else {
+ val episode =
+ this.select("span.info").text().substringAfter(",").replace(Regex("[^0-9]"), "")
+ .toIntOrNull()
+ newAnimeSearchResponse(title, href, TvType.TvSeries) {
+ this.posterUrl = posterUrl
+ addDubStatus(
+ dubExist = true,
+ dubEpisodes = episode,
+ subExist = true,
+ subEpisodes = episode
+ )
+ }
+ }
+ }
+
+ override suspend fun search(query: String): List {
+ val link = "$mainUrl/search/?do=search&subaction=search&q=$query"
+ val document = app.get(link).document
+
+ return document.select("div.b-content__inline_items div.b-content__inline_item").map {
+ it.toSearchResult()
+ }
+ }
+
+ override suspend fun load(url: String): LoadResponse {
+ val document = app.get(url).document
+
+ val id = url.split("/").last().split("-").first()
+ val title = (document.selectFirst("div.b-post__origtitle")?.text()?.trim()
+ ?: document.selectFirst("div.b-post__title h1")?.text()?.trim()).toString()
+ val poster = fixUrlNull(document.selectFirst("div.b-sidecover img")?.attr("src"))
+ val tags =
+ document.select("table.b-post__info > tbody > tr:nth-child(5) span[itemprop=genre]")
+ .map { it.text() }
+ val year = document.select("div.film-info > div:nth-child(2) a").text().toIntOrNull()
+ val tvType = if (document.select("div#simple-episodes-tabs")
+ .isNullOrEmpty()
+ ) TvType.Movie else TvType.TvSeries
+ val description = document.selectFirst("div.b-post__description_text")?.text()?.trim()
+ val trailer = app.post(
+ "$mainUrl/engine/ajax/gettrailervideo.php",
+ data = mapOf("id" to id),
+ referer = url
+ ).parsedSafe()?.code.let {
+ Jsoup.parse(it.toString()).select("iframe").attr("src")
+ }
+ val rating =
+ document.selectFirst("table.b-post__info > tbody > tr:nth-child(1) span.bold")?.text()
+ .toRatingInt()
+ val actors = document.select("table.b-post__info > tbody > tr:last-child span.item").map {
+ Actor(
+ it.selectFirst("span[itemprop=name]")?.text().toString(),
+ it.selectFirst("span[itemprop=actor]")?.attr("data-photo")
+ )
+ }
+
+ val recommendations = document.select("div.b-sidelist div.b-content__inline_item").map {
+ it.toSearchResult()
+ }
+
+ val data = HashMap()
+ val server = ArrayList