diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index 09a1a110..153ea028 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -56,7 +56,8 @@ object APIHolder { TrailersTwoProvider(), - ZoroProvider() + ZoroProvider(), + PinoyMoviePedia() ) val restrictedApis = arrayListOf( diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/FEmbed.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/FEmbed.kt new file mode 100644 index 00000000..9a12db6f --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/FEmbed.kt @@ -0,0 +1,13 @@ +package com.lagradost.cloudstream3.extractors + +import android.util.Log +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.module.kotlin.readValue +import com.lagradost.cloudstream3.network.Session +import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.mapper + +class FEmbed: XStreamCdn() { + override val name: String = "FEmbed" + override val mainUrl: String = "https://www.fembed.com" +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/XStreamCdn.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/XStreamCdn.kt index b7bcd3fb..e74aacd0 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/XStreamCdn.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/XStreamCdn.kt @@ -6,12 +6,13 @@ import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.mapper import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities +import com.lagradost.cloudstream3.utils.getQualityFromName -class XStreamCdn : ExtractorApi() { - override val name = "XStreamCdn" - override val mainUrl = "https://embedsito.com" +open class XStreamCdn : ExtractorApi() { + override val name: String = "XStreamCdn" + override val mainUrl: String = "https://embedsito.com" override val requiresReferer = false + var domainUrl: String = "embedsito.com" private data class ResponseData( @JsonProperty("file") val file: String, @@ -25,17 +26,7 @@ class XStreamCdn : ExtractorApi() { ) override fun getExtractorUrl(id: String): String { - return "$mainUrl/api/source/$id" - } - - private fun getQuality(string: String): Int { - return when (string) { - "360p" -> Qualities.P480.value - "480p" -> Qualities.P480.value - "720p" -> Qualities.P720.value - "1080p" -> Qualities.P1080.value - else -> Qualities.Unknown.value - } + return "$domainUrl/api/source/$id" } override fun getUrl(url: String, referer: String?): List { @@ -43,10 +34,13 @@ class XStreamCdn : ExtractorApi() { "Referer" to url, "User-Agent" to "Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0", ) - val newUrl = url.replace("$mainUrl/v/", "$mainUrl/api/source/") + val id = url.trimEnd('/').split("/").last() + val newUrl = "https://${domainUrl}/api/source/${id}" val extractedLinksList: MutableList = mutableListOf() with(app.post(newUrl, headers = headers)) { + if (this.code != 200) return listOf() val text = this.text + if (text.isEmpty()) return listOf() if (text == """{"success":false,"data":"Video not found or has been removed"}""") return listOf() mapper.readValue(text)?.let { if (it.success && it.data != null) { @@ -57,7 +51,7 @@ class XStreamCdn : ExtractorApi() { "$name ${data.label}", data.file, url, - getQuality(data.label), + getQualityFromName(data.label), ) ) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PinoyMoviePedia.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PinoyMoviePedia.kt new file mode 100644 index 00000000..114e95ae --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PinoyMoviePedia.kt @@ -0,0 +1,337 @@ +package com.lagradost.cloudstream3.movieproviders + +import android.util.Log +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.module.kotlin.readValue +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.extractors.DoodLaExtractor +import com.lagradost.cloudstream3.extractors.FEmbed +import com.lagradost.cloudstream3.extractors.MixDrop +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.mapper +import org.jsoup.Jsoup + +class PinoyMoviePedia : MainAPI() { + override val name: String + get() = "Pinoy Moviepedia" + + override val mainUrl: String + get() = "https://pinoymoviepedia.ru" + + override val supportedTypes: Set + get() = setOf(TvType.Movie, TvType.TvSeries) + + override val hasDownloadSupport: Boolean + get() = false + + override val hasMainPage: Boolean + get() = true + + override val hasQuickSearch: Boolean + get() = false + + private data class JsonVoeLinks( + @JsonProperty("hls") val url: String?, + @JsonProperty("video_height") val label: Int? + ) + + override fun getMainPage(): HomePageResponse { + val all = ArrayList() + try { + val html = app.get(mainUrl, timeout = 15).text + val document = Jsoup.parse(html) + val mainbody = document.getElementsByTag("body") + // All rows will be hardcoded bc of the nature of the site + val rows: List> = listOf( + Pair("Latest Movies", "featured-titles"), + Pair("Movies", "dt-movies"), + Pair("Digitally Restored", "genre_digitally-restored"), + Pair("Action", "genre_action"), + Pair("Romance", "genre_romance"), + Pair("Comedy", "genre_comedy"), + Pair("Family", "genre_family") + //Pair("Adult +18", "genre_pinay-sexy-movies") + ) + for (item in rows) { + val title = item.first + val inner = mainbody?.select("div#${item.second} > article") + if (inner != null) { + val elements: List = inner.map { + // Get inner div from article + val urlTitle = it?.select("div.data") + // Fetch details + val link = urlTitle?.select("a")?.attr("href") ?: "" + val name = urlTitle?.text() ?: "" + val image = it?.select("div.poster > img")?.attr("src") + // Get Year from Title + val rex = Regex("\\((\\d+)") + val yearRes = rex.find(name)?.value ?: "" + val year = yearRes.replace("(", "").toIntOrNull() + + val tvType = TvType.Movie + MovieSearchResponse( + name, + link, + this.name, + tvType, + image, + year, + null, + ) + } + // Add + all.add( + HomePageList( + title, elements + ) + ) + } + } + } catch (e: Exception) { + e.printStackTrace() + Log.i(this.name, "Result => (Exception) ${e}") + } + return HomePageResponse(all) + } + + override fun search(query: String): List { + val url = "$mainUrl/?s=${query}" + val html = app.get(url).text + val document = Jsoup.parse(html).select("div.search-page")?.firstOrNull() + ?.select("div.result-item") + if (document != null) { + return document.map { + val inner = it.select("article") + val details = inner.select("div.details") + val href = details?.select("div.title > a")?.attr("href") ?: "" + + val title = details?.select("div.title")?.text() ?: "" + val link: String = when (href != "") { + true -> fixUrl(href) + false -> "" + } + val year = details?.select("div.meta > span.year")?.text()?.toIntOrNull() + val image = inner.select("div.image > div > a > img")?.attr("src") + + MovieSearchResponse( + title, + link, + this.name, + TvType.Movie, + image, + year + ) + } + } + return listOf() + } + + override fun load(url: String): LoadResponse { + val response = app.get(url).text + val doc = Jsoup.parse(response) + val body = doc.getElementsByTag("body") + val inner = body?.select("div.sheader") + // Identify if movie or series + val isTvSeries = doc?.select("title")?.text()?.lowercase()?.contains("full episode -") ?: false + + // Video details + val poster = doc.select("meta[property=og:image]").firstOrNull()?.attr("content") + val title = inner?.select("div.data > h1")?.firstOrNull()?.text() ?: "" + val descript = body?.select("div#info")?.text() + val rex = Regex("\\((\\d+)") + val yearRes = rex.find(title)?.value ?: "" + //Log.i(this.name, "Result => (yearRes) ${yearRes}") + val year = yearRes.replace("(", "").toIntOrNull() + + // Video links + val linksContainer = body?.select("div#playcontainer") + val streamlinks = linksContainer?.toString() ?: "" + //Log.i(this.name, "Result => (streamlinks) ${streamlinks}") + + // Parse episodes if series + if (isTvSeries) { + val episodeList = ArrayList() + val epList = body?.select("div#playeroptions > ul > li") + //Log.i(this.name, "Result => (epList) ${epList}") + val epLinks = linksContainer?.select("div > div > div.source-box") + //Log.i(this.name, "Result => (epLinks) ${epLinks}") + if (epList != null) { + for (ep in epList) { + val epTitle = ep.select("span.title")?.text() ?: "" + if (epTitle.isNotEmpty()) { + val epNum = epTitle.lowercase().replace("episode", "").trim().toIntOrNull() + //Log.i(this.name, "Result => (epNum) ${epNum}") + val href = when (epNum != null && epLinks != null) { + true -> epLinks.select("div#source-player-${epNum}") + ?.select("iframe")?.attr("src") ?: "" + false -> "" + } + //Log.i(this.name, "Result => (epLinks href) ${href}") + episodeList.add( + TvSeriesEpisode( + name, + null, + epNum, + href, + poster, + null + ) + ) + } + } + return TvSeriesLoadResponse( + title, + url, + this.name, + TvType.TvSeries, + episodeList, + poster, + year, + descript, + null, + null, + null + ) + } + } + return MovieLoadResponse(title, url, this.name, TvType.Movie, streamlinks, poster, year, descript, null, null) + } + + override fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + if (data == "about:blank") return false + if (data == "") return false + val sources = mutableListOf() + try { + if (data.contains("playcontainer")) { + // parse movie servers + //Log.i(this.name, "Result => (data) ${data}") + val urls = Jsoup.parse(data).select("div")?.map { item -> + item.select("iframe")?.attr("src") + } + if (!urls.isNullOrEmpty()) { + for (url in urls) { + if (!url.isNullOrEmpty()) { + //Log.i(this.name, "Result => (url) ${url}") + if (url.contains("dood.watch")) { + // WIP: Not working for current domain. Still, adding it. + val extractor = DoodLaExtractor() + val src = extractor.getUrl(url) + if (src != null) { + sources.addAll(src) + } + } + if (url.contains("voe.sx/")) { + val doc = Jsoup.parse(app.get(url).text)?.toString() ?: "" + if (doc.isNotEmpty()) { + val start = "const sources =" + var src = doc.substring(doc.indexOf(start)) + src = src.substring(start.length, src.indexOf(";")) + .replace("0,", "0") + .trim() + //Log.i(this.name, "Result => (src) ${src}") + mapper.readValue(src)?.let { voelink -> + //Log.i(this.name, "Result => (voelink) ${voelink}") + val linkUrl = voelink.url + val linkLabel = voelink.label?.toString() ?: "" + if (!linkUrl.isNullOrEmpty()) { + sources.add( + ExtractorLink( + name = "Voe m3u8 ${linkLabel}", + source = "Voe", + url = linkUrl, + quality = getQualityFromName(linkLabel), + referer = url, + isM3u8 = true + ) + ) + } + } + } + } + if (url.startsWith("https://upstream.to")) { + // WIP: m3u8 link fetched but not playing + //Log.i(this.name, "Result => (no extractor) ${url}") + val doc = Jsoup.parse(app.get(url, referer = "https://upstream.to").text)?.toString() ?: "" + if (doc.isNotEmpty()) { + var reg = Regex("(?<=master)(.*)(?=hls)") + val result = reg.find(doc)?.groupValues?.map { + it.trim('|') + }?.toList() + reg = Regex("(?<=\\|file\\|)(.*)(?=\\|remove\\|)") + val domainList = reg.find(doc)?.groupValues?.get(1)?.split("|") + var domain = when (!domainList.isNullOrEmpty()) { + true -> { + if (domainList.isNotEmpty()) { + var domName = "" + for (part in domainList) { + domName = "${part}.${domName}" + } + domName.trimEnd('.') + } else { "" } + } + false -> "" + } + //Log.i(this.name, "Result => (domain) ${domain}") + if (domain.isEmpty()) { + domain = "s96.upstreamcdn.co" + //Log.i(this.name, "Result => (default domain) ${domain}") + } + result?.forEach { + val linkUrl = "https://${domain}/hls/${it}/master.m3u8" + sources.add( + ExtractorLink( + name = "Upstream m3u8", + source = "Voe", + url = linkUrl, + quality = Qualities.Unknown.value, + referer = "https://upstream.to", + isM3u8 = true + ) + ) + } + } + } + if (url.startsWith("https://mixdrop.co/")) { + val extractor = MixDrop() + val src = extractor.getUrl(url) + if (src != null) { + sources.addAll(src) + } + } + // end if + } + } + } + } else { + // parse single link + if (data.contains("fembed.com")) { + val extractor = FEmbed() + extractor.domainUrl = "diasfem.com" + val src = extractor.getUrl(data) + if (src.isNotEmpty()) { + sources.addAll(src) + } + } + } + // Invoke sources + if (sources.isNotEmpty()) { + for (source in sources) { + callback.invoke(source) + //Log.i(this.name, "Result => (source) ${source.url}") + } + return true + } + } catch (e: Exception) { + e.printStackTrace() + Log.i(this.name, "Result => (e) ${e}") + } + return false + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt index 5da5a395..98642c50 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -77,6 +77,7 @@ val extractorApis: Array = arrayOf( StreamSB(), Streamhub(), SBPlay(), + FEmbed(), // dood extractors DoodToExtractor(),