()
+ val doc = app.get("$mainUrl/page/1/?s=$query").document
+ val paginationElement = doc.select("ul[aria-label=\"pagination\"]")
+ doc.select("section article a").map {
+ val postUrl = it.attr("href")
+ if(it.select("li[aria-label=\"episode\"]").isNotEmpty()) return@map
+ if(postUrl.contains("$mainUrl/expired-download/|$mainUrl/افلام-اون-لاين/".toRegex())) return@map
+ result.add(it.toSearchResponse()!!)
+ }
+ if(paginationElement.isNotEmpty()) {
+ val max = paginationElement.select("li").not("li.active").last()?.text()?.toIntOrNull()
+ if (max != null) {
+ if(max > 5) return result.distinct().sortedBy { it.name }
+ (2..max!!).toList().apmap {
+ app.get("$mainUrl/page/$it/?s=$query\"").document.select("section article a").map { element ->
+ val postUrl = element.attr("href")
+ if(element.select("li[aria-label=\"episode\"]").isNotEmpty()) return@map
+ if(postUrl.contains("$mainUrl/expired-download/|$mainUrl/افلام-اون-لاين/".toRegex())) return@map
+ result.add(element.toSearchResponse()!!)
+ }
+ }
+ }
+ }
+ return result.distinct().sortedBy { it.name }
+ }
+
+ override suspend fun load(url: String): LoadResponse {
+ val doc = app.get(url).document
+ val posterUrl = doc.select("body > script:nth-child(3)").html().replace(".*,\"image\":\"|\".*".toRegex(),"").ifEmpty { doc.select("meta[property=\"og:image\"]").attr("content") }
+ val year = doc.select("article ul:nth-child(1) li a").last()?.text()?.toIntOrNull()
+ val title = doc.select("title").text().split(" | ")[0]
+ val isMovie = title.contains("فيلم|حفلات|مسرحية".toRegex())
+ val youtubeTrailer = doc.select("iframe")?.attr("src")
+
+ val synopsis = doc.select("ul#details li:contains(لمحة) p").text()
+
+ val tags = doc.select("article ul").first()?.select("li")?.map { it.text() }
+
+ val recommendations = doc.select("ul#related li").map { element ->
+ MovieSearchResponse(
+ apiName = this@CimaNow.name,
+ url = element.select("a").attr("href"),
+ name = element.select("img:nth-child(2)").attr("alt"),
+ posterUrl = element.select("img:nth-child(2)").attr("src")
+ )
+ }
+
+ return if (isMovie) {
+ newMovieLoadResponse(
+ title,
+ url,
+ TvType.Movie,
+ "$url/watching"
+ ) {
+ this.posterUrl = posterUrl
+ this.year = year
+ this.recommendations = recommendations
+ this.plot = synopsis
+ this.tags = tags
+ addTrailer(youtubeTrailer)
+ }
+ } else {
+ val episodes = doc.select("ul#eps li").map { episode ->
+ Episode(
+ episode.select("a").attr("href")+"/watching",
+ episode.select("a img:nth-child(2)").attr("alt"),
+ doc.select("span[aria-label=\"season-title\"]").html().replace(".*|\n".toRegex(), "").getIntFromText(),
+ episode.select("a em").text().toIntOrNull(),
+ episode.select("a img:nth-child(2)").attr("src")
+ )
+ }
+ newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes.distinct().sortedBy { it.episode }) {
+ this.posterUrl = posterUrl
+ this.tags = tags
+ this.year = year
+ this.plot = synopsis
+ this.recommendations = recommendations
+ addTrailer(youtubeTrailer)
+ }
+ }
+ }
+
+ override suspend fun loadLinks(
+ data: String,
+ isCasting: Boolean,
+ subtitleCallback: (SubtitleFile) -> Unit,
+ callback: (ExtractorLink) -> Unit
+ ): Boolean {
+ app.get("$data").document.select("ul#download [aria-label=\"quality\"]").forEach {
+ val name = if(it.select("span").text().contains("فائق السرعة")) "Fast Servers" else "Servers"
+ it.select("a").forEach { media ->
+ callback.invoke(
+ ExtractorLink(
+ source = this.name,
+ name = name,
+ url = media.attr("href"),
+ referer = this.mainUrl,
+ quality = media.text().getIntFromText() ?: Qualities.Unknown.value
+ )
+ )
+ }
+ }
+ return true
+ }
+}
\ No newline at end of file
diff --git a/EgyBestProvider/build.gradle.kts b/EgyBestProvider/build.gradle.kts
new file mode 100644
index 0000000..c702710
--- /dev/null
+++ b/EgyBestProvider/build.gradle.kts
@@ -0,0 +1,14 @@
+version = 7
+
+cloudstream {
+ description = "Egybest is broken"
+ authors = listOf( "ImZaw" )
+
+ language = "ar"
+
+ status = 0
+
+ tvTypes = listOf( "TvSeries" , "Movie" , "Anime" )
+
+ iconUrl = "https://www.google.com/s2/favicons?domain=www.egy.best&sz=%size%"
+}
diff --git a/EgyBestProvider/src/main/AndroidManifest.xml b/EgyBestProvider/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..09206f7
--- /dev/null
+++ b/EgyBestProvider/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/EgyBestProvider/src/main/kotlin/com/egybest/EgyBestPlugin.kt b/EgyBestProvider/src/main/kotlin/com/egybest/EgyBestPlugin.kt
new file mode 100644
index 0000000..f417ab6
--- /dev/null
+++ b/EgyBestProvider/src/main/kotlin/com/egybest/EgyBestPlugin.kt
@@ -0,0 +1,11 @@
+package com.egybest
+import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
+import com.lagradost.cloudstream3.plugins.Plugin
+import android.content.Context
+
+@CloudstreamPlugin
+class EgyBestPlugin: Plugin() {
+ override fun load(context: Context) {
+ registerMainAPI(EgyBest())
+ }
+}
\ No newline at end of file
diff --git a/EgyBestProvider/src/main/kotlin/com/egybest/EgyBestProvider.kt b/EgyBestProvider/src/main/kotlin/com/egybest/EgyBestProvider.kt
new file mode 100644
index 0000000..295e5c7
--- /dev/null
+++ b/EgyBestProvider/src/main/kotlin/com/egybest/EgyBestProvider.kt
@@ -0,0 +1,275 @@
+package com.egybest
+
+
+import android.annotation.TargetApi
+import android.os.Build
+import com.lagradost.cloudstream3.*
+import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
+import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.M3u8Helper
+import com.lagradost.nicehttp.Requests
+import com.lagradost.nicehttp.Session
+import okhttp3.HttpUrl.Companion.toHttpUrl
+import org.jsoup.nodes.Element
+import java.util.Base64
+import org.mozilla.javascript.Context
+import org.mozilla.javascript.Scriptable
+
+fun String.runJS(variableName: String): String {
+ val rhino = Context.enter()
+ rhino.initSafeStandardObjects()
+ rhino.optimizationLevel = -1
+ val scope: Scriptable = rhino.initSafeStandardObjects()
+ val script = this
+ val result: String
+ try {
+ var js = ""
+ for (i in script.indices) {
+ js += script[i]
+ }
+ rhino.evaluateString(scope, js, "JavaScript", 1, null)
+ result = Context.toString(scope.get(variableName, scope))
+ } finally {
+ Context.exit()
+ }
+ return result
+}
+
+class EgyBest : MainAPI() {
+ override var lang = "ar"
+ override var mainUrl = "https://egybest.org"
+ override var name = "EgyBest"
+ var pssid = ""
+ override val usesWebView = false
+ override val hasMainPage = true
+ override val supportedTypes = setOf(TvType.TvSeries, TvType.Movie, TvType.Anime)
+
+ 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")
+ var title = select("span.title").text()
+ val year = title.getYearFromTitle()
+ val isMovie = Regex(".*/movie/.*|.*/masrahiya/.*").matches(url)
+ val tvType = if (isMovie) TvType.Movie else TvType.TvSeries
+ title = if (year !== null) title else title.split(" (")[0].trim()
+ val quality = select("span.ribbon span").text().replace("-", "")
+ // If you need to differentiate use the url.
+ return MovieSearchResponse(
+ title,
+ mainUrl + url,
+ this@EgyBest.name,
+ tvType,
+ posterUrl,
+ year,
+ null,
+ quality = getQualityFromString(quality)
+ )
+ }
+
+ override val mainPage = mainPageOf(
+ "$mainUrl/trending/?page=" to "الأفلام الأكثر مشاهدة",
+ "$mainUrl/movies/?page=" to "أفلام جديدة",
+ "$mainUrl/tv/?page=" to "مسلسلات جديدة ",
+ "$mainUrl/tv/korean?page=" to "الدراما الكورية ",
+ "$mainUrl/animes/popular?page=" to "مسلسلات الانمي",
+ "$mainUrl/wwe/?page=" to "عروض المصارعة ",
+ "$mainUrl/movies/latest-bluray-2020-2019?page=" to "أفلام جديدة BluRay",
+ "$mainUrl/masrahiyat/?page=" to "مسرحيات ",
+ "$mainUrl/movies/latest?page=" to "أحدث الاضافات",
+ "$mainUrl/movies/comedy?page=" to "أفلام كوميدية",
+ "$mainUrl/explore/?q=superhero/" to "أفلام سوبر هيرو",
+ "$mainUrl/movies/animation?page=" to "أفلام انمي و كرتون",
+ "$mainUrl/movies/romance?page=" to "أفلام رومانسية",
+ "$mainUrl/movies/drama?page=" to "أفلام دراما",
+ "$mainUrl/movies/horror?page=" to "أفلام رعب",
+ "$mainUrl/movies/documentary?page=" to "أفلام وثائقية",
+ "$mainUrl/World-War-Movies/?page=" to "أفلام عن الحرب العالمية ☢",
+ "$mainUrl/End-Of-The-World-Movies/?page=" to "أفلام عن نهاية العالم",
+ "$mainUrl/movies/arab?page=" to "أفلام عربية ",
+ )
+
+ override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
+ val doc = app.get(request.data + page).document
+ val list = doc.select(".movie")
+ .mapNotNull { element ->
+ element.toSearchResponse()
+ }
+ return newHomePageResponse(request.name, list)
+ }
+
+ override suspend fun search(query: String): List {
+ val result = arrayListOf()
+ listOf("$mainUrl/explore/?q=$query").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 }
+ }
+
+ private fun String.getYearFromTitle(): Int? {
+ return Regex("""\(\d{4}\)""").find(this)?.groupValues?.firstOrNull()?.toIntOrNull()
+ }
+
+ override suspend fun load(url: String): LoadResponse {
+ val doc = app.get(url).document
+ val isMovie = Regex(".*/movie/.*|.*/masrahiya/.*").matches(url)
+ 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").text()
+ val youtubeTrailer = doc.select("div.play")?.attr("url")
+
+ 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() }
+
+ val actors = doc.select("div.cast_list .cast_item").mapNotNull {
+ val name = it.selectFirst("div > a > img")?.attr("alt") ?: return@mapNotNull null
+ val image = it.selectFirst("div > a > img")?.attr("src") ?: return@mapNotNull null
+ val roleString = it.selectFirst("div > span")!!.text()
+ val mainActor = Actor(name, image)
+ ActorData(actor = mainActor, roleString = roleString)
+ }
+
+ return if (isMovie) {
+ val recommendations = doc.select(".movies_small .movie").mapNotNull { element ->
+ element.toSearchResponse()
+ }
+
+ newMovieLoadResponse(
+ title,
+ url,
+ TvType.Movie,
+ url
+ ) {
+ this.posterUrl = posterUrl
+ this.year = year
+ this.recommendations = recommendations
+ this.plot = synopsis
+ this.tags = tags
+ this.actors = actors
+ addTrailer(youtubeTrailer)
+ }
+ } 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()
+ if(d.select("tr.published").isNotEmpty()) {
+ d.select("tr.published").map { element ->
+ val ep = Regex("ep-(.....)").find(element.select(".ep_title a").attr("href"))?.groupValues?.getOrNull(1)?.getIntFromText()
+ episodes.add(
+ Episode(
+ mainUrl + element.select(".ep_title a").attr("href"),
+ name = element.select("td.ep_title").html().replace(".*|".toRegex(), ""),
+ season,
+ ep,
+ rating = element.select("td.tam:not(.date, .ep_len)").text().getIntFromText()
+ )
+ )
+ }
+ } else {
+ 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(
+ Episode(
+ mainUrl + eit.attr("href"),
+ eit.select("span.title").text(),
+ season,
+ ep,
+ )
+ )
+ }
+ }
+ }
+ newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes.distinct().sortedBy { it.episode }) {
+ this.posterUrl = posterUrl
+ this.tags = tags
+ this.year = year
+ this.plot = synopsis
+ this.actors = actors
+ addTrailer(youtubeTrailer)
+ }
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.O)
+ override suspend fun loadLinks(
+ data: String,
+ isCasting: Boolean,
+ subtitleCallback: (SubtitleFile) -> Unit,
+ callback: (ExtractorLink) -> Unit
+ ): Boolean {
+ val baseURL = data.split("/")[0] + "//" + data.split("/")[2]
+ val client = Requests().baseClient
+ val session = Session(client)
+ println(baseURL)
+ println(data)
+ val doc = session.get(data).document
+
+ val vidstreamURL = baseURL + doc.select("iframe.auto-size").attr("src")
+
+ val videoSoup = session.get(vidstreamURL, cookies = mapOf(
+ "PSSID" to this@EgyBest.pssid,
+ )).document
+ videoSoup.select("source").firstOrNull { it.hasAttr("src") }?.attr("src")?.let {
+ M3u8Helper.generateM3u8(
+ this.name,
+ it,
+ referer = mainUrl,
+ headers = mapOf("range" to "bytes=0-")
+ ).forEach(callback)
+ } ?: run {
+ var jsCode = videoSoup.select("script")[1].html()
+ val function = videoSoup.select("script")[2].attr("onload")
+ val verificationToken = Regex("\\{'[0-9a-zA-Z_]*':'ok'\\}").findAll(jsCode).first().value.replace("\\{'|':.*".toRegex(), "")
+ val encodedAdLinkVar = Regex("\\([0-9a-zA-Z_]{2,12}\\[Math").findAll(jsCode).first().value.replace("\\(|\\[M.*".toRegex(),"")
+ val encodingArraysRegEx = Regex(",[0-9a-zA-Z_]{2,12}=\\[]").findAll(jsCode).toList()
+ val firstEncodingArray = encodingArraysRegEx[1].value.replace(",|=.*".toRegex(),"")
+ val secondEncodingArray = encodingArraysRegEx[2].value.replace(",|=.*".toRegex(),"")
+
+ jsCode = jsCode.replace("^