diff --git a/GateAnimeProvider/build.gradle.kts b/GateAnimeProvider/build.gradle.kts new file mode 100644 index 0000000..c0e4911 --- /dev/null +++ b/GateAnimeProvider/build.gradle.kts @@ -0,0 +1,14 @@ +version = 1 + +cloudstream { + description = "" + authors = listOf( "ImZaw" ) + + language = "ar" + + status = 1 + + tvTypes = listOf( "Anime", "TVSeries", "Movie" ) + + iconUrl = "https://b.gateanime.cam/wp-content/uploads/2020/12/cropped-Favicon-192x192.png" +} \ No newline at end of file diff --git a/GateAnimeProvider/src/main/AndroidManifest.xml b/GateAnimeProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..ace1207 --- /dev/null +++ b/GateAnimeProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/GateAnimeProvider/src/main/kotlin/com/gateanime/GateAnimePlugin.kt b/GateAnimeProvider/src/main/kotlin/com/gateanime/GateAnimePlugin.kt new file mode 100644 index 0000000..ef013b7 --- /dev/null +++ b/GateAnimeProvider/src/main/kotlin/com/gateanime/GateAnimePlugin.kt @@ -0,0 +1,11 @@ +package com.gateanime +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class Shahid4uPlugin: Plugin() { + override fun load(context: Context) { + registerMainAPI(GateAnime()) + } +} \ No newline at end of file diff --git a/GateAnimeProvider/src/main/kotlin/com/gateanime/GateAnimeProvider.kt b/GateAnimeProvider/src/main/kotlin/com/gateanime/GateAnimeProvider.kt new file mode 100644 index 0000000..cd80fef --- /dev/null +++ b/GateAnimeProvider/src/main/kotlin/com/gateanime/GateAnimeProvider.kt @@ -0,0 +1,140 @@ +package com.gateanime + + +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.loadExtractor +import org.jsoup.nodes.Element + +class GateAnime : MainAPI() { + override var lang = "ar" + override var mainUrl = "https://b.gateanime.cam" + override var name = "GateAnime" + override val usesWebView = false + override val hasMainPage = true + override val supportedTypes = + setOf(TvType.Anime, TvType.AnimeMovie, TvType.Cartoon ) + + fun hasEnglishLetters(string: String): Boolean { + for (c in string) + { + if (c !in 'A'..'Z' && c !in 'a'..'z') { + return false + } + } + return true + } + + private fun Element.toSearchResponse(): SearchResponse? { + val url = select("a").attr("href") + val title = select("h3.Title").text() + val posterUrl = select("img").attr("src") + val type = + if (select("span.TpTv.BgA").isNotEmpty()) TvType.Anime else TvType.AnimeMovie + val year = select("span.Year").text().toIntOrNull() + return newAnimeSearchResponse( + title, + url, + type, + ) { + addDubStatus(title.contains("مدبلج") || !hasEnglishLetters(title)) + this.year = year + this.posterUrl = posterUrl + } + } + override val mainPage = mainPageOf( + "$mainUrl/الأفلام/page/" to "Anime Movies", + "$mainUrl/المسلسلات/page/" to "Anime", + "$mainUrl/category/مدبلج/page/" to "Dubbed" + ) + + override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { + val doc = app.get(request.data + page).document + val list = doc.select("ul li.TPostMv") + .mapNotNull { element -> + element.toSearchResponse() + } + return newHomePageResponse(request.name, list) + } + + override suspend fun search(query: String): List { + return app.get("$mainUrl/?s=$query").document.select("ul li.TPostMv").mapNotNull { + it.toSearchResponse() + } + } + + override suspend fun load(url: String): LoadResponse { + val doc = app.get(url).document + + val title = doc.select("h1.Title").text() + val poster = doc.select("div.Image img").attr("src") + val description = doc.select("p:contains(قصة)").first()?.text() + val genre = doc.select("p:contains(التصنيفات)").text().replace("التصنيفات : ", "").split("،") + val year = doc.select(".Date").text().toIntOrNull() + val rating = doc.select("span.AAIco-star").text().toIntOrNull() + + val nativeName = doc.select(".SubTitle").text() + val type = if(url.contains("movie")) TvType.AnimeMovie else TvType.Anime + + val malId = doc.select("a:contains(myanimelist)").attr("href").replace(".*e\\/|\\/.*".toRegex(),"").toIntOrNull() + + val episodes = arrayListOf() + val backgroundImage = doc.select("img.TPostBg").first()?.attr("src") + val seasonsElements = doc.select("div.Wdgt.AABox") + if(seasonsElements.isEmpty()) { + episodes.add(Episode( + url, + "Watch", + posterUrl = backgroundImage + )) + } else { + seasonsElements.map { season -> + val seasonNumber = season.select("div.Title").attr("data-tab").toIntOrNull() + season.select("tr").forEach { + val titleTd = it.select("td.MvTbTtl a") + episodes.add(Episode( + titleTd.attr("href"), + titleTd.text(), + seasonNumber, + it.select("span.Num").text().toIntOrNull() + )) + } + } + } + return newAnimeLoadResponse(title, url, type) { + addMalId(malId) + japName = nativeName + engName = title + posterUrl = poster + this.year = year + addEpisodes(if(title.contains("مدبلج")) DubStatus.Dubbed else DubStatus.Subbed, episodes) // TODO CHECK + plot = description + tags = genre + this.rating = rating + + } + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + println("URL: $data") + val doc = app.get(data).document + doc.select( + "li:contains(Fembed), li:contains(خيارات 1), li:contains(Uptostream), li:contains(Dood), li:contains(Uqload)" + ).apmap { + val id = it.attr("data-tplayernv") + val iframeLink = doc.select("div#$id").html().replace(".*src=\"|\".*|#038;|amp;".toRegex(), "").replace("