()
+ 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..2c76d95
--- /dev/null
+++ b/EgyBestProvider/build.gradle.kts
@@ -0,0 +1,12 @@
+version = 1
+
+cloudstream {
+ description = ""
+ authors = listOf( "ImZaw" )
+
+ status = 1
+
+ tvTypes = listOf( "TvSeries" , "Movie" , "Anime" )
+
+ iconUrl = "https://www.google.com/s2/favicons?domain=www.egy.best&sz=24"
+}
\ No newline at end of file
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..4dbe2a9
--- /dev/null
+++ b/EgyBestProvider/src/main/kotlin/com/egybest/EgyBestProvider.kt
@@ -0,0 +1,262 @@
+package com.egybest
+
+
+import com.fasterxml.jackson.annotation.JsonProperty
+import com.lagradost.cloudstream3.*
+import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
+import com.lagradost.cloudstream3.utils.AppUtils.parseJson
+import com.lagradost.cloudstream3.utils.Qualities
+import org.jsoup.nodes.Element
+import com.lagradost.nicehttp.Requests
+
+class EgyBest : MainAPI() {
+ override var lang = "ar"
+ override var mainUrl = "https://www.egy.best"
+ override var name = "EgyBest"
+ 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,
+ url,
+ this@EgyBest.name,
+ tvType,
+ posterUrl,
+ year,
+ null,
+ quality = getQualityFromString(quality)
+ )
+ }
+
+ override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
+ // url, title
+ val doc = app.get(mainUrl).document
+ val pages = arrayListOf()
+ doc.select("#mainLoad div.mbox").apmap {
+ val name = it.select(".bdb.pda > strong").text()
+ if (it.select(".movie").first()?.attr("href")?.contains("season-(.....)|ep-(.....)".toRegex()) == true) return@apmap
+ val list = arrayListOf()
+ it.select(".movie").map { element ->
+ list.add(element.toSearchResponse()!!)
+ }
+ pages.add(HomePageList(name, list))
+ }
+ 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 }
+ }
+
+ 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(
+ 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(
+ 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)
+ }
+ }
+ }
+ data class Sources (
+ @JsonProperty("quality") val quality: Int?,
+ @JsonProperty("link") val link: String
+ )
+
+ private fun String.ExtractLinks(): Boolean? {
+ val list = Regex("#EXT.*\\n.*").findAll(this).toList()
+ println(list)
+ list.map {
+ val url = Regex(".*stream\\.m3u8").find(it.value)?.value.toString()
+ val quality = Regex("[0-9]{3,4}x[0-9]{3,4}").find(it.value)?.value?.replace(".*x".toRegex(),"")?.toInt()
+ ExtractorLink(
+ this@EgyBest.name,
+ this@EgyBest.name,
+ url,
+ this@EgyBest.mainUrl,
+ quality ?: Qualities.Unknown.value,
+ true
+ )
+ }
+ return true
+ }
+ override suspend fun loadLinks(
+ data: String,
+ isCasting: Boolean,
+ subtitleCallback: (SubtitleFile) -> Unit,
+ callback: (ExtractorLink) -> Unit
+ ): Boolean {
+ /*val baseURL = data.split("/")[0] + "//" + data.split("/")[2]
+
+ val session = Requests()
+ val episodeSoup = session.get(data).document
+
+ val vidstreamURL = fixUrlNull(episodeSoup.selectFirst("iframe.auto-size")?.attr("src") ) ?: throw ErrorLoadingException("No iframe")
+
+ val videoSoup = app.get(vidstreamURL).document
+ fixUrlNull( videoSoup.select("source").firstOrNull { it.hasAttr("src") }?.attr("src"))?.let {
+ app.get(it).text.replace("#EXTM3U\n","").ExtractLinks()
+ } ?: run {
+ var jsCode = videoSoup.select("script")[1].data()
+ println(jsCode)
+ 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("^