mirror of
				https://github.com/recloudstream/cloudstream.git
				synced 2024-08-15 01:53:11 +00:00 
			
		
		
		
	
							parent
							
								
									38d6561799
								
							
						
					
					
						commit
						2edc4571a1
					
				
					 1 changed files with 81 additions and 86 deletions
				
			
		| 
						 | 
				
			
			@ -1,11 +1,8 @@
 | 
			
		|||
package com.lagradost.cloudstream3.movieproviders
 | 
			
		||||
 | 
			
		||||
import com.lagradost.cloudstream3.*
 | 
			
		||||
import com.lagradost.cloudstream3.extractors.Pelisplus
 | 
			
		||||
import com.lagradost.cloudstream3.utils.ExtractorLink
 | 
			
		||||
import com.lagradost.cloudstream3.utils.getQualityFromName
 | 
			
		||||
import com.lagradost.cloudstream3.utils.*
 | 
			
		||||
import org.jsoup.Jsoup
 | 
			
		||||
import java.net.URI
 | 
			
		||||
 | 
			
		||||
/** Needs to inherit from MainAPI() to
 | 
			
		||||
 * make the app know what functions to call
 | 
			
		||||
| 
						 | 
				
			
			@ -14,7 +11,6 @@ import java.net.URI
 | 
			
		|||
open class PelisplusProviderTemplate : MainAPI() {
 | 
			
		||||
    override val lang = "es"
 | 
			
		||||
    open val homePageUrlList = listOf<String>()
 | 
			
		||||
    open val pelisplusExtractorUrl: String? = null
 | 
			
		||||
 | 
			
		||||
//    // mainUrl is good to have as a holder for the url to make future changes easier.
 | 
			
		||||
//    override val mainUrl: String
 | 
			
		||||
| 
						 | 
				
			
			@ -35,6 +31,7 @@ open class PelisplusProviderTemplate : MainAPI() {
 | 
			
		|||
 | 
			
		||||
    // Searching returns a SearchResponse, which can be one of the following: AnimeSearchResponse, MovieSearchResponse, TorrentSearchResponse, TvSeriesSearchResponse
 | 
			
		||||
    // Each of the classes requires some different data, but always has some critical things like name, poster and url.
 | 
			
		||||
 | 
			
		||||
    override suspend fun search(query: String): ArrayList<SearchResponse> {
 | 
			
		||||
        // Simply looking at devtools network is enough to spot a request like:
 | 
			
		||||
        // https://vidembed.cc/search.html?keyword=neverland where neverland is the query, can be written as below.
 | 
			
		||||
| 
						 | 
				
			
			@ -48,7 +45,7 @@ open class PelisplusProviderTemplate : MainAPI() {
 | 
			
		|||
            val poster = fixUrl(li.selectFirst("img").attr("src"))
 | 
			
		||||
 | 
			
		||||
            // .text() selects all the text in the element, be careful about doing this while too high up in the html hierarchy
 | 
			
		||||
            val title = li.selectFirst(".name").text()
 | 
			
		||||
            val title = cleanName(li.selectFirst(".name").text())
 | 
			
		||||
            // Use get(0) and toIntOrNull() to prevent any possible crashes, [0] or toInt() will error the search on unexpected values.
 | 
			
		||||
            val year = li.selectFirst(".date")?.text()?.split("-")?.get(0)?.toIntOrNull()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -73,33 +70,29 @@ open class PelisplusProviderTemplate : MainAPI() {
 | 
			
		|||
        val html = app.get(url).text
 | 
			
		||||
        val soup = Jsoup.parse(html)
 | 
			
		||||
 | 
			
		||||
        var title = soup.selectFirst("h1,h2,h3").text()
 | 
			
		||||
        title = if (!title.contains("Episode")) title else title.split("Episode")[0].trim()
 | 
			
		||||
 | 
			
		||||
        val title = cleanName(soup.selectFirst("h1,h2,h3").text())
 | 
			
		||||
        val description = soup.selectFirst(".post-entry")?.text()?.trim()
 | 
			
		||||
        var poster: String? = null
 | 
			
		||||
        val poster = soup.selectFirst("head meta[property=og:image]").attr("content")
 | 
			
		||||
 | 
			
		||||
        val episodes = soup.select(".listing.items.lists > .video-block").map { li ->
 | 
			
		||||
            val epTitle = if (li.selectFirst(".name") != null)
 | 
			
		||||
                if (li.selectFirst(".name").text().contains("Episode"))
 | 
			
		||||
                    "Episode " + li.selectFirst(".name").text().split("Episode")[1].trim()
 | 
			
		||||
                else
 | 
			
		||||
                    li.selectFirst(".name").text()
 | 
			
		||||
            else ""
 | 
			
		||||
            val href = fixUrl(li.selectFirst("a").attr("href"))
 | 
			
		||||
            val regexseason = Regex("(-[Tt]emporada-(\\d+)-[Cc]apitulo-(\\d+))")
 | 
			
		||||
            val aaa = regexseason.find(href)?.destructured?.component1()?.replace(Regex("(-[Tt]emporada-|[Cc]apitulo-)"),"")
 | 
			
		||||
            val seasonid = aaa.let { str ->
 | 
			
		||||
                str?.split("-")?.mapNotNull { subStr -> subStr.toIntOrNull() }
 | 
			
		||||
            }
 | 
			
		||||
            val isValid = seasonid?.size == 2
 | 
			
		||||
            val episode = if (isValid) seasonid?.getOrNull(1) else null
 | 
			
		||||
            val season = if (isValid) seasonid?.getOrNull(0) else null
 | 
			
		||||
            val epThumb = fixUrl(li.selectFirst("img").attr("src"))
 | 
			
		||||
            val epDate = li.selectFirst(".meta > .date").text()
 | 
			
		||||
 | 
			
		||||
            if (poster == null) {
 | 
			
		||||
                poster = li.selectFirst("img")?.attr("onerror")?.replace("//img", "https://img")?.split("=")?.get(1)
 | 
			
		||||
                    ?.replace(Regex("[';]"), "")
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            val epNum = Regex("""Episode (\d+)""").find(epTitle)?.destructured?.component1()?.toIntOrNull()
 | 
			
		||||
 | 
			
		||||
            TvSeriesEpisode(
 | 
			
		||||
                epTitle,
 | 
			
		||||
                null,
 | 
			
		||||
                epNum,
 | 
			
		||||
                season,
 | 
			
		||||
                episode,
 | 
			
		||||
                fixUrl(li.selectFirst("a").attr("href")),
 | 
			
		||||
                epThumb,
 | 
			
		||||
                epDate
 | 
			
		||||
| 
						 | 
				
			
			@ -119,10 +112,10 @@ open class PelisplusProviderTemplate : MainAPI() {
 | 
			
		|||
                    this.name,
 | 
			
		||||
                    tvType,
 | 
			
		||||
                    episodes,
 | 
			
		||||
                    poster,
 | 
			
		||||
                    fixUrl(poster),
 | 
			
		||||
                    year,
 | 
			
		||||
                    description,
 | 
			
		||||
                    ShowStatus.Ongoing,
 | 
			
		||||
                    null,
 | 
			
		||||
                    null,
 | 
			
		||||
                    null
 | 
			
		||||
                )
 | 
			
		||||
| 
						 | 
				
			
			@ -134,7 +127,7 @@ open class PelisplusProviderTemplate : MainAPI() {
 | 
			
		|||
                    this.name,
 | 
			
		||||
                    tvType,
 | 
			
		||||
                    episodes[0].data,
 | 
			
		||||
                    poster,
 | 
			
		||||
                    fixUrl(poster),
 | 
			
		||||
                    year,
 | 
			
		||||
                    description,
 | 
			
		||||
                    null,
 | 
			
		||||
| 
						 | 
				
			
			@ -156,12 +149,12 @@ open class PelisplusProviderTemplate : MainAPI() {
 | 
			
		|||
            val document = Jsoup.parse(response)
 | 
			
		||||
            document.select("div.main-inner")?.forEach { inner ->
 | 
			
		||||
                // Always trim your text unless you want the risk of spaces at the start or end.
 | 
			
		||||
                val title = inner.select(".widget-title").text().trim()
 | 
			
		||||
                val title = cleanName(inner.select(".widget-title").text())
 | 
			
		||||
                val elements = inner.select(".video-block").map {
 | 
			
		||||
                    val link = fixUrl(it.select("a").attr("href"))
 | 
			
		||||
                    val image = it.select(".picture > img").attr("src").replace("//img", "https://img")
 | 
			
		||||
                    val name = it.select("div.name").text().trim().replace(Regex("""[Ee]pisode \d+"""), "")
 | 
			
		||||
                    val isSeries = (name.contains("Season") || name.contains("Episode"))
 | 
			
		||||
                    val name = cleanName(it.select("div.name").text())
 | 
			
		||||
                    val isSeries = (name.contains("Temporada") || name.contains("Capítulo"))
 | 
			
		||||
 | 
			
		||||
                    if (isSeries) {
 | 
			
		||||
                        TvSeriesSearchResponse(
 | 
			
		||||
| 
						 | 
				
			
			@ -198,6 +191,38 @@ open class PelisplusProviderTemplate : MainAPI() {
 | 
			
		|||
        return HomePageResponse(homePageList)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private fun cleanName(input: String): String = input.replace(Regex("([Tt]emporada (\\d+)|[Cc]apítulo (\\d+))|[Tt]emporada|[Cc]apítulo"),"").trim()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private suspend fun getPelisStream(
 | 
			
		||||
        link: String,
 | 
			
		||||
        callback: (ExtractorLink) -> Unit) : Boolean {
 | 
			
		||||
        val soup = app.get(link).text
 | 
			
		||||
        val m3u8regex = Regex("((https:|http:)\\/\\/.*m3u8.*expiry=(\\d+))")
 | 
			
		||||
        val m3u8 = m3u8regex.find(soup)?.value ?: return false
 | 
			
		||||
        M3u8Helper().m3u8Generation(
 | 
			
		||||
            M3u8Helper.M3u8Stream(
 | 
			
		||||
                m3u8,
 | 
			
		||||
                headers = mapOf("Referer" to mainUrl)
 | 
			
		||||
            ), true
 | 
			
		||||
        )
 | 
			
		||||
            .map { stream ->
 | 
			
		||||
                val qualityString = if ((stream.quality ?: 0) == 0) "" else "${stream.quality}p"
 | 
			
		||||
                callback(
 | 
			
		||||
                    ExtractorLink(
 | 
			
		||||
                        name,
 | 
			
		||||
                        "$name $qualityString",
 | 
			
		||||
                        stream.streamUrl,
 | 
			
		||||
                        mainUrl,
 | 
			
		||||
                        getQualityFromName(stream.quality.toString()),
 | 
			
		||||
                        true
 | 
			
		||||
                    )
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // loadLinks gets the raw .mp4 or .m3u8 urls from the data parameter in the episodes class generated in load()
 | 
			
		||||
    // See TvSeriesEpisode(...) in this provider.
 | 
			
		||||
    // The data are usually links, but can be any other string to help aid loading the links.
 | 
			
		||||
| 
						 | 
				
			
			@ -208,67 +233,37 @@ open class PelisplusProviderTemplate : MainAPI() {
 | 
			
		|||
        subtitleCallback: (SubtitleFile) -> Unit,
 | 
			
		||||
        callback: (ExtractorLink) -> Unit
 | 
			
		||||
    ): Boolean {
 | 
			
		||||
        // "?: return" is a very useful statement which returns if the iframe link isn't found.
 | 
			
		||||
        val iframeLink = Jsoup.parse(app.get(data).text).selectFirst(".tab-video")?.attr("data-video") ?: return false
 | 
			
		||||
 | 
			
		||||
        // In this case the video player is a vidstream clone and can be handled by the vidstream extractor.
 | 
			
		||||
        // This case is a both unorthodox and you normally do not call extractors as they detect the url returned and does the rest.
 | 
			
		||||
        val vidstreamObject = Pelisplus(pelisplusExtractorUrl ?: mainUrl)
 | 
			
		||||
        // https://vidembed.cc/streaming.php?id=MzUwNTY2&... -> MzUwNTY2
 | 
			
		||||
        val id = Regex("""id=([^?]*)""").find(iframeLink)?.groupValues?.get(1)
 | 
			
		||||
 | 
			
		||||
        if (id != null) {
 | 
			
		||||
            vidstreamObject.getUrl(id, isCasting, callback)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val html = app.get(fixUrl(iframeLink)).text
 | 
			
		||||
        val soup = Jsoup.parse(html)
 | 
			
		||||
 | 
			
		||||
        val servers = soup.select(".list-server-items > .linkserver").mapNotNull { li ->
 | 
			
		||||
            if (!li?.attr("data-video").isNullOrEmpty()) {
 | 
			
		||||
                Pair(li.text(), fixUrl(li.attr("data-video")))
 | 
			
		||||
            } else {
 | 
			
		||||
                null
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        servers.apmap {
 | 
			
		||||
            // When checking strings make sure to make them lowercase and trimmed because edgecases like "beta server " wouldn't work otherwise.
 | 
			
		||||
            if (it.first.trim().equals("beta server", ignoreCase = true)) {
 | 
			
		||||
                // Group 1: link, Group 2: Label
 | 
			
		||||
                // Regex can be used to effectively parse small amounts of json without bothering with writing a json class.
 | 
			
		||||
                val sourceRegex = Regex("""sources:[\W\w]*?file:\s*["'](.*?)["'][\W\w]*?label:\s*["'](.*?)["']""")
 | 
			
		||||
                val trackRegex = Regex("""tracks:[\W\w]*?file:\s*["'](.*?)["'][\W\w]*?label:\s*["'](.*?)["']""")
 | 
			
		||||
 | 
			
		||||
                // Having a referer is often required. It's a basic security check most providers have.
 | 
			
		||||
                // Try to replicate what your browser does.
 | 
			
		||||
                val serverHtml = app.get(it.second, headers = mapOf("referer" to iframeLink)).text
 | 
			
		||||
                sourceRegex.findAll(serverHtml).forEach { match ->
 | 
			
		||||
                    callback.invoke(
 | 
			
		||||
                        ExtractorLink(
 | 
			
		||||
                            this.name,
 | 
			
		||||
                            match.groupValues.getOrNull(2)?.let { "${this.name} $it" } ?: this.name,
 | 
			
		||||
                            match.groupValues[1],
 | 
			
		||||
                            it.second,
 | 
			
		||||
                            // Useful function to turn something like "1080p" to an app quality.
 | 
			
		||||
                            getQualityFromName(match.groupValues.getOrNull(2) ?: ""),
 | 
			
		||||
                            // Kinda risky
 | 
			
		||||
                            // isM3u8 makes the player pick the correct extractor for the source.
 | 
			
		||||
                            // If isM3u8 is wrong the player will error on that source.
 | 
			
		||||
                            URI(match.groupValues[1]).path.endsWith(".m3u8"),
 | 
			
		||||
                        )
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
                trackRegex.findAll(serverHtml).forEach { match ->
 | 
			
		||||
                    subtitleCallback.invoke(
 | 
			
		||||
                        SubtitleFile(
 | 
			
		||||
                            match.groupValues.getOrNull(2) ?: "Unknown",
 | 
			
		||||
                            match.groupValues[1]
 | 
			
		||||
                        )
 | 
			
		||||
                    )
 | 
			
		||||
        val doc = app.get(data).document
 | 
			
		||||
        val info = doc.select("div.tabs-video li").text()
 | 
			
		||||
        if (info.contains("Latino")) {
 | 
			
		||||
            doc.select(".server-item-1 li").apmap {
 | 
			
		||||
                val serverid = fixUrl(it.attr("data-video")).replace("streaming.php","play")
 | 
			
		||||
                loadExtractor(serverid, data, callback)
 | 
			
		||||
                if (serverid.contains("pelisplus.icu")) {
 | 
			
		||||
                    getPelisStream(serverid, callback)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (info.contains("Subtitulado")) {
 | 
			
		||||
            doc.select(".server-item-0 li").apmap {
 | 
			
		||||
                val serverid = fixUrl(it.attr("data-video")).replace("streaming.php","play")
 | 
			
		||||
                loadExtractor(serverid, data, callback)
 | 
			
		||||
                if (serverid.contains("pelisplus.icu")) {
 | 
			
		||||
                    getPelisStream(serverid, callback)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (info.contains("Castellano")) {
 | 
			
		||||
            doc.select(".server-item-2 li").apmap {
 | 
			
		||||
                val serverid = fixUrl(it.attr("data-video")).replace("streaming.php","play")
 | 
			
		||||
                loadExtractor(serverid, data, callback)
 | 
			
		||||
                if (serverid.contains("pelisplus.icu")) {
 | 
			
		||||
                    getPelisStream(serverid, callback)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue