diff --git a/README.md b/README.md index 071ad8b1..9a42efb8 100644 --- a/README.md +++ b/README.md @@ -87,11 +87,13 @@ It merely scrapes 3rd-party websites that are publicly accessable via any regula - [pinoy-hd.xyz](https://www.pinoy-hd.xyz) - [pinoymovies.es](https://pinoymovies.es) - [trailers.to](https://trailers.to) +- [2embed.ru](https://www.2embed.ru) - [dramasee.net](https://dramasee.net) - [watchasian.sh](https://watchasian.sh) - [kdramahood.com](https://kdramahood.com) - [akwam.io](https://akwam.io) - [mycima.tv](https://mycima.tv) +- [egy.best](https://egy.best) - [9anime.center](https://9anime.center) - [animeworld.tv](https://www.animeworld.tv) - [asiaflix.app](https://asiaflix.app) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index f8e2d03e..9a69b95a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -81,11 +81,13 @@ object APIHolder { PinoyHDXyzProvider(), PinoyMoviesEsProvider(), TrailersTwoProvider(), + TwoEmbedProvider(), DramaSeeProvider(), WatchAsianProvider(), KdramaHoodProvider(), AkwamProvider(), MyCimaProvider(), + EgyBestProvider(), AnimePaheProvider(), NineAnimeProvider(), AnimeWorldProvider(), diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt index 1e8d20d9..147d4c80 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt @@ -17,7 +17,7 @@ class StreamSB2 : StreamSB() { } class StreamSB3 : StreamSB() { - override val mainUrl = "https://sbplay.one" + override val mainUrl = "https://sbplay3.com" } class StreamSB4 : StreamSB() { diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Zplayer.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Zplayer.kt index 5a5201ae..561f8b9c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Zplayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Zplayer.kt @@ -14,6 +14,11 @@ class Upstream: ZplayerV2() { override val mainUrl: String = "https://upstream.to" } +class Streamhub2: ZplayerV2() { + override val name: String = "Streamhub" //Here 'cause works + override val mainUrl: String = "https://streamhub.to" +} + open class ZplayerV2 : ExtractorApi() { override val name = "Zplayer V2" override val mainUrl = "https://v2.zplayer.live" diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/AllMoviesForYouProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/AllMoviesForYouProvider.kt index 9deb4e05..d7ec624d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/AllMoviesForYouProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/AllMoviesForYouProvider.kt @@ -1,17 +1,11 @@ package com.lagradost.cloudstream3.movieproviders -import com.fasterxml.jackson.module.kotlin.readValue import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.LoadResponse.Companion.setDuration -import com.lagradost.cloudstream3.utils.AppUtils.toJson +import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.cloudstream3.utils.getPostForm import com.lagradost.cloudstream3.utils.loadExtractor -import okio.Buffer import org.jsoup.Jsoup -import org.jsoup.nodes.Document -import java.net.URLDecoder class AllMoviesForYouProvider : MainAPI() { companion object { @@ -27,11 +21,44 @@ class AllMoviesForYouProvider : MainAPI() { // Fetching movies will not work if this link is outdated. override val mainUrl = "https://allmoviesforyou.net" override val name = "AllMoviesForYou" + override val hasMainPage = true override val supportedTypes = setOf( TvType.Movie, TvType.TvSeries ) + override suspend fun getMainPage(): HomePageResponse { + val items = ArrayList() + val soup = app.get(mainUrl).document + val urls = listOf( + Pair("Movies", "section[data-id=movies] article.TPost.B"), + Pair("TV Series", "section[data-id=series] article.TPost.B"), + ) + for ((name, element) in urls) { + try { + val home = soup.select(element).map { + val title = it.selectFirst("h2.title").text() + val link = it.selectFirst("a").attr("href") + TvSeriesSearchResponse( + title, + link, + this.name, + TvType.Movie, + fixUrl(it.selectFirst("figure img").attr("data-src")), + null, + null, + ) + } + + items.add(HomePageList(name, home)) + } catch (e: Exception) { + logError(e) + } + } + if (items.size <= 0) throw ErrorLoadingException() + return HomePageResponse(items) + } + override suspend fun search(query: String): List { val url = "$mainUrl/?s=$query" val document = app.get(url).document @@ -58,23 +85,23 @@ class AllMoviesForYouProvider : MainAPI() { } } - private fun getLink(document: Document): List? { - val list = ArrayList() - Regex("iframe src=\"(.*?)\"").find(document.html())?.groupValues?.get(1)?.let { - list.add(it) - } - document.select("div.OptionBx")?.forEach { element -> - val baseElement = element.selectFirst("> a.Button") - val elementText = element.selectFirst("> p.AAIco-dns")?.text() - if (elementText == "Streamhub" || elementText == "Dood") { - baseElement?.attr("href")?.let { href -> - list.add(href) - } - } - } - - return if (list.isEmpty()) null else list - } +// private fun getLink(document: Document): List? { +// val list = ArrayList() +// Regex("iframe src=\"(.*?)\"").find(document.html())?.groupValues?.get(1)?.let { +// list.add(it) +// } +// document.select("div.OptionBx")?.forEach { element -> +// val baseElement = element.selectFirst("> a.Button") +// val elementText = element.selectFirst("> p.AAIco-dns")?.text() +// if (elementText == "Streamhub" || elementText == "Dood") { +// baseElement?.attr("href")?.let { href -> +// list.add(href) +// } +// } +// } +// +// return if (list.isEmpty()) null else list +// } override suspend fun load(url: String): LoadResponse { val type = getType(url) @@ -144,14 +171,11 @@ class AllMoviesForYouProvider : MainAPI() { rating ) } else { - val data = getLink(document) - ?: throw ErrorLoadingException("No Links Found") - return newMovieLoadResponse( title, url, type, - data.filter { it != "about:blank" }.toJson() + url ) { posterUrl = backgroundPoster this.year = year?.toIntOrNull() @@ -168,74 +192,17 @@ class AllMoviesForYouProvider : MainAPI() { subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ): Boolean { - if (data.startsWith("$mainUrl/episode/")) { - val response = app.get(data).text - getLink(Jsoup.parse(response))?.let { links -> - for (link in links) { - if (link == data) continue - loadLinks(link, isCasting, subtitleCallback, callback) + val doc = app.get(data).document + val iframe = doc.select("body iframe").map { it.attr("src") } + iframe.apmap { id -> + if (id.contains("trembed")) { + val soup = app.get(id).document + soup.select("body iframe").map { + val link = it.attr("src").replace("streamhub.to/d/","streamhub.to/e/") + loadExtractor(link, data, callback) } - return true - } - return false - } else if (data.startsWith(mainUrl) && data != mainUrl) { - val realDataUrl = URLDecoder.decode(data, "UTF-8") - if (data.contains("trdownload")) { - val request = app.get(data) - val requestUrl = request.url - if (requestUrl.startsWith("https://streamhub.to/d/")) { - val buffer = Buffer() - val source = request.body?.source() - var html = "" - var tries = 0 // 20 tries = 163840 bytes = 0.16mb - - while (source?.exhausted() == false && tries < 20) { - // 8192 = max size - source.read(buffer, 8192) - tries += 1 - html += buffer.readUtf8() - } - getPostForm(request.url, html)?.let { form -> - val postDocument = Jsoup.parse(form) - - postDocument.selectFirst("a.downloadbtn")?.attr("href")?.let { url -> - callback( - ExtractorLink( - this.name, - this.name, - url, - mainUrl, - Qualities.Unknown.value - ) - ) - } - } - } else if (requestUrl.startsWith("https://dood")) { - loadExtractor(requestUrl, null, callback) - } else { - callback( - ExtractorLink( - this.name, - this.name, - realDataUrl, - mainUrl, - Qualities.Unknown.value - ) - ) - } - return true - } - val response = app.get(realDataUrl).text - Regex(" - loadExtractor(url.trimStart(), realDataUrl, callback) - } - return true - } else { - val links = mapper.readValue>(data) - for (link in links) { - loadLinks(link, isCasting, subtitleCallback, callback) - } - return true + } else loadExtractor(id, data, callback) } + return true } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/EgyBestProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/EgyBestProvider.kt new file mode 100644 index 00000000..3841f5e8 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/EgyBestProvider.kt @@ -0,0 +1,153 @@ +package com.lagradost.cloudstream3.movieproviders + +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.AppUtils.parseJson +import com.fasterxml.jackson.annotation.JsonProperty +import org.jsoup.nodes.Element + +class EgyBestProvider : MainAPI() { + override val lang = "ar" + override val mainUrl = "https://egy.best" + override val name = "EgyBest" + override val usesWebView = false + override val hasMainPage = true + override val supportedTypes = setOf(TvType.TvSeries, TvType.Movie) + + private fun String.getIntFromText(): Int? { + return Regex("""\d+""").find(this)?.groupValues?.firstOrNull()?.toIntOrNull() + } + + private fun Element.toSearchResponse(): SearchResponse? { + val url = this.attr("href") ?: return null + val posterUrl = select("img")?.attr("src") + val title = select("span.title").text() + .replace("\\(.*\\)".toRegex(), "") + val year = select("span.title").text() + .replace(".*\\(|\\)".toRegex(), "") + val tvType = if (url.contains("/movie/")) TvType.Movie else TvType.TvSeries + // If you need to differentiate use the url. + return MovieSearchResponse( + title, + url, + this@EgyBestProvider.name, + tvType, + posterUrl, + year.toIntOrNull(), + null, + ) + } + + override suspend fun getMainPage(): HomePageResponse { + // url, title + val pagesUrl = listOf( + Pair("$mainUrl/movies/?page="+(0..25).random(), "Movies"), + Pair("$mainUrl/tv/?page="+(0..25).random(), "Series"), + ) + val pages = pagesUrl.apmap { (url, name) -> + val doc = app.get(url).document + val list = doc.select("div.movies a").not("a.auto.load.btn.b").mapNotNull { element -> + element.toSearchResponse() + } + HomePageList(name, list) + }.sortedBy { it.name } + return HomePageResponse(pages) + } + + override suspend fun search(query: String): List { + val q = query.replace(" ","%20") + val result = arrayListOf() + listOf("$mainUrl/explore/?q=$q").apmap { url -> + val d = app.get(url).document + d.select("div.movies a").not("a.auto.load.btn.b").mapNotNull { + it.toSearchResponse()?.let { it1 -> result.add(it1) } + } + } + return result.distinct().sortedBy { it.name } + } + + override suspend fun load(url: String): LoadResponse { + val doc = app.get(url).document + val isMovie = url.contains("/movie/") + val posterUrl = doc.select("div.movie_img a img")?.attr("src") + val year = doc.select("div.movie_title h1 a")?.text()?.toIntOrNull() + val title = doc.select("div.movie_title h1 span[itemprop=\"name\"]").text() + + val synopsis = doc.select("div.mbox").firstOrNull { + it.text().contains("القصة") + }?.text()?.replace("القصة ", "") + + val tags = doc.select("table.movieTable tbody tr").firstOrNull { + it.text().contains("النوع") + }?.select("a")?.map { it.text() } + + return if (isMovie) { + newMovieLoadResponse( + title, + url, + TvType.Movie, + url + ) { + this.posterUrl = posterUrl + this.year = year + this.plot = synopsis + this.tags = tags + } + } else { + val episodes = ArrayList() + doc.select("#mainLoad > div:nth-child(2) > div.h_scroll > div a").map { + it.attr("href") + }.apmap { + val d = app.get(it).document + val season = Regex("season-(.....)").find(it)?.groupValues?.getOrNull(1)?.getIntFromText() + d.select("#mainLoad > div:nth-child(3) > div.movies_small a").map { eit -> + val ep = Regex("ep-(.....)").find(eit.attr("href"))?.groupValues?.getOrNull(1)?.getIntFromText() + episodes.add( + TvSeriesEpisode( + eit.select("span.title").text(), + season, + ep, + eit.attr("href"), + null, + null + ) + ) + } + } + newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes.distinct().sortedBy { it.episode }) { + this.posterUrl = posterUrl + this.tags = tags + this.year = year + this.plot = synopsis + } + } + } + data class Sources ( + @JsonProperty("quality") val quality: Int?, + @JsonProperty("link") val link: String + ) + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + val requestJSON = app.get("https://zawmedia-api.herokuapp.com/egybest?url=$data").text + val jsonArray = parseJson>(requestJSON) + for (i in jsonArray) { + val quality = i.quality + val link = i.link + callback.invoke( + ExtractorLink( + this.name, + this.name + " ${quality}p", + link, + this.mainUrl, + 2, + true + ) + ) + } + return true + } +} diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/TwoEmbedProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/TwoEmbedProvider.kt new file mode 100644 index 00000000..40c51aa8 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/TwoEmbedProvider.kt @@ -0,0 +1,69 @@ +package com.lagradost.cloudstream3.movieproviders + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.APIHolder.getCaptchaToken +import com.lagradost.cloudstream3.metaproviders.TmdbLink +import com.lagradost.cloudstream3.metaproviders.TmdbProvider +import com.lagradost.cloudstream3.movieproviders.SflixProvider.Companion.extractRabbitStream +import com.lagradost.cloudstream3.utils.AppUtils.parseJson +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.loadExtractor + +class TwoEmbedProvider : TmdbProvider() { + override val apiName = "2Embed" + override val name = "2Embed" + override val mainUrl = "https://www.2embed.ru" + override val useMetaLoadResponse = true + override val instantLinkLoading = false + override val supportedTypes = setOf( + TvType.Movie, + TvType.TvSeries, + ) + + data class EmbedJson ( + @JsonProperty("type") val type: String?, + @JsonProperty("link") val link: String, + @JsonProperty("sources") val sources: List, + @JsonProperty("tracks") val tracks: List? + ) + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + val mappedData = parseJson(data) + val (id, site) = if (mappedData.imdbID != null) listOf( + mappedData.imdbID, + "imdb" + ) else listOf(mappedData.tmdbID.toString(), "tmdb") + val isMovie = mappedData.episode == null && mappedData.season == null + val embedUrl = if (isMovie) { + "$mainUrl/embed/$site/movie?id=$id" + + } else { + val suffix = "$id&s=${mappedData.season ?: 1}&e=${mappedData.episode ?: 1}" + "$mainUrl/embed/$site/tv?id=$suffix" + } + val document = app.get(embedUrl).document + val captchaKey = + document.select("script[src*=https://www.google.com/recaptcha/api.js?render=]") + .attr("src").substringAfter("render=") + + val servers = document.select(".dropdown-menu a[data-id]").map { it.attr("data-id") } + servers.apmap { serverID -> + val token = getCaptchaToken(embedUrl, captchaKey) + val ajax = app.get("$mainUrl/ajax/embed/play?id=$serverID&_token=$token", referer = embedUrl).text + val mappedservers = parseJson(ajax) + val iframeLink = mappedservers.link + if (iframeLink.contains("rabbitstream")) { + extractRabbitStream(iframeLink, subtitleCallback, callback) { it } + } else { + loadExtractor(iframeLink, embedUrl, callback) + } + } + return true + } +} \ 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 39e885d0..70e248ce 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -112,7 +112,8 @@ val extractorApis: Array = arrayOf( StreamSB7(), StreamSB8(), StreamSB9(), - Streamhub(), + // Streamhub(), cause Streamhub2() works + Streamhub2(), FEmbed(), FeHD(),