forked from recloudstream/cloudstream
		
	4 new spanish providers and extractors (#556)
* Cuevana, Pelisflix, Seriesflix, EntrePeliculasySeries, Okru Extractor Co-authored-by: Osten <balt.758@gmail.com>
This commit is contained in:
		
							parent
							
								
									5053dff3f2
								
							
						
					
					
						commit
						bd14fad607
					
				
					 9 changed files with 952 additions and 0 deletions
				
			
		|  | @ -44,6 +44,10 @@ object APIHolder { | |||
|         DubbedAnimeProvider(), | ||||
|         DoramasYTProvider(), | ||||
|         CinecalidadProvider(), | ||||
|         CuevanaProvider(), | ||||
|         EntrepeliculasyseriesProvider(), | ||||
|         PelisflixProvider(), | ||||
|         SeriesflixProvider(), | ||||
|         IHaveNoTvProvider(), // Documentaries provider | ||||
|         //LookMovieProvider(), // RECAPTCHA (Please allow up to 5 seconds...) | ||||
|         VMoveeProvider(), | ||||
|  |  | |||
|  | @ -0,0 +1,35 @@ | |||
| package com.lagradost.cloudstream3.extractors | ||||
| 
 | ||||
| import com.lagradost.cloudstream3.utils.* | ||||
| import com.lagradost.cloudstream3.app | ||||
| 
 | ||||
| open class OkRu : ExtractorApi() { | ||||
|     override val name = "Okru" | ||||
|     override val mainUrl = "http://ok.ru" | ||||
|     override val requiresReferer = false | ||||
| 
 | ||||
|     override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? { | ||||
|         val doc = app.get(url).document | ||||
|         val urlString = doc.select("div[data-options]").attr("data-options") | ||||
|             .substringAfter("\\\"videos\\\":[{\\\"name\\\":\\\"") | ||||
|             .substringBefore("]") | ||||
|         urlString.split("{\\\"name\\\":\\\"").reversed().forEach { | ||||
|             val extractedUrl = it.substringAfter("url\\\":\\\"") | ||||
|                 .substringBefore("\\\"") | ||||
|                 .replace("\\\\u0026", "&") | ||||
|             val Quality = it.uppercase().substringBefore("\\\"") | ||||
| 
 | ||||
|             if (extractedUrl.isNotBlank())  return listOf( | ||||
|                 ExtractorLink( | ||||
|                     name, | ||||
|                     "$name ${Quality}", | ||||
|                     extractedUrl, | ||||
|                     url, | ||||
|                     Qualities.Unknown.value, | ||||
|                     isM3u8 = false | ||||
|                 ) | ||||
|             ) | ||||
|         } | ||||
|         return null | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,39 @@ | |||
| package com.lagradost.cloudstream3.extractors | ||||
| 
 | ||||
| import com.lagradost.cloudstream3.utils.* | ||||
| import com.lagradost.cloudstream3.app | ||||
| import com.fasterxml.jackson.annotation.JsonProperty | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.parseJson | ||||
| 
 | ||||
| class Cinestart: Tomatomatela() { | ||||
|     override val name: String = "Cinestart" | ||||
|     override val mainUrl: String = "https://cinestart.net" | ||||
|     override val details = "vr.php?v=" | ||||
| } | ||||
| 
 | ||||
| open class Tomatomatela : ExtractorApi() { | ||||
|     override val name = "Tomatomatela" | ||||
|     override val mainUrl = "https://tomatomatela.com" | ||||
|     override val requiresReferer = false | ||||
|     private data class Tomato ( | ||||
|         @JsonProperty("status") val status: Int, | ||||
|         @JsonProperty("file") val file: String | ||||
|     ) | ||||
|     open val details = "details.php?v=" | ||||
|     override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? { | ||||
|         val link = url.replace("$mainUrl/embed.html#","$mainUrl/$details") | ||||
|         val server = app.get(link, allowRedirects = false).text | ||||
|         val json = parseJson<Tomato>(server) | ||||
|         if (json.status == 200) return listOf( | ||||
|             ExtractorLink( | ||||
|                 name, | ||||
|                 name, | ||||
|                 json.file, | ||||
|                 "", | ||||
|                 Qualities.Unknown.value, | ||||
|                 isM3u8 = false | ||||
|             ) | ||||
|         ) | ||||
|         return null | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,251 @@ | |||
| package com.lagradost.cloudstream3.movieproviders | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonProperty | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.parseJson | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.mvvm.logError | ||||
| import com.lagradost.cloudstream3.utils.* | ||||
| import java.util.* | ||||
| 
 | ||||
| class CuevanaProvider:MainAPI() { | ||||
|     override val mainUrl = "https://cuevana3.io" | ||||
|     override val name = "Cuevana" | ||||
|     override val lang = "es" | ||||
|     override val hasMainPage = true | ||||
|     override val hasChromecastSupport = true | ||||
|     override val hasDownloadSupport = true | ||||
|     override val supportedTypes = setOf( | ||||
|         TvType.Movie, | ||||
|         TvType.TvSeries, | ||||
|     ) | ||||
|     override suspend fun getMainPage(): HomePageResponse { | ||||
|         val items = ArrayList<HomePageList>() | ||||
|         val urls = listOf( | ||||
|             Pair(mainUrl, "Recientemente actualizadas"), | ||||
|             Pair("$mainUrl/estrenos/", "Estrenos"), | ||||
|         ) | ||||
|         for (i in urls) { | ||||
|             try { | ||||
|                 val soup = app.get(i.first).document | ||||
|                 val home = soup.select("section li.xxx.TPostMv").map { | ||||
|                     val title = it.selectFirst("h2.Title").text() | ||||
|                     val link = it.selectFirst("a").attr("href") | ||||
|                     TvSeriesSearchResponse( | ||||
|                         title, | ||||
|                         link, | ||||
|                         this.name, | ||||
|                         if (link.contains("/pelicula/")) TvType.Movie else TvType.TvSeries, | ||||
|                         it.selectFirst("img.lazy").attr("data-src"), | ||||
|                         null, | ||||
|                         null, | ||||
|                     ) | ||||
|                 } | ||||
| 
 | ||||
|                 items.add(HomePageList(i.second, home)) | ||||
|             } catch (e: Exception) { | ||||
|                 logError(e) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (items.size <= 0) throw ErrorLoadingException() | ||||
|         return HomePageResponse(items) | ||||
|     } | ||||
|     override suspend fun search(query: String): List<SearchResponse> { | ||||
|         val url = "$mainUrl/?s=${query}" | ||||
|         val document = app.get(url).document | ||||
| 
 | ||||
|         return document.select("li.xxx.TPostMv").map { | ||||
|             val title = it.selectFirst("h2.Title").text() | ||||
|             val href = it.selectFirst("a").attr("href") | ||||
|             val image = it.selectFirst("img.lazy").attr("data-src") | ||||
|             val isSerie = href.contains("/serie/") | ||||
| 
 | ||||
|             if (isSerie) { | ||||
|                 TvSeriesSearchResponse( | ||||
|                     title, | ||||
|                     href, | ||||
|                     this.name, | ||||
|                     TvType.TvSeries, | ||||
|                     image, | ||||
|                     null, | ||||
|                     null | ||||
|                 ) | ||||
|             } else { | ||||
|                 MovieSearchResponse( | ||||
|                     title, | ||||
|                     href, | ||||
|                     this.name, | ||||
|                     TvType.Movie, | ||||
|                     image, | ||||
|                     null | ||||
|                 ) | ||||
|             } | ||||
|         }.toList() | ||||
|     } | ||||
|     override suspend fun load(url: String): LoadResponse? { | ||||
|         val soup = app.get(url, timeout = 120).document | ||||
|         val title = soup.selectFirst("h1.Title").text() | ||||
|         val description = soup.selectFirst(".Description p")?.text()?.trim() | ||||
|         val poster: String? = soup.selectFirst(".movtv-info div.Image img").attr("data-src") | ||||
|         val year1 = soup.selectFirst("footer p.meta").toString() | ||||
|         val yearRegex = Regex("(\\d+)<\\/span>") | ||||
|         val year =  yearRegex.findAll(year1).map { | ||||
|             it.value.replace("</span>","") | ||||
|         }.toList().first().toIntOrNull() | ||||
|         val episodes = soup.select(".all-episodes li.TPostMv article").map { li -> | ||||
|             val href = li.select("a").attr("href") | ||||
|             val epThumb = | ||||
|                 li.selectFirst("div.Image img").attr("data-src") ?: li.selectFirst("img.lazy").attr("data-srcc") | ||||
|             val name = li.selectFirst("h2.Title").text() | ||||
|             TvSeriesEpisode( | ||||
|                 name, | ||||
|                 null, | ||||
|                 null, | ||||
|                 href, | ||||
|                 fixUrl(epThumb) | ||||
|             ) | ||||
|         } | ||||
|         return when (val tvType = if (episodes.isEmpty()) TvType.Movie else TvType.TvSeries) { | ||||
|             TvType.TvSeries -> { | ||||
|                 TvSeriesLoadResponse( | ||||
|                     title, | ||||
|                     url, | ||||
|                     this.name, | ||||
|                     tvType, | ||||
|                     episodes, | ||||
|                     poster, | ||||
|                     year, | ||||
|                     description, | ||||
|                 ) | ||||
|             } | ||||
|             TvType.Movie -> { | ||||
|                 MovieLoadResponse( | ||||
|                     title, | ||||
|                     url, | ||||
|                     this.name, | ||||
|                     tvType, | ||||
|                     url, | ||||
|                     poster, | ||||
|                     year, | ||||
|                     description, | ||||
|                 ) | ||||
|             } | ||||
|             else -> null | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     data class Femcuevana( | ||||
|         @JsonProperty("url") val url: String, | ||||
|     ) | ||||
|     override suspend fun loadLinks( | ||||
|         data: String, | ||||
|         isCasting: Boolean, | ||||
|         subtitleCallback: (SubtitleFile) -> Unit, | ||||
|         callback: (ExtractorLink) -> Unit | ||||
|     ): Boolean { | ||||
|         app.get(data).document.select("div.TPlayer.embed_div iframe").apmap { | ||||
|             val iframe = fixUrl(it.attr("data-src")) | ||||
|             if (iframe.contains("api.cuevana3.io/fembed/")) { | ||||
|                 val femregex = Regex("(https.\\/\\/api\\.cuevana3\\.io\\/fembed\\/\\?h=[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)") | ||||
|                 femregex.findAll(iframe).map { femreg -> | ||||
|                     femreg.value | ||||
|                 }.toList().apmap { fem -> | ||||
|                     val key = fem.replace("https://api.cuevana3.io/fembed/?h=","") | ||||
|                     val url = app.post("https://api.cuevana3.io/fembed/api.php", allowRedirects = false, headers = mapOf("Host" to "api.cuevana3.io", | ||||
|                         "User-Agent" to USER_AGENT, | ||||
|                         "Accept" to "application/json, text/javascript, */*; q=0.01", | ||||
|                         "Accept-Language" to "en-US,en;q=0.5", | ||||
|                         "Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8", | ||||
|                         "X-Requested-With" to "XMLHttpRequest", | ||||
|                         "Origin" to "https://api.cuevana3.io", | ||||
|                         "DNT" to "1", | ||||
|                         "Connection" to "keep-alive", | ||||
|                         "Sec-Fetch-Dest" to "empty", | ||||
|                         "Sec-Fetch-Mode" to "cors", | ||||
|                         "Sec-Fetch-Site" to "same-origin",), | ||||
|                         data = mapOf(Pair("h",key))).text | ||||
|                     val json = parseJson<Femcuevana>(url) | ||||
|                     val link = json.url | ||||
|                     if (link.contains("fembed")) { | ||||
|                         loadExtractor(link, data, callback) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             if (iframe.contains("tomatomatela")) { | ||||
|                 val tomatoRegex = Regex("(\\/\\/apialfa.tomatomatela.com\\/ir\\/player.php\\?h=[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)") | ||||
|                 tomatoRegex.findAll(iframe).map { tomreg -> | ||||
|                     tomreg.value | ||||
|                 }.toList().apmap { tom -> | ||||
|                     val tomkey = tom.replace("//apialfa.tomatomatela.com/ir/player.php?h=","") | ||||
|                     app.post("https://apialfa.tomatomatela.com/ir/rd.php", allowRedirects = false, | ||||
|                         headers = mapOf("Host" to "apialfa.tomatomatela.com", | ||||
|                             "User-Agent" to USER_AGENT, | ||||
|                             "Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", | ||||
|                             "Accept-Language" to "en-US,en;q=0.5", | ||||
|                             "Content-Type" to "application/x-www-form-urlencoded", | ||||
|                             "Origin" to "null", | ||||
|                             "DNT" to "1", | ||||
|                             "Connection" to "keep-alive", | ||||
|                             "Upgrade-Insecure-Requests" to "1", | ||||
|                             "Sec-Fetch-Dest" to "iframe", | ||||
|                             "Sec-Fetch-Mode" to "navigate", | ||||
|                             "Sec-Fetch-Site" to "same-origin",), | ||||
|                         data = mapOf(Pair("url",tomkey)) | ||||
|                     ).response.headers.values("location").apmap { loc -> | ||||
|                         if (loc.contains("goto_ddh.php")) { | ||||
|                             val gotoregex = Regex("(\\/\\/api.cuevana3.io\\/ir\\/goto_ddh.php\\?h=[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)") | ||||
|                             gotoregex.findAll(loc).map { goreg -> | ||||
|                                 goreg.value.replace("//api.cuevana3.io/ir/goto_ddh.php?h=","") | ||||
|                             }.toList().apmap { gotolink -> | ||||
|                                 app.post("https://api.cuevana3.io/ir/redirect_ddh.php", allowRedirects = false, | ||||
|                                     headers = mapOf("Host" to "api.cuevana3.io", | ||||
|                                         "User-Agent" to "Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0", | ||||
|                                         "Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", | ||||
|                                         "Accept-Language" to "en-US,en;q=0.5", | ||||
|                                         "Content-Type" to "application/x-www-form-urlencoded", | ||||
|                                         "Origin" to "null", | ||||
|                                         "DNT" to "1", | ||||
|                                         "Connection" to "keep-alive", | ||||
|                                         "Upgrade-Insecure-Requests" to "1", | ||||
|                                         "Sec-Fetch-Dest" to "iframe", | ||||
|                                         "Sec-Fetch-Mode" to "navigate", | ||||
|                                         "Sec-Fetch-Site" to "same-origin",), | ||||
|                                     data = mapOf(Pair("url",gotolink)) | ||||
|                                 ).response.headers.values("location").apmap { golink -> | ||||
|                                     loadExtractor(golink, data, callback) | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                         if (loc.contains("index.php?h=")) { | ||||
|                             val indexRegex = Regex("(\\/\\/api.cuevana3.io\\/sc\\/index.php\\?h=[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)") | ||||
|                             indexRegex.findAll(loc).map { indreg -> | ||||
|                                 indreg.value.replace("//api.cuevana3.io/sc/index.php?h=","") | ||||
|                             }.toList().apmap { inlink -> | ||||
|                                 app.post("https://api.cuevana3.io/sc/r.php", allowRedirects = false, | ||||
|                                     headers = mapOf("Host" to "api.cuevana3.io", | ||||
|                                         "User-Agent" to USER_AGENT, | ||||
|                                         "Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", | ||||
|                                         "Accept-Language" to "en-US,en;q=0.5", | ||||
|                                         "Accept-Encoding" to "gzip, deflate, br", | ||||
|                                         "Content-Type" to "application/x-www-form-urlencoded", | ||||
|                                         "Origin" to "null", | ||||
|                                         "DNT" to "1", | ||||
|                                         "Connection" to "keep-alive", | ||||
|                                         "Upgrade-Insecure-Requests" to "1", | ||||
|                                         "Sec-Fetch-Dest" to "iframe", | ||||
|                                         "Sec-Fetch-Mode" to "navigate", | ||||
|                                         "Sec-Fetch-Site" to "same-origin", | ||||
|                                         "Sec-Fetch-User" to "?1",), | ||||
|                                     data = mapOf(Pair("h",inlink)) | ||||
|                                 ).response.headers.values("location").apmap { link -> | ||||
|                                     loadExtractor(link, data, callback) | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return true | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,174 @@ | |||
| package com.lagradost.cloudstream3.movieproviders | ||||
| 
 | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.mvvm.logError | ||||
| import com.lagradost.cloudstream3.utils.* | ||||
| import java.util.* | ||||
| 
 | ||||
| class EntrepeliculasyseriesProvider:MainAPI() { | ||||
|     override val mainUrl = "https://entrepeliculasyseries.nu" | ||||
|     override val name = "EntrePeliculasySeries" | ||||
|     override val lang = "es" | ||||
|     override val hasMainPage = true | ||||
|     override val hasChromecastSupport = true | ||||
|     override val hasDownloadSupport = true | ||||
|     override val supportedTypes = setOf( | ||||
|         TvType.Movie, | ||||
|         TvType.TvSeries, | ||||
|     ) | ||||
|     override val vpnStatus = VPNStatus.MightBeNeeded //Due to evoload sometimes not loading | ||||
| 
 | ||||
|     override suspend fun getMainPage(): HomePageResponse { | ||||
|         val items = ArrayList<HomePageList>() | ||||
|         val urls = listOf( | ||||
|             Pair("$mainUrl/series/", "Series"), | ||||
|             Pair("$mainUrl/peliculas/", "Peliculas"), | ||||
|             Pair("$mainUrl/anime/", "Animes"), | ||||
|         ) | ||||
| 
 | ||||
|         for (i in urls) { | ||||
|             try { | ||||
|                 val soup = app.get(i.first).document | ||||
|                 val home = soup.select("ul.list-movie li").map { | ||||
|                     val title = it.selectFirst("a.link-title h2").text() | ||||
|                     val link = it.selectFirst("a").attr("href") | ||||
|                     TvSeriesSearchResponse( | ||||
|                         title, | ||||
|                         link, | ||||
|                         this.name, | ||||
|                         if (link.contains("/pelicula/")) TvType.Movie else TvType.TvSeries, | ||||
|                         it.selectFirst("a.poster img").attr("src"), | ||||
|                         null, | ||||
|                         null, | ||||
|                     ) | ||||
|                 } | ||||
| 
 | ||||
|                 items.add(HomePageList(i.second, home)) | ||||
|             } catch (e: Exception) { | ||||
|                 logError(e) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (items.size <= 0) throw ErrorLoadingException() | ||||
|         return HomePageResponse(items) | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun search(query: String): List<SearchResponse> { | ||||
|         val url = "$mainUrl/?s=${query}" | ||||
|         val document = app.get(url).document | ||||
| 
 | ||||
|         return document.select("li.xxx.TPostMv").map { | ||||
|             val title = it.selectFirst("h2.Title").text() | ||||
|             val href = it.selectFirst("a").attr("href") | ||||
|             val image = it.selectFirst("img.lazy").attr("data-src") | ||||
|             val isMovie = href.contains("/pelicula/") | ||||
| 
 | ||||
|             if (isMovie) { | ||||
|                 MovieSearchResponse( | ||||
|                     title, | ||||
|                     href, | ||||
|                     this.name, | ||||
|                     TvType.Movie, | ||||
|                     image, | ||||
|                     null | ||||
|                 ) | ||||
|             } else { | ||||
|                 TvSeriesSearchResponse( | ||||
|                     title, | ||||
|                     href, | ||||
|                     this.name, | ||||
|                     TvType.TvSeries, | ||||
|                     image, | ||||
|                     null, | ||||
|                     null | ||||
|                 ) | ||||
|             } | ||||
|         }.toList() | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     override suspend fun load(url: String): LoadResponse? { | ||||
|         val soup = app.get(url, timeout = 120).document | ||||
| 
 | ||||
|         val title = soup.selectFirst("h1.title-post").text() | ||||
|         val description = soup.selectFirst("p.text-content:nth-child(3)")?.text()?.trim() | ||||
|         val poster: String? = soup.selectFirst("article.TPost img.lazy").attr("data-src") | ||||
|         val episodes = soup.select(".TPostMv article").map { li -> | ||||
|             val href = (li.select("a") ?: li.select(".C a") ?: li.select("article a")).attr("href") | ||||
|             val epThumb = li.selectFirst("div.Image img").attr("data-src") | ||||
|             val name = li.selectFirst("h2.Title").text() | ||||
|             TvSeriesEpisode( | ||||
|                 name, | ||||
|                 null, | ||||
|                 null, | ||||
|                 href, | ||||
|                 epThumb | ||||
|             ) | ||||
|         } | ||||
|         return when (val tvType = if (url.contains("/pelicula/")) TvType.Movie else TvType.TvSeries) { | ||||
|             TvType.TvSeries -> { | ||||
|                 TvSeriesLoadResponse( | ||||
|                     title, | ||||
|                     url, | ||||
|                     this.name, | ||||
|                     tvType, | ||||
|                     episodes, | ||||
|                     poster, | ||||
|                     null, | ||||
|                     description, | ||||
|                 ) | ||||
|             } | ||||
|             TvType.Movie -> { | ||||
|                 MovieLoadResponse( | ||||
|                     title, | ||||
|                     url, | ||||
|                     this.name, | ||||
|                     tvType, | ||||
|                     url, | ||||
|                     poster, | ||||
|                     null, | ||||
|                     description, | ||||
|                 ) | ||||
|             } | ||||
|             else -> null | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     override suspend fun loadLinks( | ||||
|         data: String, | ||||
|         isCasting: Boolean, | ||||
|         subtitleCallback: (SubtitleFile) -> Unit, | ||||
|         callback: (ExtractorLink) -> Unit | ||||
|     ): Boolean { | ||||
|         app.get(data).document.select(".video ul.dropdown-menu li").apmap { | ||||
|             val servers = it.attr("data-link") | ||||
|             val doc = app.get(servers).document | ||||
|             doc.select("input").apmap { | ||||
|                 val postkey = it.attr("value") | ||||
|                 app.post("https://entrepeliculasyseries.nu/r.php", | ||||
|                     headers = mapOf("Host" to "entrepeliculasyseries.nu", | ||||
|                         "User-Agent" to USER_AGENT, | ||||
|                         "Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", | ||||
|                         "Accept-Language" to "en-US,en;q=0.5", | ||||
|                         "Content-Type" to "application/x-www-form-urlencoded", | ||||
|                         "Origin" to "https://entrepeliculasyseries.nu", | ||||
|                         "DNT" to "1", | ||||
|                         "Connection" to "keep-alive", | ||||
|                         "Referer" to servers, | ||||
|                         "Upgrade-Insecure-Requests" to "1", | ||||
|                         "Sec-Fetch-Dest" to "document", | ||||
|                         "Sec-Fetch-Mode" to "navigate", | ||||
|                         "Sec-Fetch-Site" to "same-origin", | ||||
|                         "Sec-Fetch-User" to "?1",), | ||||
|                     //params = mapOf(Pair("h", postkey)), | ||||
|                     data =  mapOf(Pair("h", postkey)), | ||||
|                     allowRedirects = false | ||||
|                 ).response.headers.values("location").apmap { | ||||
|                     loadExtractor(it, data, callback) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return true | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,223 @@ | |||
| package com.lagradost.cloudstream3.movieproviders | ||||
| 
 | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.mvvm.logError | ||||
| import com.lagradost.cloudstream3.utils.* | ||||
| import kotlin.collections.ArrayList | ||||
| 
 | ||||
| class PelisflixProvider:MainAPI() { | ||||
|     override val mainUrl = "https://pelisflix.li" | ||||
|     override val name = "Pelisflix" | ||||
|     override val lang = "es" | ||||
|     override val hasMainPage = true | ||||
|     override val hasChromecastSupport = true | ||||
|     override val hasDownloadSupport = true | ||||
|     override val supportedTypes = setOf( | ||||
|         TvType.Movie, | ||||
|         TvType.TvSeries, | ||||
|     ) | ||||
|     override suspend fun getMainPage(): HomePageResponse { | ||||
|         val items = ArrayList<HomePageList>() | ||||
|         val urls = listOf( | ||||
|             Pair("$mainUrl/ver-peliculas-online-gratis-fullhdc3/", "Películas"), | ||||
|             Pair("$mainUrl/ver-series-online-gratis/", "Series"), | ||||
|         ) | ||||
|         for (i in urls) { | ||||
|             try { | ||||
|                 val soup = app.get(i.first).document | ||||
|                 val home = soup.select("article.TPost.B").map { | ||||
|                     val title = it.selectFirst("h2.title").text() | ||||
|                     val link = it.selectFirst("a").attr("href") | ||||
|                     TvSeriesSearchResponse( | ||||
|                         title, | ||||
|                         link, | ||||
|                         this.name, | ||||
|                         TvType.Movie, | ||||
|                         it.selectFirst("figure img").attr("data-src"), | ||||
|                         null, | ||||
|                         null, | ||||
|                     ) | ||||
|                 } | ||||
| 
 | ||||
|                 items.add(HomePageList(i.second, home)) | ||||
|             } catch (e: Exception) { | ||||
|                logError(e) | ||||
|             } | ||||
|         } | ||||
|         if (items.size <= 0) throw ErrorLoadingException() | ||||
|         return HomePageResponse(items) | ||||
|     } | ||||
|     override suspend fun search(query: String): List<SearchResponse> { | ||||
|         val url = "$mainUrl/?s=$query" | ||||
|         val doc = app.get(url).document | ||||
|         return doc.select("article.TPost.B").map { | ||||
|             val href = it.selectFirst("a").attr("href") | ||||
|             val poster = it.selectFirst("figure img").attr("data-src") | ||||
|             val name = it.selectFirst("h2.title").text() | ||||
|             val isMovie = href.contains("/pelicula/") | ||||
|             if (isMovie) { | ||||
|                 MovieSearchResponse( | ||||
|                     name, | ||||
|                     href, | ||||
|                     this.name, | ||||
|                     TvType.Movie, | ||||
|                     poster, | ||||
|                     null | ||||
|                 ) | ||||
|             } else { | ||||
|                 TvSeriesSearchResponse( | ||||
|                     name, | ||||
|                     href, | ||||
|                     this.name, | ||||
|                     TvType.TvSeries, | ||||
|                     poster, | ||||
|                     null, | ||||
|                     null | ||||
|                 ) | ||||
|             } | ||||
|         }.toList() | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun load(url: String): LoadResponse? { | ||||
|         val type = if (url.contains("/pelicula/")) TvType.Movie else TvType.TvSeries | ||||
| 
 | ||||
|         val document = app.get(url).document | ||||
| 
 | ||||
|         val title = document.selectFirst("h1.Title").text() | ||||
|         val descRegex = Regex("(.Recuerda.*Pelisflix.+)") | ||||
|         val descRegex2 = Regex("(Actualmente.*.)") | ||||
|         val descRegex3 = Regex("(.*Director:.*)") | ||||
|         val descRegex4 = Regex("(.*Actores:.*)") | ||||
|         val descRegex5 = Regex("(Ver.*(\\)|)((\\d+).))") | ||||
|         val descipt = document.selectFirst("div.Description").text().replace(descRegex, "") | ||||
|             .replace(descRegex2, "").replace(descRegex3, "") | ||||
|             .replace(descRegex4, "").replace(descRegex5, "") | ||||
|         val desc2Regex = Regex("(G(e|é)nero:.*..)") | ||||
|         val descipt2 = document.selectFirst("div.Description").text().replace(desc2Regex,"") | ||||
|         val rating = | ||||
|             document.selectFirst("div.rating-content button.like-mov span.vot_cl")?.text()?.toFloatOrNull() | ||||
|                 ?.times(0)?.toInt() | ||||
|         val year = document.selectFirst("span.Date")?.text() | ||||
|         val duration = if (type == TvType.Movie) document.selectFirst(".Container .Container  span.Time").text() else null | ||||
|         val postercss = document.selectFirst("head").toString() | ||||
|         val posterRegex = Regex("(\"og:image\" content=\"https:\\/\\/seriesflix.video\\/wp-content\\/uploads\\/(\\d+)\\/(\\d+)\\/?.*.jpg)") | ||||
|         val poster = try { | ||||
|             posterRegex.findAll(postercss).map { | ||||
|                 it.value.replace("\"og:image\" content=\"","") | ||||
|             }.toList().first() | ||||
|         } catch (e: Exception) { | ||||
|             document.select(".TPostBg").attr("src") | ||||
|         } | ||||
|         if (type == TvType.TvSeries) { | ||||
|             val list = ArrayList<Pair<Int, String>>() | ||||
| 
 | ||||
|             document.select("main > section.SeasonBx > div > div.Title > a").forEach { element -> | ||||
|                 val season = element.selectFirst("> span")?.text()?.toIntOrNull() | ||||
|                 val href = element.attr("href") | ||||
|                 if (season != null && season > 0 && !href.isNullOrBlank()) { | ||||
|                     list.add(Pair(season, fixUrl(href))) | ||||
|                 } | ||||
|             } | ||||
|             if (list.isEmpty()) throw ErrorLoadingException("No Seasons Found") | ||||
| 
 | ||||
|             val episodeList = ArrayList<TvSeriesEpisode>() | ||||
| 
 | ||||
|             for (season in list)  { | ||||
|                 val seasonDocument = app.get(season.second).document | ||||
|                 val episodes = seasonDocument.select("table > tbody > tr") | ||||
|                 if (episodes.isNotEmpty()) { | ||||
|                     episodes.forEach { episode -> | ||||
|                         val epNum = episode.selectFirst("> td > span.Num")?.text()?.toIntOrNull() | ||||
|                         val epthumb = episode.selectFirst("img")?.attr("src") | ||||
|                         val aName = episode.selectFirst("> td.MvTbTtl > a") | ||||
|                         val name = aName.text() | ||||
|                         val href = aName.attr("href") | ||||
|                         val date = episode.selectFirst("> td.MvTbTtl > span")?.text() | ||||
|                         episodeList.add( | ||||
|                             TvSeriesEpisode( | ||||
|                                 name, | ||||
|                                 season.first, | ||||
|                                 epNum, | ||||
|                                 href, | ||||
|                                 fixUrlNull(epthumb), | ||||
|                                 date | ||||
|                             ) | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             return TvSeriesLoadResponse( | ||||
|                 title, | ||||
|                 url, | ||||
|                 this.name, | ||||
|                 type, | ||||
|                 episodeList, | ||||
|                 fixUrlNull(poster), | ||||
|                 year?.toIntOrNull(), | ||||
|                 descipt2, | ||||
|                 null, | ||||
|                 null, | ||||
|                 rating | ||||
|             ) | ||||
|         } else { | ||||
| 
 | ||||
|             return newMovieLoadResponse( | ||||
|                 title, | ||||
|                 url, | ||||
|                 type, | ||||
|                 url | ||||
|             ) { | ||||
|                 posterUrl = fixUrlNull(poster) | ||||
|                 this.year = year?.toIntOrNull() | ||||
|                 this.plot = descipt | ||||
|                 this.rating = rating | ||||
|                 setDuration(duration) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun loadLinks( | ||||
|         data: String, | ||||
|         isCasting: Boolean, | ||||
|         subtitleCallback: (SubtitleFile) -> Unit, | ||||
|         callback: (ExtractorLink) -> Unit | ||||
|     ): Boolean { | ||||
|         app.get(data).document.select("li button.Button.sgty").forEach { | ||||
|             val movieID = it.attr("data-id") | ||||
|             val serverID = it.attr("data-key") | ||||
|             val type = if (data.contains("pelicula")) 1 else 2 | ||||
|             val url = "$mainUrl/?trembed=$serverID&trid=$movieID&trtype=$type" //This is to get the POST key value | ||||
|             val doc1 = app.get(url).document | ||||
|             doc1.select("div.Video iframe").apmap { | ||||
|                 val iframe = it.attr("src") | ||||
|                 val postkey = iframe.replace("/stream/index.php?h=","") // this obtains | ||||
|                 // djNIdHNCR2lKTGpnc3YwK3pyRCs3L2xkQmljSUZ4ai9ibTcza0JRODNMcmFIZ0hPejdlYW0yanJIL2prQ1JCZA POST KEY | ||||
|                 app.post("https://pelisflix.li/stream/r.php", | ||||
|                     headers = mapOf("Host" to "pelisflix.li", | ||||
|                         "User-Agent" to USER_AGENT, | ||||
|                         "Accept" to "ext/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", | ||||
|                         "Accept-Language" to "en-US,en;q=0.5", | ||||
|                         "Content-Type" to "application/x-www-form-urlencoded", | ||||
|                         "Origin" to "null", | ||||
|                         "DNT" to "1", | ||||
|                         "Connection" to "keep-alive", | ||||
|                         "Upgrade-Insecure-Requests" to "1", | ||||
|                         "Sec-Fetch-Dest" to "iframe", | ||||
|                         "Sec-Fetch-Mode" to "navigate", | ||||
|                         "Sec-Fetch-Site" to "same-origin", | ||||
|                         "Sec-Fetch-User" to "?1", | ||||
|                         "Pragma" to "no-cache", | ||||
|                         "Cache-Control" to "no-cache", | ||||
|                         "TE" to "trailers"), | ||||
|                     params = mapOf(Pair("h", postkey)), | ||||
|                     data =  mapOf(Pair("h", postkey)), | ||||
|                     allowRedirects = false | ||||
|                 ).response.headers.values("location").apmap { link -> | ||||
|                     val url1 = link.replace("#bu","") | ||||
|                     loadExtractor(url1, data, callback) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return true | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,221 @@ | |||
| package com.lagradost.cloudstream3.movieproviders | ||||
| 
 | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.mvvm.logError | ||||
| import com.lagradost.cloudstream3.utils.* | ||||
| import kotlin.collections.ArrayList | ||||
| 
 | ||||
| class SeriesflixProvider:MainAPI() { | ||||
|     override val mainUrl = "https://seriesflix.video" | ||||
|     override val name = "Seriesflix" | ||||
|     override val lang = "es" | ||||
|     override val hasMainPage = true | ||||
|     override val hasChromecastSupport = true | ||||
|     override val hasDownloadSupport = true | ||||
|     override val supportedTypes = setOf( | ||||
|         TvType.Movie, | ||||
|         TvType.TvSeries, | ||||
|     ) | ||||
|     override suspend fun getMainPage(): HomePageResponse { | ||||
|         val items = ArrayList<HomePageList>() | ||||
|         val urls = listOf( | ||||
|             Pair("$mainUrl/ver-series-online/", "Series"), | ||||
|             Pair("$mainUrl/genero/accion/", "Acción"), | ||||
|             Pair("$mainUrl/genero/ciencia-ficcion/", "Ciencia ficción"), | ||||
|         ) | ||||
|         for (i in urls) { | ||||
|             try { | ||||
|                 val soup = app.get(i.first).document | ||||
|                 val home = soup.select("article.TPost.B").map { | ||||
|                     val title = it.selectFirst("h2.title").text() | ||||
|                     val link = it.selectFirst("a").attr("href") | ||||
|                     TvSeriesSearchResponse( | ||||
|                         title, | ||||
|                         link, | ||||
|                         this.name, | ||||
|                         TvType.Movie, | ||||
|                         it.selectFirst("figure img").attr("src"), | ||||
|                         null, | ||||
|                         null, | ||||
|                     ) | ||||
|                 } | ||||
| 
 | ||||
|                 items.add(HomePageList(i.second, home)) | ||||
|             } catch (e: Exception) { | ||||
|                 logError(e) | ||||
|             } | ||||
|         } | ||||
|         if (items.size <= 0) throw ErrorLoadingException() | ||||
|         return HomePageResponse(items) | ||||
|     } | ||||
|     override suspend fun search(query: String): List<SearchResponse> { | ||||
|         val url = "$mainUrl/?s=$query" | ||||
|         val doc = app.get(url).document | ||||
|         return doc.select("article.TPost.B").map { | ||||
|             val href = it.selectFirst("a").attr("href") | ||||
|             val poster = it.selectFirst("figure img").attr("src") | ||||
|             val name = it.selectFirst("h2.title").text() | ||||
|             val isMovie = href.contains("/movies/") | ||||
|             if (isMovie) { | ||||
|                 MovieSearchResponse( | ||||
|                     name, | ||||
|                     href, | ||||
|                     this.name, | ||||
|                     TvType.Movie, | ||||
|                     poster, | ||||
|                     null | ||||
|                 ) | ||||
|             } else { | ||||
|                 TvSeriesSearchResponse( | ||||
|                     name, | ||||
|                     href, | ||||
|                     this.name, | ||||
|                     TvType.TvSeries, | ||||
|                     poster, | ||||
|                     null, | ||||
|                     null | ||||
|                 ) | ||||
|             } | ||||
|         }.toList() | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     override suspend fun load(url: String): LoadResponse? { | ||||
|         val type = if (url.contains("/movies/")) TvType.Movie else TvType.TvSeries | ||||
| 
 | ||||
|         val document = app.get(url).document | ||||
| 
 | ||||
|         val title = document.selectFirst("h1.Title").text() | ||||
|         val descRegex = Regex("(Recuerda.*Seriesflix.)") | ||||
|         val descipt = document.selectFirst("div.Description > p").text().replace(descRegex,"") | ||||
|         val rating = | ||||
|             document.selectFirst("div.Vote > div.post-ratings > span")?.text()?.toFloatOrNull() | ||||
|                 ?.times(1000)?.toInt() | ||||
|         val year = document.selectFirst("span.Date")?.text() | ||||
|         // ?: does not work | ||||
|         val duration = try { | ||||
|             document.selectFirst("span.Time").text() | ||||
|         } catch (e: Exception) { | ||||
|             null | ||||
|         } | ||||
|         val postercss = document.selectFirst("head").toString() | ||||
|         val posterRegex = Regex("(\"og:image\" content=\"https:\\/\\/seriesflix.video\\/wp-content\\/uploads\\/(\\d+)\\/(\\d+)\\/?.*.jpg)") | ||||
|         val poster = try { | ||||
|             posterRegex.findAll(postercss).map { | ||||
|                 it.value.replace("\"og:image\" content=\"","") | ||||
|             }.toList().first() | ||||
|         } catch (e: Exception) { | ||||
|             document.select(".TPostBg").attr("src") | ||||
|         } | ||||
| 
 | ||||
|         if (type == TvType.TvSeries) { | ||||
|             val list = ArrayList<Pair<Int, String>>() | ||||
| 
 | ||||
|             document.select("main > section.SeasonBx > div > div.Title > a").forEach { element -> | ||||
|                 val season = element.selectFirst("> span")?.text()?.toIntOrNull() | ||||
|                 val href = element.attr("href") | ||||
|                 if (season != null && season > 0 && !href.isNullOrBlank()) { | ||||
|                     list.add(Pair(season, fixUrl(href))) | ||||
|                 } | ||||
|             } | ||||
|             if (list.isEmpty()) throw ErrorLoadingException("No Seasons Found") | ||||
| 
 | ||||
|             val episodeList = ArrayList<TvSeriesEpisode>() | ||||
| 
 | ||||
|             for (season in list) { | ||||
|                 val seasonDocument = app.get(season.second).document | ||||
|                 val episodes = seasonDocument.select("table > tbody > tr") | ||||
|                 if (episodes.isNotEmpty()) { | ||||
|                     episodes.forEach { episode -> | ||||
|                         val epNum = episode.selectFirst("> td > span.Num")?.text()?.toIntOrNull() | ||||
|                         val epthumb = episode.selectFirst("img")?.attr("src") | ||||
|                         val aName = episode.selectFirst("> td.MvTbTtl > a") | ||||
|                         val name = aName.text() | ||||
|                         val href = aName.attr("href") | ||||
|                         val date = episode.selectFirst("> td.MvTbTtl > span")?.text() | ||||
|                         episodeList.add( | ||||
|                             TvSeriesEpisode( | ||||
|                                 name, | ||||
|                                 season.first, | ||||
|                                 epNum, | ||||
|                                 href, | ||||
|                                 fixUrlNull(epthumb), | ||||
|                                 date | ||||
|                             ) | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             return TvSeriesLoadResponse( | ||||
|                 title, | ||||
|                 url, | ||||
|                 this.name, | ||||
|                 type, | ||||
|                 episodeList, | ||||
|                 fixUrlNull(poster), | ||||
|                 year?.toIntOrNull(), | ||||
|                 descipt, | ||||
|                 null, | ||||
|                 null, | ||||
|                 rating | ||||
|             ) | ||||
|         } else { | ||||
|             return newMovieLoadResponse( | ||||
|                 title, | ||||
|                 url, | ||||
|                 type, | ||||
|                 url | ||||
|             ) { | ||||
|                 posterUrl = fixUrlNull(poster) | ||||
|                 this.year = year?.toIntOrNull() | ||||
|                 this.plot = descipt | ||||
|                 this.rating = rating | ||||
|                 setDuration(duration) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun loadLinks( | ||||
|         data: String, | ||||
|         isCasting: Boolean, | ||||
|         subtitleCallback: (SubtitleFile) -> Unit, | ||||
|         callback: (ExtractorLink) -> Unit | ||||
|     ): Boolean { | ||||
|         app.get(data).document.select("ul.ListOptions li").forEach { | ||||
|             val movieID = it.attr("data-id") | ||||
|             val serverID = it.attr("data-key") | ||||
|             val type = if (data.contains("movies")) 1 else 2 | ||||
|             val url = "$mainUrl/?trembed=$serverID&trid=$movieID&trtype=$type" //This is to get the POST key value | ||||
|             val doc1 = app.get(url).document | ||||
|             doc1.select("div.Video iframe").apmap { | ||||
|                 val iframe = it.attr("src") | ||||
|                 val postkey = iframe.replace("https://sc.seriesflix.video/index.php?h=","") // this obtains | ||||
|                 // djNIdHNCR2lKTGpnc3YwK3pyRCs3L2xkQmljSUZ4ai9ibTcza0JRODNMcmFIZ0hPejdlYW0yanJIL2prQ1JCZA POST KEY | ||||
|                 app.post("https://sc.seriesflix.video/r.php", | ||||
|                     headers = mapOf("Host" to "sc.seriesflix.video", | ||||
|                         "User-Agent" to USER_AGENT, | ||||
|                         "Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", | ||||
|                         "Accept-Language" to "en-US,en;q=0.5", | ||||
|                         "Content-Type" to "application/x-www-form-urlencoded", | ||||
|                         "Origin" to "null", | ||||
|                         "DNT" to "1", | ||||
|                         "Alt-Used" to "sc.seriesflix.video", | ||||
|                         "Connection" to "keep-alive", | ||||
|                         "Upgrade-Insecure-Requests" to "1", | ||||
|                         "Sec-Fetch-Dest" to "iframe", | ||||
|                         "Sec-Fetch-Mode" to "navigate", | ||||
|                         "Sec-Fetch-Site" to "same-origin", | ||||
|                         "Sec-Fetch-User" to "?1",), | ||||
|                     params = mapOf(Pair("h", postkey)), | ||||
|                     data =  mapOf(Pair("h", postkey)), | ||||
|                     allowRedirects = false | ||||
|                 ).response.headers.values("location").apmap {link -> | ||||
|                     val url1 = link.replace("#bu","") | ||||
|                     loadExtractor(url1, data, callback) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return true | ||||
|     } | ||||
| } | ||||
|  | @ -110,6 +110,10 @@ val extractorApis: Array<ExtractorApi> = arrayOf( | |||
|     VoeExtractor(), | ||||
|     UpstreamExtractor(), | ||||
| 
 | ||||
|     Tomatomatela(), | ||||
|     Cinestart(), | ||||
|     OkRu(), | ||||
| 
 | ||||
|     // dood extractors | ||||
|     DoodToExtractor(), | ||||
|     DoodSoExtractor(), | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue