From dd683a98d22b5f87f7cca5c4794f36eb58c85385 Mon Sep 17 00:00:00 2001 From: Zaw <42999156+ImZaw@users.noreply.github.com> Date: Tue, 16 Aug 2022 23:11:46 +0300 Subject: [PATCH] Added Providers --- .gitignore | 12 + AkwamProvider/build.gradle.kts | 12 + AkwamProvider/src/main/AndroidManifest.xml | 2 + .../src/main/kotlin/com/akwam/AkwamPlugin.kt | 11 + .../main/kotlin/com/akwam/AkwamProvider.kt | 221 ++++++++++++ AnimeBlkomProvider/build.gradle.kts | 12 + .../src/main/AndroidManifest.xml | 2 + .../kotlin/com/animeblkom/AnimeBlkomPlugin.kt | 11 + .../com/animeblkom/AnimeBlkomProvider.kt | 127 +++++++ CimaNowProvider/build.gradle.kts | 12 + CimaNowProvider/src/main/AndroidManifest.xml | 2 + .../main/kotlin/com/cimanow/CimaNowPlugin.kt | 11 + .../kotlin/com/cimanow/CimaNowProvider.kt | 164 +++++++++ EgyBestProvider/build.gradle.kts | 12 + EgyBestProvider/src/main/AndroidManifest.xml | 2 + .../main/kotlin/com/egybest/EgyBestPlugin.kt | 11 + .../kotlin/com/egybest/EgyBestProvider.kt | 262 ++++++++++++++ Extractors/build.gradle.kts | 10 + Extractors/src/main/AndroidManifest.xml | 2 + .../kotlin/com/extractors/DoodExtractor.kt | 62 ++++ .../kotlin/com/extractors/ExtractorPlugin.kt | 28 ++ .../com/extractors/JWPlayerExtractor.kt | 61 ++++ .../com/extractors/StreamTapeExtractor.kt | 33 ++ .../com/extractors/StreamlareExtractor.kt | 60 ++++ .../com/extractors/UpstreamExtractor.kt | 59 ++++ .../kotlin/com/extractors/UqloadExtractor.kt | 49 +++ .../kotlin/com/extractors/VoeExtractor.kt | 51 +++ FaselHDProvider/build.gradle.kts | 12 + FaselHDProvider/src/main/AndroidManifest.xml | 2 + .../main/kotlin/com/faselhd/FaselHDPlugin.kt | 11 + .../kotlin/com/faselhd/FaselHDProvider.kt | 160 +++++++++ MovizlandProvider/build.gradle.kts | 12 + .../src/main/AndroidManifest.xml | 2 + .../kotlin/com/movizland/MovizlandPlugin.kt | 11 + .../kotlin/com/movizland/MovizlandProvider.kt | 151 ++++++++ MyCimaProvider/build.gradle.kts | 12 + MyCimaProvider/src/main/AndroidManifest.xml | 2 + .../main/kotlin/com/mycima/MyCimaPlugin.kt | 11 + .../main/kotlin/com/mycima/MyCimaProvider.kt | 327 ++++++++++++++++++ Shahid4uProvider/build.gradle.kts | 12 + Shahid4uProvider/src/main/AndroidManifest.xml | 2 + .../kotlin/com/shahid4u/Shahid4uPlugin.kt | 11 + .../kotlin/com/shahid4u/Shahid4uProvider.kt | 174 ++++++++++ YallaShootsProvider/build.gradle.kts | 12 + .../src/main/AndroidManifest.xml | 2 + .../com/yallashoots/YallaShootsPlugin.kt | 11 + .../com/yallashoots/YallaShootsProvider.kt | 86 +++++ build.gradle.kts | 86 +++++ gradle.properties | 19 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54329 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 172 +++++++++ gradlew.bat | 84 +++++ settings.gradle.kts | 13 + 54 files changed, 2704 insertions(+) create mode 100644 .gitignore create mode 100644 AkwamProvider/build.gradle.kts create mode 100644 AkwamProvider/src/main/AndroidManifest.xml create mode 100644 AkwamProvider/src/main/kotlin/com/akwam/AkwamPlugin.kt create mode 100644 AkwamProvider/src/main/kotlin/com/akwam/AkwamProvider.kt create mode 100644 AnimeBlkomProvider/build.gradle.kts create mode 100644 AnimeBlkomProvider/src/main/AndroidManifest.xml create mode 100644 AnimeBlkomProvider/src/main/kotlin/com/animeblkom/AnimeBlkomPlugin.kt create mode 100644 AnimeBlkomProvider/src/main/kotlin/com/animeblkom/AnimeBlkomProvider.kt create mode 100644 CimaNowProvider/build.gradle.kts create mode 100644 CimaNowProvider/src/main/AndroidManifest.xml create mode 100644 CimaNowProvider/src/main/kotlin/com/cimanow/CimaNowPlugin.kt create mode 100644 CimaNowProvider/src/main/kotlin/com/cimanow/CimaNowProvider.kt create mode 100644 EgyBestProvider/build.gradle.kts create mode 100644 EgyBestProvider/src/main/AndroidManifest.xml create mode 100644 EgyBestProvider/src/main/kotlin/com/egybest/EgyBestPlugin.kt create mode 100644 EgyBestProvider/src/main/kotlin/com/egybest/EgyBestProvider.kt create mode 100644 Extractors/build.gradle.kts create mode 100644 Extractors/src/main/AndroidManifest.xml create mode 100644 Extractors/src/main/kotlin/com/extractors/DoodExtractor.kt create mode 100644 Extractors/src/main/kotlin/com/extractors/ExtractorPlugin.kt create mode 100644 Extractors/src/main/kotlin/com/extractors/JWPlayerExtractor.kt create mode 100644 Extractors/src/main/kotlin/com/extractors/StreamTapeExtractor.kt create mode 100644 Extractors/src/main/kotlin/com/extractors/StreamlareExtractor.kt create mode 100644 Extractors/src/main/kotlin/com/extractors/UpstreamExtractor.kt create mode 100644 Extractors/src/main/kotlin/com/extractors/UqloadExtractor.kt create mode 100644 Extractors/src/main/kotlin/com/extractors/VoeExtractor.kt create mode 100644 FaselHDProvider/build.gradle.kts create mode 100644 FaselHDProvider/src/main/AndroidManifest.xml create mode 100644 FaselHDProvider/src/main/kotlin/com/faselhd/FaselHDPlugin.kt create mode 100644 FaselHDProvider/src/main/kotlin/com/faselhd/FaselHDProvider.kt create mode 100644 MovizlandProvider/build.gradle.kts create mode 100644 MovizlandProvider/src/main/AndroidManifest.xml create mode 100644 MovizlandProvider/src/main/kotlin/com/movizland/MovizlandPlugin.kt create mode 100644 MovizlandProvider/src/main/kotlin/com/movizland/MovizlandProvider.kt create mode 100644 MyCimaProvider/build.gradle.kts create mode 100644 MyCimaProvider/src/main/AndroidManifest.xml create mode 100644 MyCimaProvider/src/main/kotlin/com/mycima/MyCimaPlugin.kt create mode 100644 MyCimaProvider/src/main/kotlin/com/mycima/MyCimaProvider.kt create mode 100644 Shahid4uProvider/build.gradle.kts create mode 100644 Shahid4uProvider/src/main/AndroidManifest.xml create mode 100644 Shahid4uProvider/src/main/kotlin/com/shahid4u/Shahid4uPlugin.kt create mode 100644 Shahid4uProvider/src/main/kotlin/com/shahid4u/Shahid4uProvider.kt create mode 100644 YallaShootsProvider/build.gradle.kts create mode 100644 YallaShootsProvider/src/main/AndroidManifest.xml create mode 100644 YallaShootsProvider/src/main/kotlin/com/yallashoots/YallaShootsPlugin.kt create mode 100644 YallaShootsProvider/src/main/kotlin/com/yallashoots/YallaShootsProvider.kt create mode 100644 build.gradle.kts create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle.kts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..41fd579 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +*.iml +.gradle +/local.properties +/.idea +.DS_Store +/build +**/build +/captures +.externalNativeBuild +.cxx +local.properties +.vscode \ No newline at end of file diff --git a/AkwamProvider/build.gradle.kts b/AkwamProvider/build.gradle.kts new file mode 100644 index 0000000..ef99315 --- /dev/null +++ b/AkwamProvider/build.gradle.kts @@ -0,0 +1,12 @@ +version = 1 + +cloudstream { + description = "" + authors = listOf( "ImZaw" ) + + status = 1 + + tvTypes = listOf( "TvSeries" , "Movie" , "Anime" , "Cartoon" ) + + iconUrl = "https://www.google.com/s2/favicons?domain=akwam.to&sz=24" +} \ No newline at end of file diff --git a/AkwamProvider/src/main/AndroidManifest.xml b/AkwamProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..ba79288 --- /dev/null +++ b/AkwamProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/AkwamProvider/src/main/kotlin/com/akwam/AkwamPlugin.kt b/AkwamProvider/src/main/kotlin/com/akwam/AkwamPlugin.kt new file mode 100644 index 0000000..4e8b42e --- /dev/null +++ b/AkwamProvider/src/main/kotlin/com/akwam/AkwamPlugin.kt @@ -0,0 +1,11 @@ +package com.akwam +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class AkwamPlugin: Plugin() { + override fun load(context: Context) { + registerMainAPI(Akwam()) + } +} \ No newline at end of file diff --git a/AkwamProvider/src/main/kotlin/com/akwam/AkwamProvider.kt b/AkwamProvider/src/main/kotlin/com/akwam/AkwamProvider.kt new file mode 100644 index 0000000..cc84c5f --- /dev/null +++ b/AkwamProvider/src/main/kotlin/com/akwam/AkwamProvider.kt @@ -0,0 +1,221 @@ +package com.akwam + + +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.LoadResponse.Companion.addActors +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.Qualities +import org.jsoup.nodes.Element + +class Akwam : MainAPI() { + override var lang = "ar" + override var mainUrl = "https://akwam.to" + override var name = "Akwam" + override val usesWebView = false + override val hasMainPage = true + override val supportedTypes = setOf(TvType.TvSeries, TvType.Movie, TvType.Anime, TvType.Cartoon) + + private fun Element.toSearchResponse(): SearchResponse? { + val url = select("a.box").attr("href") ?: return null + if (url.contains("/games/") || url.contains("/programs/")) return null + val poster = select("picture > img") + val title = poster.attr("alt") + val posterUrl = poster.attr("data-src") + val year = select(".badge-secondary").text().toIntOrNull() + + // If you need to differentiate use the url. + return MovieSearchResponse( + title, + url, + this@Akwam.name, + TvType.TvSeries, + posterUrl, + year, + null, + ) + } + override val mainPage = mainPageOf( + "$mainUrl/movies?page=" to "Movies", + "$mainUrl/series?page=" to "Series", + "$mainUrl/shows?page=" to "Shows" + ) + + override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { + val doc = app.get(request.data + page).document + val list = doc.select("div.col-lg-auto.col-md-4.col-6.mb-12").mapNotNull { element -> + element.toSearchResponse() + } + return newHomePageResponse(request.name, list) + } + + override suspend fun search(query: String): List { + val url = "$mainUrl/search?q=$query" + val doc = app.get(url).document + return doc.select("div.col-lg-auto").mapNotNull { + it.toSearchResponse() + } + } + + private fun String.getIntFromText(): Int? { + return Regex("""\d+""").find(this)?.groupValues?.firstOrNull()?.toIntOrNull() + } + + private fun Element.toEpisode(): Episode { + val a = select("a.text-white") + val url = a.attr("href") + val title = a.text() + val thumbUrl = select("picture > img").attr("src") + val date = select("p.entry-date").text() + return newEpisode(url) { + name = title + episode = title.getIntFromText() + posterUrl = thumbUrl + addDate(date) + } + } + + + override suspend fun load(url: String): LoadResponse { + val doc = app.get(url).document + val isMovie = url.contains("/movie/") + val title = doc.select("h1.entry-title").text() + val posterUrl = doc.select("picture > img").attr("src") + + val year = + doc.select("div.font-size-16.text-white.mt-2").firstOrNull { + it.text().contains("السنة") + }?.text()?.getIntFromText() + + // A bit iffy to parse twice like this, but it'll do. + val duration = + doc.select("div.font-size-16.text-white.mt-2").firstOrNull { + it.text().contains("مدة الفيلم") + }?.text()?.getIntFromText() + + val synopsis = doc.select("div.widget-body p:first-child").text() + + val rating = doc.select("span.mx-2").text().split("/").lastOrNull()?.toRatingInt() + + val tags = doc.select("div.font-size-16.d-flex.align-items-center.mt-3 > a").map { + it.text() + } + + val actors = doc.select("div.widget-body > div > div.entry-box > a").mapNotNull { + val name = it?.selectFirst("div > .entry-title")?.text() ?: return@mapNotNull null + val image = it.selectFirst("div > img")?.attr("src") ?: return@mapNotNull null + Actor(name, image) + } + + val recommendations = + doc.select("div > div.widget-body > div.row > div > div.entry-box").mapNotNull { + val recTitle = it?.selectFirst("div.entry-body > .entry-title > .text-white") + ?: return@mapNotNull null + val href = recTitle.attr("href") ?: return@mapNotNull null + val name = recTitle.text() ?: return@mapNotNull null + val poster = it.selectFirst(".entry-image > a > picture > img")?.attr("data-src") + ?: return@mapNotNull null + MovieSearchResponse(name, href, this.name, TvType.Movie, fixUrl(poster)) + } + + return if (isMovie) { + newMovieLoadResponse( + title, + url, + TvType.Movie, + url + ) { + this.posterUrl = posterUrl + this.year = year + this.plot = synopsis + this.rating = rating + this.tags = tags + this.duration = duration + this.recommendations = recommendations + addActors(actors) + } + } else { + val episodes = doc.select("div.bg-primary2.p-4.col-lg-4.col-md-6.col-12").map { + it.toEpisode() + }.let { + val isReversed = (it.lastOrNull()?.episode ?: 1) < (it.firstOrNull()?.episode ?: 0) + if (isReversed) + it.reversed() + else it + } + + newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) { + this.duration = duration + this.posterUrl = posterUrl + this.tags = tags.filterNotNull() + this.rating = rating + this.year = year + this.plot = synopsis + this.recommendations = recommendations + addActors(actors) + } + } + } + + +// // Maybe possible to not use the url shortener but cba investigating that. +// private suspend fun skipUrlShortener(url: String): AppResponse { +// return app.get(app.get(url).document.select("a.download-link").attr("href")) +// } + + private fun getQualityFromId(id: Int?): Qualities { + return when (id) { + 2 -> Qualities.P360 // Extrapolated + 3 -> Qualities.P480 + 4 -> Qualities.P720 + 5 -> Qualities.P1080 + else -> Qualities.Unknown + } + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + val doc = app.get(data).document + + val links = doc.select("div.tab-content.quality").map { element -> + val quality = getQualityFromId(element.attr("id").getIntFromText()) + element.select(".col-lg-6 > a:contains(تحميل)").map { linkElement -> + if (linkElement.attr("href").contains("/download/")) { + Pair( + linkElement.attr("href"), + quality, + ) + } else { + val url = "$mainUrl/download${ + linkElement.attr("href").split("/link")[1] + }${data.split("/movie|/episode|/show/episode".toRegex())[1]}" + Pair( + url, + quality, + ) + // just in case if they add the shorts urls again + } + } + }.flatten() + + links.map { + val linkDoc = app.get(it.first).document + val button = linkDoc.select("div.btn-loader > a") + val url = button.attr("href") + + callback.invoke( + ExtractorLink( + this.name, + this.name, + url, + this.mainUrl, + it.second.value + ) + ) + } + return true + } +} \ No newline at end of file diff --git a/AnimeBlkomProvider/build.gradle.kts b/AnimeBlkomProvider/build.gradle.kts new file mode 100644 index 0000000..cc115d8 --- /dev/null +++ b/AnimeBlkomProvider/build.gradle.kts @@ -0,0 +1,12 @@ +version = 1 + +cloudstream { + description = "" + authors = listOf( "ImZaw" ) + + status = 1 + + tvTypes = listOf( "Anime" , "AnimeMovie" , "OVA" ) + + iconUrl = "https://www.google.com/s2/favicons?domain=animeblkom.net&sz=24" +} \ No newline at end of file diff --git a/AnimeBlkomProvider/src/main/AndroidManifest.xml b/AnimeBlkomProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..2887205 --- /dev/null +++ b/AnimeBlkomProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/AnimeBlkomProvider/src/main/kotlin/com/animeblkom/AnimeBlkomPlugin.kt b/AnimeBlkomProvider/src/main/kotlin/com/animeblkom/AnimeBlkomPlugin.kt new file mode 100644 index 0000000..5278e84 --- /dev/null +++ b/AnimeBlkomProvider/src/main/kotlin/com/animeblkom/AnimeBlkomPlugin.kt @@ -0,0 +1,11 @@ +package com.animeblkom +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class AnimeBlkomPlugin: Plugin() { + override fun load(context: Context) { + registerMainAPI(AnimeBlkom()) + } +} \ No newline at end of file diff --git a/AnimeBlkomProvider/src/main/kotlin/com/animeblkom/AnimeBlkomProvider.kt b/AnimeBlkomProvider/src/main/kotlin/com/animeblkom/AnimeBlkomProvider.kt new file mode 100644 index 0000000..08351ea --- /dev/null +++ b/AnimeBlkomProvider/src/main/kotlin/com/animeblkom/AnimeBlkomProvider.kt @@ -0,0 +1,127 @@ +package com.animeblkom + + +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId +import com.lagradost.cloudstream3.utils.ExtractorLink +import org.jsoup.nodes.Element + +class AnimeBlkom : MainAPI() { + override var mainUrl = "https://animeblkom.net" + override var name = "AnimeBlkom" + override var lang = "ar" + override val hasMainPage = true + + override val supportedTypes = setOf( + TvType.Anime, + TvType.AnimeMovie, + TvType.OVA, + ) + + private fun Element.toSearchResponse(): SearchResponse { + val url = select("div.poster a").attr("href") + val name = select("div.name a").text() + val poster = mainUrl + select("div.poster img").attr("data-original") + val year = select("div[title=\"سنة الانتاج\"]").text().toIntOrNull() + val episodesNumber = select("div[title=\"عدد الحلقات\"]").text().toIntOrNull() + val tvType = select("div[title=\"النوع\"]").text().let { if(it.contains("فيلم|خاصة".toRegex())) TvType.AnimeMovie else if(it.contains("أوفا|أونا".toRegex())) TvType.OVA else TvType.Anime } + return newAnimeSearchResponse( + name, + url, + tvType, + ) { + addDubStatus(false, episodesNumber) + this.year = year + this.posterUrl = poster + } + } + override val mainPage = mainPageOf( + "$mainUrl/anime-list?sort_by=rate&page=" to "Most rated", + "$mainUrl/anime-list?sort_by=created_at&page=" to "Recently added", + "$mainUrl/anime-list?states=finished&page=" to "Completed" + ) + override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { + val doc = app.get(request.data + page).document + val list = doc.select("div.content-inner") + .mapNotNull { element -> + element.toSearchResponse() + } + return newHomePageResponse(request.name, list) + } + + override suspend fun search(query: String): List { + val q = query.replace(" ","+") + return app.get("$mainUrl/search?query=$q").document.select("div.content.ratable").map { + it.toSearchResponse() + } + } + override suspend fun load(url: String): LoadResponse { + val doc = app.get(url).document + + val title = doc.select("span h1").text().replace("\\(.*".toRegex(),"") + val poster = mainUrl + doc.select("div.poster img").attr("data-original") + val description = doc.select(".story p").text() + val genre = doc.select("p.genres a").map { + it.text() + } + val year = doc.select(".info-table div:contains(تاريخ الانتاج) span.info").text().split("-")[0].toIntOrNull() + val status = doc.select(".info-table div:contains(حالة الأنمي) span.info").text().let { if(it.contains("مستمر")) ShowStatus.Ongoing else ShowStatus.Completed } + val nativeName = doc.select("span[title=\"الاسم باليابانية\"]").text().replace(".*:".toRegex(),"") + val type = doc.select("h1 small").text().let { + if (it.contains("movie")) TvType.AnimeMovie + if (it.contains("ova|ona".toRegex())) TvType.OVA + else TvType.Anime + } + + val malId = doc.select("a.blue.cta:contains(المزيد من المعلومات)").attr("href").replace(".*e\\/|\\/.*".toRegex(),"").toInt() + val episodes = arrayListOf() + val episodeElements = doc.select(".episode-link") + if(episodeElements.isEmpty()) { + episodes.add(Episode( + url, + "Watch", + )) + } else { + episodeElements.map { + val a = it.select("a") + episodes.add(Episode( + mainUrl + a.attr("href"), + a.text().replace(":"," "), + episode = a.select("span").not(".pull-left").last()?.text()?.toIntOrNull() + )) + } + } + return newAnimeLoadResponse(title, url, type) { + addMalId(malId) + japName = nativeName + engName = title + posterUrl = poster + this.year = year + addEpisodes(DubStatus.Subbed, episodes) // TODO CHECK + plot = description + tags = genre + + showStatus = status + } + } + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + val doc = app.get(data).document + doc.select(".panel .panel-body a").forEach { + callback.invoke( + ExtractorLink( + this.name, + it.attr("title") + " " + it.select("small").text(), + it.attr("href"), + this.mainUrl, + it.text().replace("p.*| ".toRegex(),"").toInt(), + ) + ) + } + return true + } +} \ No newline at end of file diff --git a/CimaNowProvider/build.gradle.kts b/CimaNowProvider/build.gradle.kts new file mode 100644 index 0000000..7069838 --- /dev/null +++ b/CimaNowProvider/build.gradle.kts @@ -0,0 +1,12 @@ +version = 1 + +cloudstream { + description = "" + authors = listOf( "ImZaw" ) + + status = 1 + + tvTypes = listOf( "TvSeries" , "Movie" ) + + iconUrl = "https://www.google.com/s2/favicons?domain=cimanow.cc&sz=24" +} \ No newline at end of file diff --git a/CimaNowProvider/src/main/AndroidManifest.xml b/CimaNowProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..95a9f4b --- /dev/null +++ b/CimaNowProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/CimaNowProvider/src/main/kotlin/com/cimanow/CimaNowPlugin.kt b/CimaNowProvider/src/main/kotlin/com/cimanow/CimaNowPlugin.kt new file mode 100644 index 0000000..ddb39b4 --- /dev/null +++ b/CimaNowProvider/src/main/kotlin/com/cimanow/CimaNowPlugin.kt @@ -0,0 +1,11 @@ +package com.cimanow +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class CimaNowPlugin: Plugin() { + override fun load(context: Context) { + registerMainAPI(CimaNow()) + } +} \ No newline at end of file diff --git a/CimaNowProvider/src/main/kotlin/com/cimanow/CimaNowProvider.kt b/CimaNowProvider/src/main/kotlin/com/cimanow/CimaNowProvider.kt new file mode 100644 index 0000000..d9c45b7 --- /dev/null +++ b/CimaNowProvider/src/main/kotlin/com/cimanow/CimaNowProvider.kt @@ -0,0 +1,164 @@ +package com.cimanow + + +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.Qualities +import org.jsoup.nodes.Element + +class CimaNow : MainAPI() { + override var lang = "ar" + override var mainUrl = "https://cimanow.cc" + override var name = "CimaNow" + 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") + val posterUrl = select("img")?.attr("data-src") + var title = select("li[aria-label=\"title\"]").html().replace(" .*|\\\\n".toRegex(), "").replace(" ", "") + val year = select("li[aria-label=\"year\"]").text().toIntOrNull() + val tvType = if (url.contains("فيلم|مسرحية|حفلات".toRegex())) TvType.Movie else TvType.TvSeries + val quality = select("li[aria-label=\"ribbon\"]").first()?.text()?.replace(" |-|1080|720".toRegex(), "") + val dubEl = select("li[aria-label=\"ribbon\"]:nth-child(2)").isNotEmpty() + val dubStatus = if(dubEl) select("li[aria-label=\"ribbon\"]:nth-child(2)").text().contains("مدبلج") + else select("li[aria-label=\"ribbon\"]:nth-child(1)").text().contains("مدبلج") + if(dubStatus) title = "$title (مدبلج)" + return MovieSearchResponse( + "$title ${select("li[aria-label=\"ribbon\"]:contains(الموسم)").text()}", + url, + this@CimaNow.name, + tvType, + posterUrl, + year, + null, + quality = getQualityFromString(quality) + ) + } + + override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { + + val doc = app.get("$mainUrl/home", headers = mapOf("user-agent" to "MONKE")).document + val pages = doc.select("section").not("section:contains(أختر وجهتك المفضلة)").not("section:contains(تم اضافته حديثاً)").apmap { + val name = it.select("span").html().replace(".*| { + val result = arrayListOf() + 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("^