forked from recloudstream/cloudstream
		
	Added Inf home to some providers and new providers (#1377)
* The Boys!! * Prikitieww...
This commit is contained in:
		
							parent
							
								
									c5406acc1e
								
							
						
					
					
						commit
						df509dfe1b
					
				
					 25 changed files with 938 additions and 477 deletions
				
			
		|  | @ -109,7 +109,8 @@ object APIHolder { | ||||||
|             PhimmoichillProvider(), |             PhimmoichillProvider(), | ||||||
|             HDrezkaProvider(), |             HDrezkaProvider(), | ||||||
|             YomoviesProvider(), |             YomoviesProvider(), | ||||||
| 
 |             DubokuProvider(), | ||||||
|  |             KisskhProvider(), | ||||||
| 
 | 
 | ||||||
|             // Metadata providers |             // Metadata providers | ||||||
|             //TmdbProvider(), |             //TmdbProvider(), | ||||||
|  | @ -148,6 +149,7 @@ object APIHolder { | ||||||
|             AnimeIndoProvider(), |             AnimeIndoProvider(), | ||||||
|             AnimeSailProvider(), |             AnimeSailProvider(), | ||||||
|             TocanimeProvider(), |             TocanimeProvider(), | ||||||
|  |             WcofunProvider(), | ||||||
|             //MultiAnimeProvider(), |             //MultiAnimeProvider(), | ||||||
|             NginxProvider(), |             NginxProvider(), | ||||||
|             OlgplyProvider(), |             OlgplyProvider(), | ||||||
|  |  | ||||||
|  | @ -62,20 +62,20 @@ class AnimeIndoProvider : MainAPI() { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { |     override val mainPage = mainPageOf( | ||||||
|         val document = request(mainUrl).document |         "$mainUrl/anime-terbaru/page/" to "Anime Terbaru", | ||||||
|  |         "$mainUrl/donghua-terbaru/page/" to "Donghua Terbaru" | ||||||
|  |     ) | ||||||
| 
 | 
 | ||||||
|         val homePageList = ArrayList<HomePageList>() |     override suspend fun getMainPage( | ||||||
| 
 |         page: Int, | ||||||
|         document.select("div.widget_senction").forEach { block -> |         request: MainPageRequest | ||||||
|             val header = block.selectFirst("div.widget-title > h3")!!.text().trim() |     ): HomePageResponse { | ||||||
|             val items = block.select("div.post-show > article").map { |         val document = request(request.data + page).document | ||||||
|  |         val home = document.select("div.post-show > article").mapNotNull { | ||||||
|             it.toSearchResult() |             it.toSearchResult() | ||||||
|         } |         } | ||||||
|             if (items.isNotEmpty()) homePageList.add(HomePageList(header, items)) |         return newHomePageResponse(request.name, home) | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return HomePageResponse(homePageList) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun getProperAnimeLink(uri: String): String { |     private fun getProperAnimeLink(uri: String): String { | ||||||
|  | @ -96,8 +96,8 @@ class AnimeIndoProvider : MainAPI() { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun Element.toSearchResult(): AnimeSearchResponse { |     private fun Element.toSearchResult(): AnimeSearchResponse? { | ||||||
|         val title = this.selectFirst("div.title")!!.text().trim() |         val title = this.selectFirst("div.title")?.text()?.trim() ?: return null | ||||||
|         val href = getProperAnimeLink(this.selectFirst("a")!!.attr("href")) |         val href = getProperAnimeLink(this.selectFirst("a")!!.attr("href")) | ||||||
|         val posterUrl = this.select("img[itemprop=image]").attr("src").toString() |         val posterUrl = this.select("img[itemprop=image]").attr("src").toString() | ||||||
|         val type = getType(this.select("div.type").text().trim()) |         val type = getType(this.select("div.type").text().trim()) | ||||||
|  |  | ||||||
|  | @ -47,20 +47,18 @@ class AnimeSailProvider : MainAPI() { | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     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 { |     override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { | ||||||
|         val document = request(mainUrl).document |         val document = request(request.data + page).document | ||||||
| 
 |         val home = document.select("article").map { | ||||||
|         val homePageList = ArrayList<HomePageList>() |  | ||||||
| 
 |  | ||||||
|         document.select(".bixbox").forEach { block -> |  | ||||||
|             val header = block.select(".releases > h3").text().trim() |  | ||||||
|             val animes = block.select("article").map { |  | ||||||
|             it.toSearchResult() |             it.toSearchResult() | ||||||
|         } |         } | ||||||
|             if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes)) |         return newHomePageResponse(request.name, home) | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return HomePageResponse(homePageList) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun getProperAnimeLink(uri: String): String { |     private fun getProperAnimeLink(uri: String): String { | ||||||
|  |  | ||||||
|  | @ -3,7 +3,6 @@ package com.lagradost.cloudstream3.animeproviders | ||||||
| import com.fasterxml.jackson.annotation.JsonProperty | import com.fasterxml.jackson.annotation.JsonProperty | ||||||
| import com.lagradost.cloudstream3.* | import com.lagradost.cloudstream3.* | ||||||
| import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer | import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer | ||||||
| import com.lagradost.cloudstream3.mvvm.logError |  | ||||||
| import com.lagradost.cloudstream3.mvvm.safeApiCall | import com.lagradost.cloudstream3.mvvm.safeApiCall | ||||||
| import com.lagradost.cloudstream3.utils.AppUtils.parseJson | import com.lagradost.cloudstream3.utils.AppUtils.parseJson | ||||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | import com.lagradost.cloudstream3.utils.ExtractorLink | ||||||
|  | @ -41,35 +40,31 @@ class GomunimeProvider : MainAPI() { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private data class Response( |     override val mainPage = mainPageOf( | ||||||
|         @JsonProperty("status") val status: Boolean, |         "e" to "Episode Baru", | ||||||
|         @JsonProperty("html") val html: String |         "c" to "Completed", | ||||||
|  |         "la" to "Live Action", | ||||||
|  |         "t" to "Trending" | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { |     override suspend fun getMainPage( | ||||||
|         val urls = listOf( |         page: Int, | ||||||
|             Pair("e", "Episode Baru"), |         request: MainPageRequest | ||||||
|             Pair("c", "Completed"), |     ): HomePageResponse { | ||||||
|             Pair("la", "Live Action"), |  | ||||||
|             Pair("t", "Trending"), |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
|         val items = ArrayList<HomePageList>() |  | ||||||
| 
 |  | ||||||
|         for ((payload, name) in urls) { |  | ||||||
|             try { |  | ||||||
|         val home = Jsoup.parse( |         val home = Jsoup.parse( | ||||||
|                     parseJson<Response>( |             (app.post( | ||||||
|                         app.post( |  | ||||||
|                 url = "$mainUrl/wp-admin/admin-ajax.php/wp-admin/admin-ajax.php", |                 url = "$mainUrl/wp-admin/admin-ajax.php/wp-admin/admin-ajax.php", | ||||||
|                 headers = mapOf("Referer" to mainUrl), |                 headers = mapOf("Referer" to mainUrl), | ||||||
|                             data = mapOf("action" to "home_ajax", "fungsi" to payload, "pag" to "1") |                 data = mapOf( | ||||||
|                         ).text |                     "action" to "home_ajax", | ||||||
|                     ).html |                     "fungsi" to request.data, | ||||||
|                 ).select("li").map { |                     "pag" to "$page" | ||||||
|                     val title = it.selectFirst("a.name")!!.text().trim() |                 ) | ||||||
|  |             ).parsedSafe<Response>()?.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 href = getProperAnimeLink(it.selectFirst("a")!!.attr("href")) | ||||||
|                     val posterUrl = it.selectFirst("img")!!.attr("src") |             val posterUrl = it.selectFirst("img")?.attr("src") | ||||||
|             val type = getType(it.selectFirst(".taglist > span")!!.text().trim()) |             val type = getType(it.selectFirst(".taglist > span")!!.text().trim()) | ||||||
|             val epNum = it.select(".tag.ep").text().replace(Regex("[^0-9]"), "").trim() |             val epNum = it.select(".tag.ep").text().replace(Regex("[^0-9]"), "").trim() | ||||||
|                 .toIntOrNull() |                 .toIntOrNull() | ||||||
|  | @ -78,14 +73,8 @@ class GomunimeProvider : MainAPI() { | ||||||
|                 addSub(epNum) |                 addSub(epNum) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|                 items.add(HomePageList(name, home)) |  | ||||||
|             } catch (e: Exception) { |  | ||||||
|                 logError(e) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         if (items.size <= 0) throw ErrorLoadingException() |         return newHomePageResponse(request.name, home) | ||||||
|         return HomePageResponse(items) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun getProperAnimeLink(uri: String): String { |     private fun getProperAnimeLink(uri: String): String { | ||||||
|  | @ -119,14 +108,6 @@ class GomunimeProvider : MainAPI() { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private data class EpisodeElement( |  | ||||||
|         @JsonProperty("data-index") val dataIndex: Long?, |  | ||||||
|         @JsonProperty("ep-num") val epNum: String?, |  | ||||||
|         @JsonProperty("ep-title") val epTitle: String?, |  | ||||||
|         @JsonProperty("ep-link") val epLink: String, |  | ||||||
|         @JsonProperty("ep-date") val epDate: String? |  | ||||||
|     ) |  | ||||||
| 
 |  | ||||||
|     override suspend fun load(url: String): LoadResponse { |     override suspend fun load(url: String): LoadResponse { | ||||||
|         val document = app.get(url).document |         val document = app.get(url).document | ||||||
| 
 | 
 | ||||||
|  | @ -164,12 +145,6 @@ class GomunimeProvider : MainAPI() { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     data class MobiSource( |  | ||||||
|         @JsonProperty("file") val file: String, |  | ||||||
|         @JsonProperty("label") val label: String, |  | ||||||
|         @JsonProperty("type") val type: String |  | ||||||
|     ) |  | ||||||
| 
 |  | ||||||
|     override suspend fun loadLinks( |     override suspend fun loadLinks( | ||||||
|         data: String, |         data: String, | ||||||
|         isCasting: Boolean, |         isCasting: Boolean, | ||||||
|  | @ -234,4 +209,24 @@ class GomunimeProvider : MainAPI() { | ||||||
| 
 | 
 | ||||||
|         return true |         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? | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  | @ -2,11 +2,9 @@ package com.lagradost.cloudstream3.animeproviders | ||||||
| 
 | 
 | ||||||
| import com.lagradost.cloudstream3.* | import com.lagradost.cloudstream3.* | ||||||
| import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall | import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall | ||||||
| import com.lagradost.cloudstream3.network.DdosGuardKiller |  | ||||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | import com.lagradost.cloudstream3.utils.ExtractorLink | ||||||
| import org.jsoup.Jsoup | import org.jsoup.Jsoup | ||||||
| import org.jsoup.nodes.Element | import org.jsoup.nodes.Element | ||||||
| import java.util.* |  | ||||||
| 
 | 
 | ||||||
| class KuramanimeProvider : MainAPI() { | class KuramanimeProvider : MainAPI() { | ||||||
|     override var mainUrl = "https://kuramanime.com" |     override var mainUrl = "https://kuramanime.com" | ||||||
|  | @ -38,28 +36,24 @@ class KuramanimeProvider : MainAPI() { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { |     override val mainPage = mainPageOf( | ||||||
|         val document = app.get(mainUrl).document |         "$mainUrl/anime/ongoing?order_by=updated&page=" to "Sedang Tayang", | ||||||
|  |         "$mainUrl/anime/finished?order_by=updated&page=" to "Selesai Tayang", | ||||||
|  |         "$mainUrl/properties/season/summer-2022?order_by=most_viewed&page=" to "Dilihat Terbanyak Musim Ini", | ||||||
|  |         "$mainUrl/anime/movie?order_by=updated&page=" to "Film Layar Lebar", | ||||||
|  |     ) | ||||||
| 
 | 
 | ||||||
|         val homePageList = ArrayList<HomePageList>() |     override suspend fun getMainPage( | ||||||
|  |         page: Int, | ||||||
|  |         request: MainPageRequest | ||||||
|  |     ): HomePageResponse { | ||||||
|  |         val document = app.get(request.data + page).document | ||||||
| 
 | 
 | ||||||
|         document.select("div.trending__product").forEach { block -> |         val home = document.select("div.col-lg-4.col-md-6.col-sm-6").mapNotNull { | ||||||
|             val header = block.selectFirst("h4")?.text().toString() |  | ||||||
|             val animes = block.select("div.col-lg-4.col-md-6.col-sm-6").map { |  | ||||||
|             it.toSearchResult() |             it.toSearchResult() | ||||||
|         } |         } | ||||||
|             if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes)) |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         document.select("div.product__sidebar__view").forEach { block -> |         return newHomePageResponse(request.name, home) | ||||||
|             val header = block.selectFirst("h5")?.text().toString() |  | ||||||
|             val animes = block.select("div.product__sidebar__comment__item").map { |  | ||||||
|                 it.toSearchResultView() |  | ||||||
|             } |  | ||||||
|             if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes)) |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return HomePageResponse(homePageList) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun getProperAnimeLink(uri: String): String { |     private fun getProperAnimeLink(uri: String): String { | ||||||
|  | @ -70,9 +64,9 @@ class KuramanimeProvider : MainAPI() { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun Element.toSearchResult(): AnimeSearchResponse { |     private fun Element.toSearchResult(): AnimeSearchResponse? { | ||||||
|         val href = getProperAnimeLink(fixUrl(this.selectFirst("a")!!.attr("href"))) |         val href = getProperAnimeLink(fixUrl(this.selectFirst("a")!!.attr("href"))) | ||||||
|         val title = this.selectFirst("h5 a")?.text().toString() |         val title = this.selectFirst("h5 a")?.text() ?: return null | ||||||
|         val posterUrl = fixUrl(this.select("div.product__item__pic.set-bg").attr("data-setbg")) |         val posterUrl = fixUrl(this.select("div.product__item__pic.set-bg").attr("data-setbg")) | ||||||
|         val episode = Regex("([0-9*])\\s?/").find( |         val episode = Regex("([0-9*])\\s?/").find( | ||||||
|             this.select("div.ep span").text() |             this.select("div.ep span").text() | ||||||
|  | @ -85,23 +79,6 @@ class KuramanimeProvider : MainAPI() { | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun Element.toSearchResultView(): AnimeSearchResponse { |  | ||||||
|         val href = getProperAnimeLink(fixUrl(this.selectFirst("a")?.attr("href").toString())) |  | ||||||
|         val title = this.selectFirst("h5")?.text()?.trim().toString() |  | ||||||
|         val posterUrl = fixUrlNull( |  | ||||||
|             this.selectFirst("div.product__sidebar__comment__item__pic.set-bg")?.attr("data-setbg") |  | ||||||
|         ) |  | ||||||
|         val episode = |  | ||||||
|             this.selectFirst("h5")?.nextElementSibling()?.text()?.replace(Regex("[^0-9]"), "") |  | ||||||
|                 ?.toIntOrNull() |  | ||||||
| 
 |  | ||||||
|         return newAnimeSearchResponse(title, href, TvType.Anime) { |  | ||||||
|             this.posterUrl = posterUrl |  | ||||||
|             addSub(episode) |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     override suspend fun search(query: String): List<SearchResponse> { |     override suspend fun search(query: String): List<SearchResponse> { | ||||||
|         val link = "$mainUrl/anime?search=$query&order_by=oldest" |         val link = "$mainUrl/anime?search=$query&order_by=oldest" | ||||||
|         val document = app.get(link).document |         val document = app.get(link).document | ||||||
|  |  | ||||||
|  | @ -39,20 +39,23 @@ class KuronimeProvider : MainAPI() { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { |     override val mainPage = mainPageOf( | ||||||
|         val document = app.get(mainUrl).document |         "$mainUrl/page/" to "New Episodes", | ||||||
|  |         "$mainUrl/popular-anime/page/" to "Popular Anime", | ||||||
|  |         "$mainUrl/movies/page/" to "Movies", | ||||||
|  |         "$mainUrl/genres/donghua/page/" to "Donghua", | ||||||
|  |         "$mainUrl/live-action/page/" to "Live Action", | ||||||
|  |     ) | ||||||
| 
 | 
 | ||||||
|         val homePageList = ArrayList<HomePageList>() |     override suspend fun getMainPage( | ||||||
| 
 |         page: Int, | ||||||
|         document.select(".bixbox").forEach { block -> |         request: MainPageRequest | ||||||
|             val header = block.select(".releases > h3").text().trim() |     ): HomePageResponse { | ||||||
|             val animes = block.select("article").map { |         val document = app.get(request.data + page).document | ||||||
|  |         val home = document.select("article").map { | ||||||
|             it.toSearchResult() |             it.toSearchResult() | ||||||
|         } |         } | ||||||
|             if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes)) |         return newHomePageResponse(request.name, home) | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return HomePageResponse(homePageList) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun getProperAnimeLink(uri: String): String { |     private fun getProperAnimeLink(uri: String): String { | ||||||
|  | @ -77,7 +80,10 @@ class KuronimeProvider : MainAPI() { | ||||||
|     private fun Element.toSearchResult(): AnimeSearchResponse { |     private fun Element.toSearchResult(): AnimeSearchResponse { | ||||||
|         val href = getProperAnimeLink(fixUrlNull(this.selectFirst("a")?.attr("href")).toString()) |         val href = getProperAnimeLink(fixUrlNull(this.selectFirst("a")?.attr("href")).toString()) | ||||||
|         val title = this.select(".bsuxtt, .tt > h4").text().trim() |         val title = this.select(".bsuxtt, .tt > h4").text().trim() | ||||||
|         val posterUrl = fixUrlNull(this.selectFirst("div.view,div.bt")?.nextElementSibling()?.select("img")?.attr("data-src")) |         val posterUrl = fixUrlNull( | ||||||
|  |             this.selectFirst("div.view,div.bt")?.nextElementSibling()?.select("img") | ||||||
|  |                 ?.attr("data-src") | ||||||
|  |         ) | ||||||
|         val epNum = this.select(".ep").text().replace(Regex("[^0-9]"), "").trim().toIntOrNull() |         val epNum = this.select(".ep").text().replace(Regex("[^0-9]"), "").trim().toIntOrNull() | ||||||
|         val tvType = getType(this.selectFirst(".bt > span")?.text().toString()) |         val tvType = getType(this.selectFirst(".bt > span")?.text().toString()) | ||||||
|         return newAnimeSearchResponse(title, href, tvType) { |         return newAnimeSearchResponse(title, href, tvType) { | ||||||
|  | @ -117,7 +123,8 @@ class KuronimeProvider : MainAPI() { | ||||||
| 
 | 
 | ||||||
|         val episodes = document.select("div.bixbox.bxcl > ul > li").map { |         val episodes = document.select("div.bixbox.bxcl > ul > li").map { | ||||||
|             val name = it.selectFirst("a")?.text()?.trim() |             val name = it.selectFirst("a")?.text()?.trim() | ||||||
|             val episode = it.selectFirst("a")?.text()?.trim()?.replace("Episode", "")?.trim()?.toIntOrNull() |             val episode = | ||||||
|  |                 it.selectFirst("a")?.text()?.trim()?.replace("Episode", "")?.trim()?.toIntOrNull() | ||||||
|             val link = it.selectFirst("a")!!.attr("href") |             val link = it.selectFirst("a")!!.attr("href") | ||||||
|             Episode(link, name = name, episode = episode) |             Episode(link, name = name, episode = episode) | ||||||
|         }.reversed() |         }.reversed() | ||||||
|  |  | ||||||
|  | @ -40,20 +40,18 @@ class NeonimeProvider : MainAPI() { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     override val mainPage = mainPageOf( | ||||||
|  |         "$mainUrl/episode/page/" to "Episode Terbaru", | ||||||
|  |         "$mainUrl/tvshows/page/" to "Anime Terbaru", | ||||||
|  |         "$mainUrl/movies/page/" to "Movie", | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|     override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { |     override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { | ||||||
|         val document = app.get(mainUrl).document |         val document = app.get(request.data + page).document | ||||||
| 
 |         val home = document.select("tbody tr,div.item").mapNotNull { | ||||||
|         val homePageList = ArrayList<HomePageList>() |  | ||||||
|          |  | ||||||
|         document.select("div.item_1.items,div#slid01").forEach { block -> |  | ||||||
|             val header = block.previousElementSibling()?.select("h1")?.text() ?: block.selectFirst("h3")?.text().toString() |  | ||||||
|             val animes = block.select("div.item").map { |  | ||||||
|                 it.toSearchResult() |                 it.toSearchResult() | ||||||
|             } |             } | ||||||
|             if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes)) |         return newHomePageResponse(request.name, home) | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return HomePageResponse(homePageList) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun getProperAnimeLink(uri: String): String { |     private fun getProperAnimeLink(uri: String): String { | ||||||
|  | @ -80,11 +78,13 @@ class NeonimeProvider : MainAPI() { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun Element.toSearchResult(): AnimeSearchResponse { |     private fun Element.toSearchResult(): AnimeSearchResponse? { | ||||||
|  |         val title = this.selectFirst("td.bb a")?.ownText() ?: this.selectFirst("h2")?.text() ?: return null | ||||||
|         val href = getProperAnimeLink(fixUrl(this.select("a").attr("href"))) |         val href = getProperAnimeLink(fixUrl(this.select("a").attr("href"))) | ||||||
|         val title = this.select("span.tt.title-episode,h2.title-episode-movie,span.ttps").text() |  | ||||||
|         val posterUrl = fixUrl(this.select("img").attr("data-src")) |         val posterUrl = fixUrl(this.select("img").attr("data-src")) | ||||||
|         val epNum = this.select(".fixyear > h2.text-center").text().replace(Regex("[^0-9]"), "").trim().toIntOrNull() |         val epNum = this.selectFirst("td.bb span")?.text()?.let { eps -> | ||||||
|  |             Regex("Episode\\s?([0-9]+)").find(eps)?.groupValues?.getOrNull(1)?.toIntOrNull() | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         return newAnimeSearchResponse(title, href, TvType.Anime) { |         return newAnimeSearchResponse(title, href, TvType.Anime) { | ||||||
|             this.posterUrl = posterUrl |             this.posterUrl = posterUrl | ||||||
|  |  | ||||||
|  | @ -228,7 +228,7 @@ class NontonAnimeIDProvider : MainAPI() { | ||||||
|         val document = app.get(data).document |         val document = app.get(data).document | ||||||
|         val sources = ArrayList<String>() |         val sources = ArrayList<String>() | ||||||
| 
 | 
 | ||||||
|         document.select(".container1 > ul > li").apmap { |         document.select(".container1 > ul > li:not(.boxtab)").apmap { | ||||||
|             val dataPost = it.attr("data-post") |             val dataPost = it.attr("data-post") | ||||||
|             val dataNume = it.attr("data-nume") |             val dataNume = it.attr("data-nume") | ||||||
|             val dataType = it.attr("data-type") |             val dataType = it.attr("data-type") | ||||||
|  | @ -240,7 +240,9 @@ class NontonAnimeIDProvider : MainAPI() { | ||||||
|                     "post" to dataPost, |                     "post" to dataPost, | ||||||
|                     "nume" to dataNume, |                     "nume" to dataNume, | ||||||
|                     "type" to dataType |                     "type" to dataType | ||||||
|                 ) |                 ), | ||||||
|  |                 referer = data, | ||||||
|  |                 headers = mapOf("X-Requested-With" to "XMLHttpRequest") | ||||||
|             ).document.select("iframe").attr("src") |             ).document.select("iframe").attr("src") | ||||||
| 
 | 
 | ||||||
|             sources.add(fixUrl(iframe)) |             sources.add(fixUrl(iframe)) | ||||||
|  |  | ||||||
|  | @ -40,20 +40,21 @@ class OploverzProvider : MainAPI() { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { |     override val mainPage = mainPageOf( | ||||||
|         val document = app.get(mainUrl).document |         "&status=&type=&order=update" to "Episode Terbaru", | ||||||
|  |         "&status=&type=&order=latest" to "Anime Terbaru", | ||||||
|  |         "&sub=&order=popular" to "Popular Anime", | ||||||
|  |     ) | ||||||
| 
 | 
 | ||||||
|         val homePageList = ArrayList<HomePageList>() |     override suspend fun getMainPage( | ||||||
| 
 |         page: Int, | ||||||
|         document.select(".bixbox.bbnofrm").forEach { block -> |         request: MainPageRequest | ||||||
|             val header = block.selectFirst("h3")!!.text().trim() |     ): HomePageResponse { | ||||||
|             val animes = block.select("article[itemscope=itemscope]").map { |         val document = app.get("$mainUrl/anime/?page=$page${request.data}").document | ||||||
|  |         val home = document.select("article[itemscope=itemscope]").mapNotNull { | ||||||
|             it.toSearchResult() |             it.toSearchResult() | ||||||
|         } |         } | ||||||
|             if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes)) |         return newHomePageResponse(request.name, home) | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return HomePageResponse(homePageList) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun getProperAnimeLink(uri: String): String { |     private fun getProperAnimeLink(uri: String): String { | ||||||
|  | @ -68,7 +69,8 @@ class OploverzProvider : MainAPI() { | ||||||
|                 )?.groupValues?.get(1).toString() |                 )?.groupValues?.get(1).toString() | ||||||
|                 (title.contains("-ova")) -> Regex("(.+)-ova").find(title)?.groupValues?.get(1) |                 (title.contains("-ova")) -> Regex("(.+)-ova").find(title)?.groupValues?.get(1) | ||||||
|                     .toString() |                     .toString() | ||||||
|                 (title.contains("-movie")) -> Regex("(.+)-subtitle").find(title)?.groupValues?.get(1).toString() |                 (title.contains("-movie")) -> Regex("(.+)-subtitle").find(title)?.groupValues?.get(1) | ||||||
|  |                     .toString() | ||||||
|                 else -> Regex("(.+)-subtitle").find(title)?.groupValues?.get(1).toString() |                 else -> Regex("(.+)-subtitle").find(title)?.groupValues?.get(1).toString() | ||||||
|                     .replace(Regex("-\\d+"), "") |                     .replace(Regex("-\\d+"), "") | ||||||
|             } |             } | ||||||
|  | @ -87,16 +89,14 @@ class OploverzProvider : MainAPI() { | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun Element.toSearchResult(): AnimeSearchResponse { |     private fun Element.toSearchResult(): AnimeSearchResponse? { | ||||||
|         val href = getProperAnimeLink(this.selectFirst("a.tip")!!.attr("href")) |         val href = getProperAnimeLink(this.selectFirst("a.tip")!!.attr("href")) | ||||||
|         val title = this.selectFirst("h2[itemprop=headline]")?.text()?.trim() ?: "" |         val title = this.selectFirst("h2[itemprop=headline]")?.text()?.trim() ?: return null | ||||||
|         val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src")) |         val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src")) | ||||||
|         val type = getType(this.selectFirst(".eggtype, .typez")?.text()?.trim().toString()) |         val type = getType(this.selectFirst(".eggtype, .typez")?.text()?.trim().toString()) | ||||||
|         val epNum = this.selectFirst(".eggepisode, span.epx")?.text()?.replace(Regex("[^0-9]"), "")?.trim()?.toIntOrNull() |  | ||||||
| 
 | 
 | ||||||
|         return newAnimeSearchResponse(title, href, type) { |         return newAnimeSearchResponse(title, href, type) { | ||||||
|             this.posterUrl = posterUrl |             this.posterUrl = posterUrl | ||||||
|             addSub(epNum) |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -144,7 +144,8 @@ class OploverzProvider : MainAPI() { | ||||||
| 
 | 
 | ||||||
|         val episodes = document.select(".eplister > ul > li").map { |         val episodes = document.select(".eplister > ul > li").map { | ||||||
|             val header = it.select(".epl-title").text() |             val header = it.select(".epl-title").text() | ||||||
|             val name = Regex("(Episode\\s?[0-9]+)").find(header)?.groupValues?.getOrNull(0) ?: header |             val name = | ||||||
|  |                 Regex("(Episode\\s?[0-9]+)").find(header)?.groupValues?.getOrNull(0) ?: header | ||||||
|             val link = fixUrl(it.select("a").attr("href")) |             val link = fixUrl(it.select("a").attr("href")) | ||||||
|             Episode(link, name) |             Episode(link, name) | ||||||
|         }.reversed() |         }.reversed() | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ import com.lagradost.cloudstream3.utils.ExtractorLink | ||||||
| import com.lagradost.cloudstream3.utils.loadExtractor | import com.lagradost.cloudstream3.utils.loadExtractor | ||||||
| import org.jsoup.Jsoup | import org.jsoup.Jsoup | ||||||
| import org.jsoup.nodes.Element | import org.jsoup.nodes.Element | ||||||
|  | import java.util.ArrayList | ||||||
| 
 | 
 | ||||||
| class OtakudesuProvider : MainAPI() { | class OtakudesuProvider : MainAPI() { | ||||||
|     override var mainUrl = "https://otakudesu.watch" |     override var mainUrl = "https://otakudesu.watch" | ||||||
|  | @ -37,24 +38,24 @@ class OtakudesuProvider : MainAPI() { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { |     override val mainPage = mainPageOf( | ||||||
|         val document = app.get(mainUrl).document |         "$mainUrl/ongoing-anime/page/" to "Anime Ongoing", | ||||||
|  |         "$mainUrl/complete-anime/page/" to "Anime Completed" | ||||||
|  |     ) | ||||||
| 
 | 
 | ||||||
|         val homePageList = ArrayList<HomePageList>() |     override suspend fun getMainPage( | ||||||
| 
 |         page: Int, | ||||||
|         document.select("div.rseries").forEach { block -> |         request: MainPageRequest | ||||||
|             val header = block.selectFirst("div.rvad > h1")!!.text().trim() |     ): HomePageResponse { | ||||||
|             val items = block.select("div.venz > ul > li").map { |         val document = app.get(request.data + page).document | ||||||
|  |         val home = document.select("div.venz > ul > li").mapNotNull { | ||||||
|             it.toSearchResult() |             it.toSearchResult() | ||||||
|         } |         } | ||||||
|             if (items.isNotEmpty()) homePageList.add(HomePageList(header, items)) |         return newHomePageResponse(request.name, home) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|         return HomePageResponse(homePageList) |     private fun Element.toSearchResult(): AnimeSearchResponse? { | ||||||
|     } |         val title = this.selectFirst("h2.jdlflm")?.text()?.trim() ?: return null | ||||||
| 
 |  | ||||||
|     private fun Element.toSearchResult(): AnimeSearchResponse { |  | ||||||
|         val title = this.selectFirst("h2.jdlflm")!!.text().trim() |  | ||||||
|         val href = this.selectFirst("a")!!.attr("href") |         val href = this.selectFirst("a")!!.attr("href") | ||||||
|         val posterUrl = this.select("div.thumbz > img").attr("src").toString() |         val posterUrl = this.select("div.thumbz > img").attr("src").toString() | ||||||
|         val epNum = this.selectFirst("div.epz")?.ownText()?.replace(Regex("[^0-9]"), "")?.trim() |         val epNum = this.selectFirst("div.epz")?.ownText()?.replace(Regex("[^0-9]"), "")?.trim() | ||||||
|  | @ -103,7 +104,9 @@ class OtakudesuProvider : MainAPI() { | ||||||
|         val description = document.select("div.sinopc > p").text() |         val description = document.select("div.sinopc > p").text() | ||||||
| 
 | 
 | ||||||
|         val episodes = document.select("div.episodelist")[1].select("ul > li").mapNotNull { |         val episodes = document.select("div.episodelist")[1].select("ul > li").mapNotNull { | ||||||
|             val name = Regex("(Episode\\s?[0-9]+)").find(it.selectFirst("a")?.text().toString())?.groupValues?.getOrNull(0) ?: it.selectFirst("a")?.text() |             val name = Regex("(Episode\\s?[0-9]+)").find( | ||||||
|  |                 it.selectFirst("a")?.text().toString() | ||||||
|  |             )?.groupValues?.getOrNull(0) ?: it.selectFirst("a")?.text() | ||||||
|             val link = fixUrl(it.selectFirst("a")!!.attr("href")) |             val link = fixUrl(it.selectFirst("a")!!.attr("href")) | ||||||
|             Episode(link, name) |             Episode(link, name) | ||||||
|         }.reversed() |         }.reversed() | ||||||
|  | @ -180,7 +183,10 @@ class OtakudesuProvider : MainAPI() { | ||||||
|             ).select("iframe").attr("src") |             ).select("iframe").attr("src") | ||||||
| 
 | 
 | ||||||
|             if (sources.startsWith("https://desustream.me")) { |             if (sources.startsWith("https://desustream.me")) { | ||||||
|                 if (!sources.contains("/arcg/") && !sources.contains("/odchan/") && !sources.contains("/desudrive/")) { |                 if (!sources.contains("/arcg/") && !sources.contains("/odchan/") && !sources.contains( | ||||||
|  |                         "/desudrive/" | ||||||
|  |                     ) | ||||||
|  |                 ) { | ||||||
|                     sources = app.get(sources).document.select("iframe").attr("src") |                     sources = app.get(sources).document.select("iframe").attr("src") | ||||||
|                 } |                 } | ||||||
|                 if (sources.startsWith("https://yourupload.com")) { |                 if (sources.startsWith("https://yourupload.com")) { | ||||||
|  |  | ||||||
|  | @ -0,0 +1,172 @@ | ||||||
|  | package com.lagradost.cloudstream3.animeproviders | ||||||
|  | 
 | ||||||
|  | import com.fasterxml.jackson.annotation.JsonProperty | ||||||
|  | import com.lagradost.cloudstream3.* | ||||||
|  | import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall | ||||||
|  | import com.lagradost.cloudstream3.utils.ExtractorLink | ||||||
|  | import com.lagradost.cloudstream3.utils.Qualities | ||||||
|  | import org.jsoup.Jsoup | ||||||
|  | import org.jsoup.nodes.Element | ||||||
|  | 
 | ||||||
|  | class WcofunProvider : MainAPI() { | ||||||
|  |     override var mainUrl = "https://www.wcofun.com" | ||||||
|  |     override var name = "WCO Fun" | ||||||
|  |     override val hasMainPage = true | ||||||
|  |     override val hasDownloadSupport = true | ||||||
|  | 
 | ||||||
|  |     override val supportedTypes = setOf( | ||||||
|  |         TvType.Anime, | ||||||
|  |         TvType.AnimeMovie, | ||||||
|  |         TvType.OVA | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     override suspend fun getMainPage( | ||||||
|  |         page: Int, | ||||||
|  |         request: MainPageRequest | ||||||
|  |     ): HomePageResponse { | ||||||
|  |         val document = app.get(mainUrl).document | ||||||
|  | 
 | ||||||
|  |         val homePageList = ArrayList<HomePageList>() | ||||||
|  | 
 | ||||||
|  |         document.select("div#sidebar_right,div#sidebar_right2").forEach { block -> | ||||||
|  |             val header = block.previousElementSibling()?.ownText() ?: return@forEach | ||||||
|  |             val animes = block.select("ul.items li").mapNotNull { | ||||||
|  |                 it.toSearchResult() | ||||||
|  |             } | ||||||
|  |             if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes)) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return HomePageResponse(homePageList) | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private fun getProperAnimeLink(uri: String): String { | ||||||
|  |         return if (uri.contains("/anime/")) { | ||||||
|  |             uri | ||||||
|  |         } else { | ||||||
|  |             var title = uri.substringAfter("$mainUrl/") | ||||||
|  |             title = when { | ||||||
|  |                 (title.contains(Regex("-season-[0-9]+-episode"))) && title.contains("-dubbed") -> title.substringBefore("-season") | ||||||
|  |                 (title.contains(Regex("-season-[0-9]+-episode"))) && title.contains("-subbed") -> title.replace(Regex("-season-[0-9]+-episode-[0-9]+"), "") | ||||||
|  |                 title.contains("-subbed") -> title.replace(Regex("-episode-[0-9]+"), "") | ||||||
|  |                 title.contains("-dubbed") -> title.substringBefore("-episode") | ||||||
|  |                 else -> title | ||||||
|  |             } | ||||||
|  |             "$mainUrl/anime/$title" | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private fun Element.toSearchResult(): AnimeSearchResponse? { | ||||||
|  |         val header = this.selectFirst("div.recent-release-episodes a")?.text() | ||||||
|  |         val title = header?.trim() ?: return null | ||||||
|  |         val href = getProperAnimeLink(this.selectFirst("a")!!.attr("href")) | ||||||
|  |         val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src")) | ||||||
|  |         val epNum = header.let { eps -> | ||||||
|  |             Regex("Episode\\s?([0-9]+)").find(eps)?.groupValues?.getOrNull(1)?.toIntOrNull() | ||||||
|  |         } | ||||||
|  |         val isDub = header.contains("Dubbed") | ||||||
|  |         val isSub = header.contains("Subbed") | ||||||
|  |         return newAnimeSearchResponse(title, href, TvType.Anime) { | ||||||
|  |             this.posterUrl = posterUrl | ||||||
|  |             addDubStatus(isDub, isSub, epNum, epNum) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override suspend fun search(query: String): List<SearchResponse> { | ||||||
|  |         val document = app.post( | ||||||
|  |             "$mainUrl/search", | ||||||
|  |             referer = mainUrl, | ||||||
|  |             data = mapOf("catara" to query, "konuara" to "series") | ||||||
|  |         ).document | ||||||
|  | 
 | ||||||
|  |         return document.select("div#sidebar_right2 li").mapNotNull { | ||||||
|  |             it.toSearchResult() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override suspend fun load(url: String): LoadResponse? { | ||||||
|  |         val document = app.get(url).document | ||||||
|  |         val title = document.selectFirst("div.h1-tag a")?.text() ?: return null | ||||||
|  |         val eps = document.select("div#sidebar_right3 div.cat-eps") | ||||||
|  |         val type = if (eps.size == 1 || eps.first()?.text() | ||||||
|  |                 ?.contains(Regex("Episode\\s?[0-9]+")) != true | ||||||
|  |         ) TvType.AnimeMovie else TvType.Anime | ||||||
|  |         val episodes = eps.map { | ||||||
|  |             val name = it.select("a").text() | ||||||
|  |             val link = it.selectFirst("a")!!.attr("href") | ||||||
|  |             Episode(link, name = name) | ||||||
|  |         }.reversed() | ||||||
|  | 
 | ||||||
|  |         return newAnimeLoadResponse(title, url, type) { | ||||||
|  |             posterUrl = fixUrlNull(document.selectFirst("img.img5")?.attr("src")) | ||||||
|  |             addEpisodes(DubStatus.Subbed, episodes) | ||||||
|  |             plot = document.select("div#sidebar_cat > p").text() | ||||||
|  |             this.tags = document.select("div#sidebar_cat a").map { it.text() } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private suspend fun getIframe(url: String): String? { | ||||||
|  |         val document = app.get(url).document | ||||||
|  |         val scriptData = | ||||||
|  |             document.select("script").find { it.data().contains("= \"\";") }?.data() ?: return null | ||||||
|  |         val subtractionNumber = | ||||||
|  |             Regex("""(?<=\.replace\(/\\D/g,''\)\) - )\d+""").find(scriptData)?.value?.toInt() | ||||||
|  |                 ?: return null | ||||||
|  |         val html = Regex("""(?<=\["|, ").+?(?=")""").findAll(scriptData).map { | ||||||
|  |             val number = base64Decode(it.value).replace(Regex("\\D"), "").toInt() | ||||||
|  |             (number - subtractionNumber).toChar() | ||||||
|  |         }.joinToString("") | ||||||
|  |         return Jsoup.parse(html).select("iframe").attr("src").let { fixUrl(it) } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override suspend fun loadLinks( | ||||||
|  |         data: String, | ||||||
|  |         isCasting: Boolean, | ||||||
|  |         subtitleCallback: (SubtitleFile) -> Unit, | ||||||
|  |         callback: (ExtractorLink) -> Unit | ||||||
|  |     ): Boolean { | ||||||
|  | 
 | ||||||
|  |         getIframe(data)?.let { iframe -> | ||||||
|  |             val link = app.get(iframe, referer = data).text.let { | ||||||
|  |                 fixUrlNull( | ||||||
|  |                     Regex("\"(/inc/embed/getvidlink.php.*)\"").find(it)?.groupValues?.getOrNull( | ||||||
|  |                         1 | ||||||
|  |                     ) | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  |             app.get( | ||||||
|  |                 link ?: return@let, | ||||||
|  |                 referer = iframe, | ||||||
|  |                 headers = mapOf("x-requested-with" to "XMLHttpRequest") | ||||||
|  |             ).parsedSafe<Sources>()?.let { | ||||||
|  |                 listOf( | ||||||
|  |                     Pair(it.hd, "HD"), | ||||||
|  |                     Pair(it.enc, "SD") | ||||||
|  |                 ).map { source -> | ||||||
|  |                     suspendSafeApiCall { | ||||||
|  |                         callback.invoke( | ||||||
|  |                             ExtractorLink( | ||||||
|  |                                 "${this.name} ${source.second}", | ||||||
|  |                                 "${this.name} ${source.second}", | ||||||
|  |                                 "${it.server}/getvid?evid=${source.first}", | ||||||
|  |                                 mainUrl, | ||||||
|  |                                 if (source.second == "HD") Qualities.P720.value else Qualities.P480.value | ||||||
|  |                             ) | ||||||
|  |                         ) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return true | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     data class Sources( | ||||||
|  |         @JsonProperty("enc") val enc: String?, | ||||||
|  |         @JsonProperty("server") val server: String?, | ||||||
|  |         @JsonProperty("cdn") val cdn: String?, | ||||||
|  |         @JsonProperty("hd") val hd: String?, | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -7,6 +7,10 @@ import com.lagradost.cloudstream3.utils.ExtractorApi | ||||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | import com.lagradost.cloudstream3.utils.ExtractorLink | ||||||
| import com.lagradost.cloudstream3.utils.M3u8Helper | import com.lagradost.cloudstream3.utils.M3u8Helper | ||||||
| 
 | 
 | ||||||
|  | class Ssbstream : StreamSB() { | ||||||
|  |     override var mainUrl = "https://ssbstream.net" | ||||||
|  | } | ||||||
|  | 
 | ||||||
| class SBfull : StreamSB() { | class SBfull : StreamSB() { | ||||||
|     override var mainUrl = "https://sbfull.com" |     override var mainUrl = "https://sbfull.com" | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -8,7 +8,6 @@ import com.lagradost.cloudstream3.utils.getQualityFromName | ||||||
| import com.lagradost.cloudstream3.utils.loadExtractor | import com.lagradost.cloudstream3.utils.loadExtractor | ||||||
| import org.jsoup.Jsoup | import org.jsoup.Jsoup | ||||||
| import org.jsoup.nodes.Element | import org.jsoup.nodes.Element | ||||||
| import java.util.ArrayList |  | ||||||
| 
 | 
 | ||||||
| class DramaidProvider : MainAPI() { | class DramaidProvider : MainAPI() { | ||||||
|     override var mainUrl = "https://185.224.83.103" |     override var mainUrl = "https://185.224.83.103" | ||||||
|  | @ -30,20 +29,18 @@ class DramaidProvider : MainAPI() { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     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 { |     override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { | ||||||
|         val document = app.get(mainUrl).document |         val document = app.get("$mainUrl/series/?page=$page${request.data}").document | ||||||
| 
 |         val home = document.select("article[itemscope=itemscope]").mapNotNull { | ||||||
|         val homePageList = ArrayList<HomePageList>() |  | ||||||
| 
 |  | ||||||
|         document.select(".bixbox").forEach { block -> |  | ||||||
|             val header = block.selectFirst(".releases > h3")!!.text().trim() |  | ||||||
|             val dramas = block.select("article[itemscope=itemscope]").mapNotNull { |  | ||||||
|             it.toSearchResult() |             it.toSearchResult() | ||||||
|         } |         } | ||||||
|             if (dramas.isNotEmpty()) homePageList.add(HomePageList(header, dramas)) |         return newHomePageResponse(request.name, home) | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return HomePageResponse(homePageList) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun getProperDramaLink(uri: String): String { |     private fun getProperDramaLink(uri: String): String { | ||||||
|  | @ -55,10 +52,10 @@ class DramaidProvider : MainAPI() { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun Element.toSearchResult(): SearchResponse { |     private fun Element.toSearchResult(): SearchResponse? { | ||||||
|         val href = getProperDramaLink(this.selectFirst("a.tip")!!.attr("href")) |         val href = getProperDramaLink(this.selectFirst("a.tip")!!.attr("href")) | ||||||
|         val title = this.selectFirst("h2[itemprop=headline]")!!.text().trim() |         val title = this.selectFirst("h2[itemprop=headline]")?.text()?.trim() ?: return null | ||||||
|         val posterUrl = this.selectFirst(".limit > noscript > img")!!.attr("src") |         val posterUrl = fixUrlNull(this.selectFirst(".limit > noscript > img")?.attr("src")) | ||||||
| 
 | 
 | ||||||
|         return newTvSeriesSearchResponse(title, href, TvType.AsianDrama) { |         return newTvSeriesSearchResponse(title, href, TvType.AsianDrama) { | ||||||
|             this.posterUrl = posterUrl |             this.posterUrl = posterUrl | ||||||
|  |  | ||||||
|  | @ -0,0 +1,133 @@ | ||||||
|  | package com.lagradost.cloudstream3.movieproviders | ||||||
|  | 
 | ||||||
|  | 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<SearchResponse> { | ||||||
|  |         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<Sources>("{$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?, | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -6,6 +6,7 @@ import com.lagradost.cloudstream3.utils.ExtractorLink | ||||||
| import com.lagradost.cloudstream3.utils.httpsify | import com.lagradost.cloudstream3.utils.httpsify | ||||||
| import com.lagradost.cloudstream3.utils.loadExtractor | import com.lagradost.cloudstream3.utils.loadExtractor | ||||||
| import org.jsoup.Jsoup | import org.jsoup.Jsoup | ||||||
|  | import org.jsoup.nodes.Element | ||||||
| 
 | 
 | ||||||
| class HDMovie5 : MainAPI() { | class HDMovie5 : MainAPI() { | ||||||
|     override var mainUrl = "https://hdmovie2.click" |     override var mainUrl = "https://hdmovie2.click" | ||||||
|  | @ -19,29 +20,27 @@ class HDMovie5 : MainAPI() { | ||||||
|         TvType.TvSeries, |         TvType.TvSeries, | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { |     override val mainPage = mainPageOf( | ||||||
|         val doc = app.get(mainUrl).document.select("div.content") |         "$mainUrl/genre/tv-movie/page/" to "TV Movie", | ||||||
|         val list = mapOf( |         "$mainUrl/genre/tv-show/page/" to "TV- Show", | ||||||
|             "Featured Movies" to "featured", |         "$mainUrl/genre/hindi-dubbed/page/" to "Hindi Dubbed", | ||||||
|             "Updated Movies" to "normal" |         "$mainUrl/genre/netflix/page/" to "NETFLIX", | ||||||
|     ) |     ) | ||||||
|         return HomePageResponse(list.map { item -> |  | ||||||
|             HomePageList(item.key, |  | ||||||
|                 doc.select("div.${item.value}>.item").map { |  | ||||||
|                     val data = it.select(".data") |  | ||||||
|                     val a = data.select("a") |  | ||||||
|                     MovieSearchResponse( |  | ||||||
|                         a.text(), |  | ||||||
|                         a.attr("href"), |  | ||||||
| 
 | 
 | ||||||
|                         this.name, |     override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { | ||||||
|                         TvType.Movie, |         val home = app.get(request.data + page).document.select("article.item").mapNotNull { | ||||||
|                         it.select("img").attr("src"), |             it.toSearchResult() | ||||||
|                         data.select("span").text().toIntOrNull() |         } | ||||||
|                     ) |         return newHomePageResponse(request.name, home) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private fun Element.toSearchResult(): SearchResponse? { | ||||||
|  |         val title = this.selectFirst("h3 > a")?.text()?.trim() ?: return null | ||||||
|  |         val href = fixUrl(this.selectFirst("a")!!.attr("href")) | ||||||
|  |         val posterUrl = this.selectFirst("img")?.attr("src") | ||||||
|  |         return newMovieSearchResponse(title, href, TvType.Movie) { | ||||||
|  |             addPoster(posterUrl) | ||||||
|         } |         } | ||||||
|             ) |  | ||||||
|         }) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private data class QuickSearchResponse( |     private data class QuickSearchResponse( | ||||||
|  |  | ||||||
|  | @ -28,27 +28,25 @@ class HDrezkaProvider : MainAPI() { | ||||||
|         TvType.AsianDrama |         TvType.AsianDrama | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { |     override val mainPage = mainPageOf( | ||||||
|  |         "$mainUrl/films/?filter=watching" to "фильмы", | ||||||
|  |         "$mainUrl/series/?filter=watching" to "сериалы", | ||||||
|  |         "$mainUrl/cartoons/?filter=watching" to "мультфильмы", | ||||||
|  |         "$mainUrl/animation/?filter=watching" to "аниме", | ||||||
|  |     ) | ||||||
| 
 | 
 | ||||||
|         val items = ArrayList<HomePageList>() |     override suspend fun getMainPage( | ||||||
| 
 |         page: Int, | ||||||
|         listOf( |         request: MainPageRequest | ||||||
|             Pair("фильмы", "$mainUrl/films/?filter=watching"), |     ): HomePageResponse { | ||||||
|             Pair("сериалы", "$mainUrl/series/?filter=watching"), |         val url = request.data.split("?") | ||||||
|             Pair("мультфильмы", "$mainUrl/cartoons/?filter=watching"), |         val home = app.get("${url.first()}page/$page/?${url.last()}").document.select( | ||||||
|             Pair("аниме", "$mainUrl/animation/?filter=watching"), |  | ||||||
|         ).apmap { (header, url) -> |  | ||||||
|             safeApiCall { |  | ||||||
|                 val home = app.get(url).document.select( |  | ||||||
|             "div.b-content__inline_items div.b-content__inline_item" |             "div.b-content__inline_items div.b-content__inline_item" | ||||||
|         ).map { |         ).map { | ||||||
|             it.toSearchResult() |             it.toSearchResult() | ||||||
|         } |         } | ||||||
|                 items.add(HomePageList(fixTitle(header), home)) | 
 | ||||||
|             } |         return newHomePageResponse(request.name, home) | ||||||
|         } |  | ||||||
|         if (items.size <= 0) throw ErrorLoadingException() |  | ||||||
|         return HomePageResponse(items) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun Element.toSearchResult(): SearchResponse { |     private fun Element.toSearchResult(): SearchResponse { | ||||||
|  |  | ||||||
|  | @ -22,22 +22,25 @@ class IdlixProvider : MainAPI() { | ||||||
|         TvType.TvSeries, |         TvType.TvSeries, | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { |     override val mainPage = mainPageOf( | ||||||
|         val document = app.get(mainUrl).document |         "$mainUrl/trending/page/?get=movies" to "Trending Movies", | ||||||
|  |         "$mainUrl/trending/page/?get=tv" to "Trending TV Series", | ||||||
|  |         "$mainUrl/movie/page/" to "Movie Terbaru", | ||||||
|  |         "$mainUrl/tvseries/page/" to "TV Series Terbaru", | ||||||
|  |         "$mainUrl/season/page/" to "Season Terbaru", | ||||||
|  |         "$mainUrl/episode/page/" to "Episode Terbaru", | ||||||
|  |     ) | ||||||
| 
 | 
 | ||||||
|         val homePageList = ArrayList<HomePageList>() |     override suspend fun getMainPage( | ||||||
| 
 |         page: Int, | ||||||
|         document.select("div.items").forEach { block -> |         request: MainPageRequest | ||||||
|             val header = |     ): HomePageResponse { | ||||||
|                 fixTitle(block.previousElementSibling()?.previousElementSibling()?.select("header > h2") |         val url = request.data.split("?") | ||||||
|                     ?.text()!!.trim()) |         val document = app.get("${url.first()}$page/?${url.lastOrNull()}").document | ||||||
|             val items = block.select("article.item").mapNotNull { |         val home = document.select("article").mapNotNull { | ||||||
|             it.toSearchResult() |             it.toSearchResult() | ||||||
|         } |         } | ||||||
|             if (items.isNotEmpty()) homePageList.add(HomePageList(header, items)) |         return newHomePageResponse(request.name, home) | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return HomePageResponse(homePageList) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun getProperLink(uri: String): String { |     private fun getProperLink(uri: String): String { | ||||||
|  | @ -75,7 +78,8 @@ class IdlixProvider : MainAPI() { | ||||||
|         val document = app.get(link).document |         val document = app.get(link).document | ||||||
| 
 | 
 | ||||||
|         return document.select("div.result-item").map { |         return document.select("div.result-item").map { | ||||||
|             val title = it.selectFirst("div.title > a")!!.text().replace(Regex("\\(\\d{4}\\)"), "").trim() |             val title = | ||||||
|  |                 it.selectFirst("div.title > a")!!.text().replace(Regex("\\(\\d{4}\\)"), "").trim() | ||||||
|             val href = getProperLink(it.selectFirst("div.title > a")!!.attr("href")) |             val href = getProperLink(it.selectFirst("div.title > a")!!.attr("href")) | ||||||
|             val posterUrl = it.selectFirst("img")!!.attr("src").toString() |             val posterUrl = it.selectFirst("img")!!.attr("src").toString() | ||||||
|             newMovieSearchResponse(title, href, TvType.TvSeries) { |             newMovieSearchResponse(title, href, TvType.TvSeries) { | ||||||
|  | @ -87,7 +91,9 @@ class IdlixProvider : MainAPI() { | ||||||
|     override suspend fun load(url: String): LoadResponse { |     override suspend fun load(url: String): LoadResponse { | ||||||
|         val document = app.get(url).document |         val document = app.get(url).document | ||||||
| 
 | 
 | ||||||
|         val title = document.selectFirst("div.data > h1")?.text()?.replace(Regex("\\(\\d{4}\\)"), "")?.trim().toString() |         val title = | ||||||
|  |             document.selectFirst("div.data > h1")?.text()?.replace(Regex("\\(\\d{4}\\)"), "") | ||||||
|  |                 ?.trim().toString() | ||||||
|         val poster = document.select("div.poster > img").attr("src").toString() |         val poster = document.select("div.poster > img").attr("src").toString() | ||||||
|         val tags = document.select("div.sgeneros > a").map { it.text() } |         val tags = document.select("div.sgeneros > a").map { it.text() } | ||||||
| 
 | 
 | ||||||
|  | @ -157,7 +163,8 @@ class IdlixProvider : MainAPI() { | ||||||
| 
 | 
 | ||||||
|     private fun getLanguage(str: String): String { |     private fun getLanguage(str: String): String { | ||||||
|         return when { |         return when { | ||||||
|             str.lowercase().contains("indonesia") || str.lowercase().contains("bahasa") -> "Indonesian" |             str.lowercase().contains("indonesia") || str.lowercase() | ||||||
|  |                 .contains("bahasa") -> "Indonesian" | ||||||
|             else -> str |             else -> str | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -203,7 +210,8 @@ class IdlixProvider : MainAPI() { | ||||||
| 
 | 
 | ||||||
|         document.select("script").map { script -> |         document.select("script").map { script -> | ||||||
|             if (script.data().contains("eval(function(p,a,c,k,e,d)")) { |             if (script.data().contains("eval(function(p,a,c,k,e,d)")) { | ||||||
|                 val subData = getAndUnpack(script.data()).substringAfter("\"tracks\":[").substringBefore("],") |                 val subData = | ||||||
|  |                     getAndUnpack(script.data()).substringAfter("\"tracks\":[").substringBefore("],") | ||||||
|                 tryParseJson<List<Tracks>>("[$subData]")?.map { subtitle -> |                 tryParseJson<List<Tracks>>("[$subData]")?.map { subtitle -> | ||||||
|                     subCallback.invoke( |                     subCallback.invoke( | ||||||
|                         SubtitleFile( |                         SubtitleFile( | ||||||
|  |  | ||||||
|  | @ -0,0 +1,204 @@ | ||||||
|  | package com.lagradost.cloudstream3.movieproviders | ||||||
|  | 
 | ||||||
|  | import com.fasterxml.jackson.annotation.JsonProperty | ||||||
|  | import com.lagradost.cloudstream3.* | ||||||
|  | import com.lagradost.cloudstream3.mvvm.safeApiCall | ||||||
|  | import com.lagradost.cloudstream3.utils.AppUtils.parseJson | ||||||
|  | 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.M3u8Helper | ||||||
|  | import com.lagradost.cloudstream3.utils.loadExtractor | ||||||
|  | import java.util.ArrayList | ||||||
|  | 
 | ||||||
|  | class KisskhProvider : MainAPI() { | ||||||
|  |     override var mainUrl = "https://kisskh.me" | ||||||
|  |     override var name = "Kisskh" | ||||||
|  |     override val hasMainPage = true | ||||||
|  |     override val hasDownloadSupport = true | ||||||
|  |     override val supportedTypes = setOf( | ||||||
|  |         TvType.AsianDrama, | ||||||
|  |         TvType.Anime | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     override val mainPage = mainPageOf( | ||||||
|  |         "&type=2&sub=0&country=2&status=0&order=1" to "Movie Pupular", | ||||||
|  |         "&type=2&sub=0&country=2&status=0&order=2" to "Movie Last Update", | ||||||
|  |         "&type=1&sub=0&country=2&status=0&order=1" to "TVSeries Popular", | ||||||
|  |         "&type=1&sub=0&country=2&status=0&order=2" to "TVSeries Last Update", | ||||||
|  |         "&type=3&sub=0&country=0&status=0&order=1" to "Anime Popular", | ||||||
|  |         "&type=3&sub=0&country=0&status=0&order=2" to "Anime Last Update", | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     override suspend fun getMainPage( | ||||||
|  |         page: Int, | ||||||
|  |         request: MainPageRequest | ||||||
|  |     ): HomePageResponse { | ||||||
|  |         val home = app.get("$mainUrl/api/DramaList/List?page=$page${request.data}") | ||||||
|  |             .parsedSafe<Responses>()?.data | ||||||
|  |             ?.mapNotNull { media -> | ||||||
|  |                 media.toSearchResponse() | ||||||
|  |             } ?: throw ErrorLoadingException("Invalid Json reponse") | ||||||
|  |         return newHomePageResponse(request.name, home) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private fun Media.toSearchResponse(): SearchResponse? { | ||||||
|  | 
 | ||||||
|  |         return newAnimeSearchResponse( | ||||||
|  |             title ?: return null, | ||||||
|  |             "$title/$id", | ||||||
|  |             TvType.TvSeries, | ||||||
|  |         ) { | ||||||
|  |             this.posterUrl = thumbnail | ||||||
|  |             addSub(episodesCount) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override suspend fun search(query: String): List<SearchResponse> { | ||||||
|  |         val searchResponse = app.get("$mainUrl/api/DramaList/Search?q=$query&type=0", referer = "$mainUrl/").text | ||||||
|  |         return tryParseJson<ArrayList<Media>>(searchResponse)?.mapNotNull { media -> | ||||||
|  |             media.toSearchResponse() | ||||||
|  |         } ?: throw ErrorLoadingException("Invalid Json reponse") | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private fun getTitle(str: String): String { | ||||||
|  |         return str.replace(Regex("[^a-zA-Z0-9]"), "-") | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override suspend fun load(url: String): LoadResponse? { | ||||||
|  |         val id = url.split("/") | ||||||
|  |         val res = app.get( | ||||||
|  |             "$mainUrl/api/DramaList/Drama/${id.last()}?isq=false", | ||||||
|  |             referer = "$mainUrl/Drama/${ | ||||||
|  |                 getTitle(id.first()) | ||||||
|  |             }?id=${id.last()}" | ||||||
|  |         ).parsedSafe<MediaDetail>() | ||||||
|  |             ?: throw ErrorLoadingException("Invalid Json reponse") | ||||||
|  | 
 | ||||||
|  |         val episodes = res.episodes?.map { eps -> | ||||||
|  |             Episode( | ||||||
|  |                 data = Data(res.title, eps.number, res.id, eps.id).toJson(), | ||||||
|  |                 episode = eps.number | ||||||
|  |             ) | ||||||
|  |         } ?: throw ErrorLoadingException("No Episode") | ||||||
|  | 
 | ||||||
|  |         return newTvSeriesLoadResponse( | ||||||
|  |             res.title ?: return null, | ||||||
|  |             url, | ||||||
|  |             if (res.type == "Movie" || episodes.size == 1) TvType.Movie else TvType.TvSeries, | ||||||
|  |             episodes | ||||||
|  |         ) { | ||||||
|  |             this.posterUrl = res.thumbnail | ||||||
|  |             this.year = res.releaseDate?.split("-")?.first()?.toIntOrNull() | ||||||
|  |             this.plot = res.description | ||||||
|  |             this.tags = listOf("${res.country}", "${res.status}", "${res.type}") | ||||||
|  |             this.showStatus = when (res.status) { | ||||||
|  |                 "Completed" -> ShowStatus.Completed | ||||||
|  |                 "Ongoing" -> ShowStatus.Ongoing | ||||||
|  |                 else -> null | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private fun getLanguage(str: String): String { | ||||||
|  |         return when (str) { | ||||||
|  |             "Indonesia" -> "Indonesian" | ||||||
|  |             else -> str | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override suspend fun loadLinks( | ||||||
|  |         data: String, | ||||||
|  |         isCasting: Boolean, | ||||||
|  |         subtitleCallback: (SubtitleFile) -> Unit, | ||||||
|  |         callback: (ExtractorLink) -> Unit | ||||||
|  |     ): Boolean { | ||||||
|  | 
 | ||||||
|  |         val loadData = parseJson<Data>(data) | ||||||
|  | 
 | ||||||
|  |         app.get( | ||||||
|  |             "$mainUrl/api/DramaList/Episode/${loadData.epsId}.png?err=false&ts=&time=", | ||||||
|  |             referer = "$mainUrl/Drama/${getTitle("${loadData.title}")}/Episode-${loadData.eps}?id=${loadData.id}&ep=${loadData.epsId}&page=0&pageSize=100" | ||||||
|  |         ).parsedSafe<Sources>()?.let { source -> | ||||||
|  |             listOf(source.video, source.thirdParty).apmap { link -> | ||||||
|  |                 safeApiCall { | ||||||
|  |                     if (link?.contains(".m3u8") == true) { | ||||||
|  |                         M3u8Helper.generateM3u8( | ||||||
|  |                             this.name, | ||||||
|  |                             link, | ||||||
|  |                             referer = "$mainUrl/", | ||||||
|  |                             headers = mapOf("Origin" to mainUrl) | ||||||
|  |                         ).forEach(callback) | ||||||
|  |                     } else { | ||||||
|  |                         loadExtractor( | ||||||
|  |                             link?.substringBefore("=http") ?: return@safeApiCall, | ||||||
|  |                             "$mainUrl/", | ||||||
|  |                             subtitleCallback, | ||||||
|  |                             callback | ||||||
|  |                         ) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         app.get("$mainUrl/api/Sub/${loadData.epsId}").parsedSafe<List<Subtitle>>()?.map { sub -> | ||||||
|  |             subtitleCallback.invoke( | ||||||
|  |                 SubtitleFile( | ||||||
|  |                     getLanguage(sub.label ?: return@map), | ||||||
|  |                     sub.src ?: return@map | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return true | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     data class Data( | ||||||
|  |         val title: String?, | ||||||
|  |         val eps: Int?, | ||||||
|  |         val id: Int?, | ||||||
|  |         val epsId: Int?, | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     data class Sources( | ||||||
|  |         @JsonProperty("Video") val video: String?, | ||||||
|  |         @JsonProperty("ThirdParty") val thirdParty: String?, | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     data class Subtitle( | ||||||
|  |         @JsonProperty("src") val src: String?, | ||||||
|  |         @JsonProperty("label") val label: String?, | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     data class Responses( | ||||||
|  |         @JsonProperty("data") val data: ArrayList<Media>? = arrayListOf(), | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     data class Media( | ||||||
|  |         @JsonProperty("episodesCount") val episodesCount: Int?, | ||||||
|  |         @JsonProperty("thumbnail") val thumbnail: String?, | ||||||
|  |         @JsonProperty("id") val id: Int?, | ||||||
|  |         @JsonProperty("title") val title: String?, | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     data class Episodes( | ||||||
|  |         @JsonProperty("id") val id: Int?, | ||||||
|  |         @JsonProperty("number") val number: Int?, | ||||||
|  |         @JsonProperty("sub") val sub: Int?, | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     data class MediaDetail( | ||||||
|  |         @JsonProperty("description") val description: String?, | ||||||
|  |         @JsonProperty("releaseDate") val releaseDate: String?, | ||||||
|  |         @JsonProperty("status") val status: String?, | ||||||
|  |         @JsonProperty("type") val type: String?, | ||||||
|  |         @JsonProperty("country") val country: String?, | ||||||
|  |         @JsonProperty("episodes") val episodes: ArrayList<Episodes>? = arrayListOf(), | ||||||
|  |         @JsonProperty("thumbnail") val thumbnail: String?, | ||||||
|  |         @JsonProperty("id") val id: Int?, | ||||||
|  |         @JsonProperty("title") val title: String?, | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -7,7 +7,6 @@ import com.lagradost.cloudstream3.utils.ExtractorLink | ||||||
| import com.lagradost.cloudstream3.utils.loadExtractor | import com.lagradost.cloudstream3.utils.loadExtractor | ||||||
| import org.jsoup.Jsoup | import org.jsoup.Jsoup | ||||||
| import org.jsoup.nodes.Element | import org.jsoup.nodes.Element | ||||||
| import java.util.* |  | ||||||
| 
 | 
 | ||||||
| class LayarKacaProvider : MainAPI() { | class LayarKacaProvider : MainAPI() { | ||||||
|     override var mainUrl = "https://lk21.xn--6frz82g" |     override var mainUrl = "https://lk21.xn--6frz82g" | ||||||
|  | @ -21,90 +20,33 @@ class LayarKacaProvider : MainAPI() { | ||||||
|         TvType.AsianDrama |         TvType.AsianDrama | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { |     override val mainPage = mainPageOf( | ||||||
|         val document = app.get(mainUrl).document |         "$mainUrl/populer/page/" to "Film Terplopuler", | ||||||
|  |         "$mainUrl/latest-series/page/" to "Series Terbaru", | ||||||
|  |         "$mainUrl/series/asian/page/" to "Film Asian Terbaru", | ||||||
|  |         "$mainUrl/latest/page/" to "Film Upload Terbaru", | ||||||
|  |     ) | ||||||
| 
 | 
 | ||||||
|         val homePageList = ArrayList<HomePageList>() |     override suspend fun getMainPage( | ||||||
| 
 |         page: Int, | ||||||
|         document.select("section.hot-block,section#newseries").forEach { block -> |         request: MainPageRequest | ||||||
|             val header = fixTitle(block.select("footer.load-more > a").text().trim()) |     ): HomePageResponse { | ||||||
|             val items = block.select("div.slider-item").mapNotNull { |         val document = app.get(request.data + page).document | ||||||
|                 it.toTopSearchResult() |         val home = document.select("article.mega-item").mapNotNull { | ||||||
|  |             it.toSearchResult() | ||||||
|         } |         } | ||||||
|             if (items.isNotEmpty()) homePageList.add(HomePageList(header, items)) |         return newHomePageResponse(request.name, home) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|         document.select("div#newest").forEach { block -> |     private fun Element.toSearchResult(): SearchResponse? { | ||||||
|             val header = fixTitle(block.select(".header > h2 > a").text()) |         val title = this.selectFirst("h1.grid-title > a")?.ownText()?.trim() ?: return null | ||||||
|             val items = block.select("div.item").mapNotNull { |         val href = fixUrl(this.selectFirst("h1.grid-title > a")!!.attr("href")) | ||||||
|                 it.toMainSearchResult() |         val posterUrl = fixUrlNull(this.selectFirst(".grid-poster > a > img")?.attr("src")) | ||||||
|             } |         val quality = this.select("div.quality").text().trim() | ||||||
|             if (items.isNotEmpty()) homePageList.add(HomePageList(header, items)) |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         document.select("section#recomendation,section#populer,section#seriespopuler") |  | ||||||
|             .forEach { block -> |  | ||||||
|                 val header = fixTitle(block.select(".header > h2 > a").text()) |  | ||||||
|                 val items = block.select("div.item").mapNotNull { |  | ||||||
|                     it.toBottomSearchResult() |  | ||||||
|                 } |  | ||||||
|                 if (items.isNotEmpty()) homePageList.add(HomePageList(header, items)) |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         return HomePageResponse(homePageList) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private fun Element.toTopSearchResult(): SearchResponse { |  | ||||||
|         val title = this.selectFirst("h3.caption")!!.text().trim() |  | ||||||
|         val href = this.selectFirst("a")!!.attr("href") |  | ||||||
|         val posterUrl = fixUrl(this.selectFirst("a > img")?.attr("src").toString()) |  | ||||||
|         val type = |  | ||||||
|             if (this.select("div.quality-top").isNotEmpty()) TvType.Movie else TvType.TvSeries |  | ||||||
|         return if (type == TvType.Movie) { |  | ||||||
|             val quality = getQualityFromString(this.select("div.quality-top").text().trim()) |  | ||||||
|             newMovieSearchResponse(title, href, TvType.Movie) { |  | ||||||
|                 this.posterUrl = posterUrl |  | ||||||
|                 this.quality = quality |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             val episode = this.select("div.last-episode > span").text().toIntOrNull() |  | ||||||
|             newAnimeSearchResponse(title, href, TvType.TvSeries) { |  | ||||||
|                 this.posterUrl = posterUrl |  | ||||||
|                 addSub(episode) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private fun Element.toMainSearchResult(): SearchResponse { |  | ||||||
|         val title = this.selectFirst("h3.caption")!!.text().trim() |  | ||||||
|         val href = this.selectFirst("a")!!.attr("href") |  | ||||||
|         val posterUrl = fixUrl(this.select("a > img").attr("src").toString()) |  | ||||||
|         val quality = getQualityFromString(this.select("div.quality-top").text().trim()) |  | ||||||
|         return newMovieSearchResponse(title, href, TvType.Movie) { |         return newMovieSearchResponse(title, href, TvType.Movie) { | ||||||
|             this.posterUrl = posterUrl |             this.posterUrl = posterUrl | ||||||
|             this.quality = quality |             addQuality(quality) | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private fun Element.toBottomSearchResult(): SearchResponse { |  | ||||||
|         val title = this.selectFirst("h1.grid-title > a")!!.ownText().trim() |  | ||||||
|         val href = this.selectFirst("h1.grid-title > a")!!.attr("href") |  | ||||||
|         val posterUrl = fixUrl(this.selectFirst(".grid-poster > a > img")?.attr("src").toString()) |  | ||||||
|         val type = if (this.select("div.quality").isNotEmpty()) TvType.Movie else TvType.TvSeries |  | ||||||
|         return if (type == TvType.Movie) { |  | ||||||
|             val quality = getQualityFromString(this.select("div.quality").text().trim()) |  | ||||||
|             return newMovieSearchResponse(title, href, TvType.Movie) { |  | ||||||
|                 this.posterUrl = posterUrl |  | ||||||
|                 this.quality = quality |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             newTvSeriesSearchResponse(title, href, TvType.TvSeries) { |  | ||||||
|                 this.posterUrl = posterUrl |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override suspend fun search(query: String): List<SearchResponse> { |     override suspend fun search(query: String): List<SearchResponse> { | ||||||
|  | @ -138,12 +80,14 @@ class LayarKacaProvider : MainAPI() { | ||||||
|         val trailer = document.selectFirst("div.action-player li > a.fancybox")?.attr("href") |         val trailer = document.selectFirst("div.action-player li > a.fancybox")?.attr("href") | ||||||
|         val rating = |         val rating = | ||||||
|             document.selectFirst("div.content > div:nth-child(6) > h3")?.text()?.toRatingInt() |             document.selectFirst("div.content > div:nth-child(6) > h3")?.text()?.toRatingInt() | ||||||
|         val actors = document.select("div.col-xs-9.content > div:nth-child(3) > h3 > a").map { it.text() } |         val actors = | ||||||
|  |             document.select("div.col-xs-9.content > div:nth-child(3) > h3 > a").map { it.text() } | ||||||
| 
 | 
 | ||||||
|         val recommendations = document.select("div.row.item-media").map { |         val recommendations = document.select("div.row.item-media").map { | ||||||
|             val recName = it.selectFirst("h3")?.text()?.trim().toString() |             val recName = it.selectFirst("h3")?.text()?.trim().toString() | ||||||
|             val recHref = it.selectFirst(".content-media > a")!!.attr("href") |             val recHref = it.selectFirst(".content-media > a")!!.attr("href") | ||||||
|             val recPosterUrl = fixUrl(it.selectFirst(".poster-media > a > img")?.attr("src").toString()) |             val recPosterUrl = | ||||||
|  |                 fixUrl(it.selectFirst(".poster-media > a > img")?.attr("src").toString()) | ||||||
|             newTvSeriesSearchResponse(recName, recHref, TvType.TvSeries) { |             newTvSeriesSearchResponse(recName, recHref, TvType.TvSeries) { | ||||||
|                 this.posterUrl = recPosterUrl |                 this.posterUrl = recPosterUrl | ||||||
|             } |             } | ||||||
|  | @ -172,8 +116,7 @@ class LayarKacaProvider : MainAPI() { | ||||||
|                 this.recommendations = recommendations |                 this.recommendations = recommendations | ||||||
|                 addTrailer(trailer) |                 addTrailer(trailer) | ||||||
|             } |             } | ||||||
|         } |         } else { | ||||||
|         else { |  | ||||||
|             newMovieLoadResponse(title, url, TvType.Movie, url) { |             newMovieLoadResponse(title, url, TvType.Movie, url) { | ||||||
|                 this.posterUrl = poster |                 this.posterUrl = poster | ||||||
|                 this.year = year |                 this.year = year | ||||||
|  | @ -199,7 +142,8 @@ class LayarKacaProvider : MainAPI() { | ||||||
|         val sources = if (data.contains("-episode-")) { |         val sources = if (data.contains("-episode-")) { | ||||||
|             document.select("script").mapNotNull { script -> |             document.select("script").mapNotNull { script -> | ||||||
|                 if (script.data().contains("var data =")) { |                 if (script.data().contains("var data =")) { | ||||||
|                     val scriptData = script.toString().substringAfter("var data = '").substringBefore("';") |                     val scriptData = | ||||||
|  |                         script.toString().substringAfter("var data = '").substringBefore("';") | ||||||
|                     Jsoup.parse(scriptData).select("li").map { |                     Jsoup.parse(scriptData).select("li").map { | ||||||
|                         fixUrl(it.select("a").attr("href")) |                         fixUrl(it.select("a").attr("href")) | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
|  | @ -8,7 +8,6 @@ import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson | ||||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | import com.lagradost.cloudstream3.utils.ExtractorLink | ||||||
| import com.lagradost.cloudstream3.utils.getQualityFromName | import com.lagradost.cloudstream3.utils.getQualityFromName | ||||||
| import org.jsoup.nodes.Element | import org.jsoup.nodes.Element | ||||||
| import java.util.ArrayList |  | ||||||
| 
 | 
 | ||||||
| class MultiplexProvider : MainAPI() { | class MultiplexProvider : MainAPI() { | ||||||
|     override var mainUrl = "https://146.19.24.137" |     override var mainUrl = "https://146.19.24.137" | ||||||
|  | @ -22,45 +21,30 @@ class MultiplexProvider : MainAPI() { | ||||||
|         TvType.AsianDrama |         TvType.AsianDrama | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { |     override val mainPage = mainPageOf( | ||||||
|         val document = app.get(mainUrl).document |         "$mainUrl/genre/top-popular-movies/page/" to "Top Popolar Movies", | ||||||
|  |         "$mainUrl/genre/series-ongoing/page/" to "Series Ongoing", | ||||||
|  |         "$mainUrl/genre/series-barat/page/" to "Series Barat", | ||||||
|  |         "$mainUrl/genre/series-korea/page/" to "Series Korea", | ||||||
|  |     ) | ||||||
| 
 | 
 | ||||||
|         val homePageList = ArrayList<HomePageList>() |     override suspend fun getMainPage( | ||||||
| 
 |         page: Int, | ||||||
|         document.select("div.col-md-12 > div.home-widget").forEach { block -> |         request: MainPageRequest | ||||||
|             val header = fixTitle(block.select("h3.homemodule-title").text()) |     ): HomePageResponse { | ||||||
|             val items = block.select("div.col-md-125").mapNotNull { |         val document = app.get(request.data + page).document | ||||||
|  |         val home = document.select("article.item").mapNotNull { | ||||||
|             it.toSearchResult() |             it.toSearchResult() | ||||||
|         } |         } | ||||||
|             if (items.isNotEmpty()) homePageList.add(HomePageList(header, items)) |         return newHomePageResponse(request.name, home) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|         document.select("div.container.gmr-maincontent") |     private fun Element.toSearchResult(): SearchResponse? { | ||||||
|             .forEach { block -> |         val title = this.selectFirst("h2.entry-title > a")?.text()?.trim() ?: return null | ||||||
|                 val header = fixTitle(block.select("h3.homemodule-title").text()) |         val href = fixUrl(this.selectFirst("a")!!.attr("href")) | ||||||
|                 val items = block.select("article.item").mapNotNull { |         val posterUrl = fixUrlNull(this.selectFirst("a > img")?.attr("data-src")) | ||||||
|                     it.toSearchResult() |         val quality = this.select("div.gmr-quality-item > a").text().trim() | ||||||
|                 } |         return if (quality.isEmpty()) { | ||||||
|                 if (items.isNotEmpty()) homePageList.add(HomePageList(header, items)) |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         document.select("div#idmuvi-rp-2").forEach { block -> |  | ||||||
|             val header = fixTitle(block.selectFirst("h3.widget-title")?.ownText()!!.trim()) |  | ||||||
|             val items = block.select("div.idmuvi-rp ul li").mapNotNull { |  | ||||||
|                 it.toBottomSearchResult() |  | ||||||
|             } |  | ||||||
|             if (items.isNotEmpty()) homePageList.add(HomePageList(header, items)) |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return HomePageResponse(homePageList) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private fun Element.toSearchResult(): SearchResponse { |  | ||||||
|         val title = this.selectFirst("h2.entry-title > a")!!.text().trim() |  | ||||||
|         val href = this.selectFirst("a")!!.attr("href") |  | ||||||
|         val posterUrl = fixUrl(this.selectFirst("a > img")?.attr("data-src").toString()) |  | ||||||
|         val quality = getQualityFromString(this.select("div.gmr-quality-item > a").text().trim()) |  | ||||||
|         return if (quality == null) { |  | ||||||
|             val episode = this.select("div.gmr-numbeps > span").text().toIntOrNull() |             val episode = this.select("div.gmr-numbeps > span").text().toIntOrNull() | ||||||
|             newAnimeSearchResponse(title, href, TvType.TvSeries) { |             newAnimeSearchResponse(title, href, TvType.TvSeries) { | ||||||
|                 this.posterUrl = posterUrl |                 this.posterUrl = posterUrl | ||||||
|  | @ -69,27 +53,24 @@ class MultiplexProvider : MainAPI() { | ||||||
|         } else { |         } else { | ||||||
|             newMovieSearchResponse(title, href, TvType.Movie) { |             newMovieSearchResponse(title, href, TvType.Movie) { | ||||||
|                 this.posterUrl = posterUrl |                 this.posterUrl = posterUrl | ||||||
|                 this.quality = quality |                 addQuality(quality) | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     } |     private fun Element.toBottomSearchResult(): SearchResponse? { | ||||||
| 
 |         val title = this.selectFirst("a > span.idmuvi-rp-title")?.text()?.trim() ?: return null | ||||||
|     private fun Element.toBottomSearchResult(): SearchResponse { |  | ||||||
|         val title = this.selectFirst("a > span.idmuvi-rp-title")!!.text().trim() |  | ||||||
|         val href = this.selectFirst("a")!!.attr("href") |         val href = this.selectFirst("a")!!.attr("href") | ||||||
|         val posterUrl = fixUrl(this.selectFirst("a > img")?.attr("data-src").toString()) |         val posterUrl = fixUrl(this.selectFirst("a > img")?.attr("data-src").toString()) | ||||||
|         return newMovieSearchResponse(title, href, TvType.Movie) { |         return newMovieSearchResponse(title, href, TvType.Movie) { | ||||||
|             this.posterUrl = posterUrl |             this.posterUrl = posterUrl | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override suspend fun search(query: String): List<SearchResponse> { |     override suspend fun search(query: String): List<SearchResponse> { | ||||||
|         val link = "$mainUrl/?s=$query&post_type[]=post&post_type[]=tv" |         val link = "$mainUrl/?s=$query&post_type[]=post&post_type[]=tv" | ||||||
|         val document = app.get(link).document |         val document = app.get(link).document | ||||||
| 
 |         return document.select("article.item").mapNotNull { | ||||||
|         return document.select("div#gmr-main-load > article.item").map { |  | ||||||
|             it.toSearchResult() |             it.toSearchResult() | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -112,9 +93,10 @@ class MultiplexProvider : MainAPI() { | ||||||
|         val rating = |         val rating = | ||||||
|             document.selectFirst("div.gmr-meta-rating > span[itemprop=ratingValue]")?.text() |             document.selectFirst("div.gmr-meta-rating > span[itemprop=ratingValue]")?.text() | ||||||
|                 ?.toRatingInt() |                 ?.toRatingInt() | ||||||
|         val actors = document.select("div.gmr-moviedata").last()?.select("span[itemprop=actors]")?.map { it.select("a").text() } |         val actors = document.select("div.gmr-moviedata").last()?.select("span[itemprop=actors]") | ||||||
|  |             ?.map { it.select("a").text() } | ||||||
| 
 | 
 | ||||||
|         val recommendations = document.select("div.idmuvi-rp ul li").map { |         val recommendations = document.select("div.idmuvi-rp ul li").mapNotNull { | ||||||
|             it.toBottomSearchResult() |             it.toBottomSearchResult() | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -23,20 +23,25 @@ class PhimmoichillProvider : MainAPI() { | ||||||
|         TvType.AsianDrama |         TvType.AsianDrama | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { |     override val mainPage = mainPageOf( | ||||||
|         val document = app.get(mainUrl).document |         "$mainUrl/genre/phim-chieu-rap/page-" to "Phim Chiếu Rạp", | ||||||
|  |         "$mainUrl/list/phim-le/page-" to "Phim Lẻ", | ||||||
|  |         "$mainUrl/list/phim-bo/page-" to "Phim Bộ", | ||||||
|  |         "$mainUrl/genre/phim-hoat-hinh/page-" to "Phim Hoạt Hình", | ||||||
|  |         "$mainUrl/country/phim-han-quoc/page-" to "Phim Hàn Quốc", | ||||||
|  |         "$mainUrl/country/phim-trung-quoc/page-" to "Phim Trung Quốc", | ||||||
|  |         "$mainUrl/country/phim-thai-lan/page-" to "Phim Thái Lan", | ||||||
|  |     ) | ||||||
| 
 | 
 | ||||||
|         val homePageList = ArrayList<HomePageList>() |     override suspend fun getMainPage( | ||||||
| 
 |         page: Int, | ||||||
|         document.select("div.container div.block").forEach { block -> |         request: MainPageRequest | ||||||
|             val header = fixTitle(block.selectFirst("h2")!!.text()) |     ): HomePageResponse { | ||||||
|             val items = block.select("li.item").mapNotNull { |         val document = app.get(request.data + page).document | ||||||
|  |         val home = document.select("li.item").mapNotNull { | ||||||
|             it.toSearchResult() |             it.toSearchResult() | ||||||
|         } |         } | ||||||
|             if (items.isNotEmpty()) homePageList.add(HomePageList(header, items)) |         return newHomePageResponse(request.name, home) | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return HomePageResponse(homePageList) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun decode(input: String): String? = URLDecoder.decode(input, "utf-8") |     private fun decode(input: String): String? = URLDecoder.decode(input, "utf-8") | ||||||
|  |  | ||||||
|  | @ -15,7 +15,7 @@ import org.jsoup.nodes.Element | ||||||
| import java.net.URI | import java.net.URI | ||||||
| 
 | 
 | ||||||
| class RebahinProvider : MainAPI() { | class RebahinProvider : MainAPI() { | ||||||
|     override var mainUrl = "http://167.88.14.149" |     override var mainUrl = "http://104.237.198.194" | ||||||
|     override var name = "Rebahin" |     override var name = "Rebahin" | ||||||
|     override val hasMainPage = true |     override val hasMainPage = true | ||||||
|     override var lang = "id" |     override var lang = "id" | ||||||
|  |  | ||||||
|  | @ -23,20 +23,24 @@ class UakinoProvider : MainAPI() { | ||||||
|         TvType.Anime |         TvType.Anime | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { |     override val mainPage = mainPageOf( | ||||||
|         val document = app.get(mainUrl).document |         "$mainUrl/filmy/page/" to "Фільми", | ||||||
|  |         "$mainUrl/seriesss/page/" to "Серіали", | ||||||
|  |         "$mainUrl/seriesss/doramy/page/" to "Дорами", | ||||||
|  |         "$mainUrl/animeukr/page/" to "Аніме", | ||||||
|  |         "$mainUrl/cartoon/page/" to "Мультфільми", | ||||||
|  |         "$mainUrl/cartoon/cartoonseries/page/" to "Мультсеріали", | ||||||
|  |     ) | ||||||
| 
 | 
 | ||||||
|         val homePageList = ArrayList<HomePageList>() |     override suspend fun getMainPage( | ||||||
| 
 |         page: Int, | ||||||
|         document.select("div.main-section-inner").forEach { block -> |         request: MainPageRequest | ||||||
|             val header = block.selectFirst("p.sidebar-title")?.text()?.trim().toString() |     ): HomePageResponse { | ||||||
|             val items = block.select("div.owl-item, div.movie-item").map { |         val document = app.get(request.data + page).document | ||||||
|  |         val home = document.select("div.owl-item, div.movie-item").map { | ||||||
|             it.toSearchResponse() |             it.toSearchResponse() | ||||||
|         } |         } | ||||||
|             if (items.isNotEmpty()) homePageList.add(HomePageList(header, items)) |         return newHomePageResponse(request.name, home) | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return HomePageResponse(homePageList) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun Element.toSearchResponse(): SearchResponse { |     private fun Element.toSearchResponse(): SearchResponse { | ||||||
|  | @ -70,7 +74,8 @@ class UakinoProvider : MainAPI() { | ||||||
|         val poster = fixUrl(document.selectFirst("div.film-poster img")?.attr("src").toString()) |         val poster = fixUrl(document.selectFirst("div.film-poster img")?.attr("src").toString()) | ||||||
|         val tags = document.select("div.film-info > div:nth-child(4) a").map { it.text() } |         val tags = document.select("div.film-info > div:nth-child(4) a").map { it.text() } | ||||||
|         val year = document.select("div.film-info > div:nth-child(2) a").text().toIntOrNull() |         val year = document.select("div.film-info > div:nth-child(2) a").text().toIntOrNull() | ||||||
|         val tvType = if (url.contains(Regex("(/anime-series)|(/seriesss)|(/cartoonseries)"))) TvType.TvSeries else TvType.Movie |         val tvType = | ||||||
|  |             if (url.contains(Regex("(/anime-series)|(/seriesss)|(/cartoonseries)"))) TvType.TvSeries else TvType.Movie | ||||||
|         val description = document.selectFirst("div[itemprop=description]")?.text()?.trim() |         val description = document.selectFirst("div[itemprop=description]")?.text()?.trim() | ||||||
|         val trailer = document.selectFirst("iframe#pre")?.attr("data-src") |         val trailer = document.selectFirst("iframe#pre")?.attr("data-src") | ||||||
|         val rating = document.selectFirst("div.film-info > div:nth-child(8) div.fi-desc")?.text() |         val rating = document.selectFirst("div.film-info > div:nth-child(8) div.fi-desc")?.text() | ||||||
|  | @ -83,7 +88,8 @@ class UakinoProvider : MainAPI() { | ||||||
| 
 | 
 | ||||||
|         return if (tvType == TvType.TvSeries) { |         return if (tvType == TvType.TvSeries) { | ||||||
|             val id = url.split("/").last().split("-").first() |             val id = url.split("/").last().split("-").first() | ||||||
|             val episodes = app.get("$mainUrl/engine/ajax/playlists.php?news_id=$id&xfield=playlist&time=${Date().time}") |             val episodes = | ||||||
|  |                 app.get("$mainUrl/engine/ajax/playlists.php?news_id=$id&xfield=playlist&time=${Date().time}") | ||||||
|                     .parsedSafe<Responses>()?.response.let { |                     .parsedSafe<Responses>()?.response.let { | ||||||
|                         Jsoup.parse(it.toString()).select("ul > li").mapNotNull { eps -> |                         Jsoup.parse(it.toString()).select("ul > li").mapNotNull { eps -> | ||||||
|                             val href = fixUrl(eps.attr("data-file")) |                             val href = fixUrl(eps.attr("data-file")) | ||||||
|  |  | ||||||
|  | @ -9,7 +9,7 @@ import com.lagradost.cloudstream3.utils.loadExtractor | ||||||
| import org.jsoup.nodes.Element | import org.jsoup.nodes.Element | ||||||
| 
 | 
 | ||||||
| class YomoviesProvider : MainAPI() { | class YomoviesProvider : MainAPI() { | ||||||
|     override var mainUrl = "https://yomovies.vip" |     override var mainUrl = "https://yomovies.skin" | ||||||
|     override var name = "Yomovies" |     override var name = "Yomovies" | ||||||
|     override val hasMainPage = true |     override val hasMainPage = true | ||||||
|     override var lang = "hi" |     override var lang = "hi" | ||||||
|  | @ -19,21 +19,25 @@ class YomoviesProvider : MainAPI() { | ||||||
|         TvType.TvSeries, |         TvType.TvSeries, | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { |     override val mainPage = mainPageOf( | ||||||
|         val document = app.get(mainUrl).document |         "$mainUrl/most-favorites/page/" to "Most Viewed", | ||||||
|  |         "$mainUrl/genre/web-series/page/" to "Web Series Movies", | ||||||
|  |         "$mainUrl/genre/dual-audio/page/" to "Dual Audio Movies", | ||||||
|  |         "$mainUrl/genre/bollywood/page/" to "Bollywood Movies", | ||||||
|  |         "$mainUrl/genre/tv-shows/page/" to "TV Shows Movies", | ||||||
|  |         "$mainUrl/genre/hollywood/page/" to "Hollywood Movies", | ||||||
|  |         "$mainUrl/series/page/" to "All TV Series", | ||||||
|  |     ) | ||||||
| 
 | 
 | ||||||
|         val homePageList = ArrayList<HomePageList>() |     override suspend fun getMainPage( | ||||||
| 
 |         page: Int, | ||||||
|         document.select("div.movies-list-wrap.mlw-topview,div.movies-list-wrap.mlw-latestmovie") |         request: MainPageRequest | ||||||
|             .forEach { block -> |     ): HomePageResponse { | ||||||
|                 val header = fixTitle(block.selectFirst("div.ml-title span")?.text() ?: "") |         val document = app.get(request.data + page).document | ||||||
|                 val items = block.select("div.ml-item").mapNotNull { |         val home = document.select("div.ml-item").mapNotNull { | ||||||
|             it.toSearchResult() |             it.toSearchResult() | ||||||
|         } |         } | ||||||
|                 if (items.isNotEmpty()) homePageList.add(HomePageList(header, items)) |         return newHomePageResponse(request.name, home) | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         return HomePageResponse(homePageList) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun Element.toSearchResult(): SearchResponse? { |     private fun Element.toSearchResult(): SearchResponse? { | ||||||
|  | @ -63,8 +67,9 @@ class YomoviesProvider : MainAPI() { | ||||||
|         val year = document.select("div.mvici-right p:nth-child(3) a").text().trim() |         val year = document.select("div.mvici-right p:nth-child(3) a").text().trim() | ||||||
|             .toIntOrNull() |             .toIntOrNull() | ||||||
|         val tvType = if (document.selectFirst("div.les-content") |         val tvType = if (document.selectFirst("div.les-content") | ||||||
|                 ?.select("a")?.size!! <= 1 |                 ?.select("a")?.size!! > 1 || document.selectFirst("ul.idTabs li strong")?.text() | ||||||
|         ) TvType.Movie else TvType.TvSeries |                 ?.contains(Regex("(?i)(EP\\s?[0-9]+)|(episode\\s?[0-9]+)")) == true | ||||||
|  |         ) TvType.TvSeries else TvType.Movie | ||||||
|         val description = document.selectFirst("p.f-desc")?.text()?.trim() |         val description = document.selectFirst("p.f-desc")?.text()?.trim() | ||||||
|         val trailer = fixUrlNull(document.select("iframe#iframe-trailer").attr("src")) |         val trailer = fixUrlNull(document.select("iframe#iframe-trailer").attr("src")) | ||||||
|         val rating = document.select("div.mvici-right > div.imdb_r span").text().toRatingInt() |         val rating = document.select("div.mvici-right > div.imdb_r span").text().toRatingInt() | ||||||
|  | @ -74,14 +79,25 @@ class YomoviesProvider : MainAPI() { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return if (tvType == TvType.TvSeries) { |         return if (tvType == TvType.TvSeries) { | ||||||
|             val episodes = document.select("div.les-content a").map { |             val episodes = if (document.selectFirst("div.les-title strong")?.text().toString() | ||||||
|                 val href = it.attr("href") |                     .contains(Regex("(?i)EP\\s?[0-9]+|Episode\\s?[0-9]+")) | ||||||
|                 val name = it.text().trim() |             ) { | ||||||
|  |                 document.select("ul.idTabs li").map { | ||||||
|  |                     val id = it.select("a").attr("href") | ||||||
|                     Episode( |                     Episode( | ||||||
|                     data = href, |                         data = fixUrl(document.select("div$id iframe").attr("src")), | ||||||
|                     name = name, |                         name = it.select("strong").text(), | ||||||
|                     ) |                     ) | ||||||
|                 } |                 } | ||||||
|  |             } else { | ||||||
|  |                 document.select("div.les-content a").map { | ||||||
|  |                     Episode( | ||||||
|  |                         data = it.attr("href"), | ||||||
|  |                         name = it.text().trim(), | ||||||
|  |                     ) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) { |             newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) { | ||||||
|                 this.posterUrl = poster |                 this.posterUrl = poster | ||||||
|                 this.year = year |                 this.year = year | ||||||
|  | @ -113,6 +129,7 @@ class YomoviesProvider : MainAPI() { | ||||||
|         callback: (ExtractorLink) -> Unit |         callback: (ExtractorLink) -> Unit | ||||||
|     ): Boolean { |     ): Boolean { | ||||||
| 
 | 
 | ||||||
|  |         if (data.startsWith(mainUrl)) { | ||||||
|             app.get(data).document.select("div.movieplay iframe").map { fixUrl(it.attr("src")) } |             app.get(data).document.select("div.movieplay iframe").map { fixUrl(it.attr("src")) } | ||||||
|                 .apmap { source -> |                 .apmap { source -> | ||||||
|                     safeApiCall { |                     safeApiCall { | ||||||
|  | @ -133,6 +150,9 @@ class YomoviesProvider : MainAPI() { | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |         } else { | ||||||
|  |             loadExtractor(data, "$mainUrl/", subtitleCallback, callback) | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         return true |         return true | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -232,6 +232,7 @@ val extractorApis: Array<ExtractorApi> = arrayOf( | ||||||
|     SBfull(), |     SBfull(), | ||||||
|     // Streamhub(), cause Streamhub2() works |     // Streamhub(), cause Streamhub2() works | ||||||
|     Streamhub2(), |     Streamhub2(), | ||||||
|  |     Ssbstream(), | ||||||
| 
 | 
 | ||||||
|     FEmbed(), |     FEmbed(), | ||||||
|     FeHD(), |     FeHD(), | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue