diff --git a/AkwamProvider/build.gradle.kts b/AkwamProvider/build.gradle.kts new file mode 100644 index 0000000..2451b0c --- /dev/null +++ b/AkwamProvider/build.gradle.kts @@ -0,0 +1,27 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "ar" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + // authors = listOf("Cloudburst") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "Anime", + "Cartoon", + "TvSeries", + "Movie", + ) + iconUrl = "https://www.google.com/s2/favicons?domain=akwam.to&sz=%size%" +} \ 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..29aec9d --- /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/lagradost/AkwamProvider.kt b/AkwamProvider/src/main/kotlin/com/lagradost/AkwamProvider.kt new file mode 100644 index 0000000..b262f3a --- /dev/null +++ b/AkwamProvider/src/main/kotlin/com/lagradost/AkwamProvider.kt @@ -0,0 +1,224 @@ +package com.lagradost + +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 AkwamProvider : 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@AkwamProvider.name, + TvType.TvSeries, + posterUrl, + year, + null, + ) + } + + override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { + // Title, Url + val moviesUrl = listOf( + "Movies" to "$mainUrl/movies", + "Series" to "$mainUrl/series", + "Shows" to "$mainUrl/shows" + ) + val pages = moviesUrl.apmap { + val doc = app.get(it.second).document + val list = doc.select("div.col-lg-auto.col-md-4.col-6.mb-12").mapNotNull { element -> + element.toSearchResponse() + } + HomePageList(it.first, list) + }.sortedBy { it.name } + return HomePageResponse(pages) + } + + 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 + } +} diff --git a/AkwamProvider/src/main/kotlin/com/lagradost/AkwamProviderPlugin.kt b/AkwamProvider/src/main/kotlin/com/lagradost/AkwamProviderPlugin.kt new file mode 100644 index 0000000..9f65a9c --- /dev/null +++ b/AkwamProvider/src/main/kotlin/com/lagradost/AkwamProviderPlugin.kt @@ -0,0 +1,14 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class AkwamProviderPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(AkwamProvider()) + } +} \ No newline at end of file diff --git a/AltadefinizioneProvider/build.gradle.kts b/AltadefinizioneProvider/build.gradle.kts new file mode 100644 index 0000000..2d177e7 --- /dev/null +++ b/AltadefinizioneProvider/build.gradle.kts @@ -0,0 +1,25 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "it" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + // authors = listOf("Cloudburst") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "Movie", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=altadefinizione.tienda&sz=%size%" +} \ No newline at end of file diff --git a/AltadefinizioneProvider/src/main/AndroidManifest.xml b/AltadefinizioneProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/AltadefinizioneProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/AltadefinizioneProvider/src/main/kotlin/com/lagradost/AltadefinizioneProvider.kt b/AltadefinizioneProvider/src/main/kotlin/com/lagradost/AltadefinizioneProvider.kt new file mode 100644 index 0000000..6c7a92e --- /dev/null +++ b/AltadefinizioneProvider/src/main/kotlin/com/lagradost/AltadefinizioneProvider.kt @@ -0,0 +1,159 @@ +package com.lagradost + +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.loadExtractor +import com.lagradost.cloudstream3.utils.AppUtils.html + + +class AltadefinizioneProvider : MainAPI() { + override var lang = "it" + override var mainUrl = "https://altadefinizione.tienda" + override var name = "Altadefinizione" + override val hasMainPage = true + override val hasChromecastSupport = true + override val supportedTypes = setOf( + TvType.Movie + ) + + override val mainPage = mainPageOf( + Pair("$mainUrl/cerca/anno/2022/page/", "Ultimi Film"), + Pair("$mainUrl/cerca/openload-quality/HD/page/", "Film in HD"), + Pair("$mainUrl/cinema/page/", "Ora al cinema") + ) + + override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { + val url = request.data + page + + val soup = app.get(url).document + val home = soup.select("div.box").map { + val title = it.selectFirst("img")!!.attr("alt") + val link = it.selectFirst("a")!!.attr("href") + val image = mainUrl + it.selectFirst("img")!!.attr("src") + val quality = getQualityFromString(it.selectFirst("span")!!.text()) + + MovieSearchResponse( + title, + link, + this.name, + TvType.Movie, + image, + null, + null, + quality, + ) + } + return newHomePageResponse(request.name, home) + } + + override suspend fun search(query: String): List { + val doc = app.post( + "$mainUrl/index.php", data = mapOf( + "do" to "search", + "subaction" to "search", + "story" to query, + "sortby" to "news_read" + ) + ).document + return doc.select("div.box").map { + val title = it.selectFirst("img")!!.attr("alt") + val link = it.selectFirst("a")!!.attr("href") + val image = mainUrl + it.selectFirst("img")!!.attr("src") + val quality = getQualityFromString(it.selectFirst("span")!!.text()) + + MovieSearchResponse( + title, + link, + this.name, + TvType.Movie, + image, + null, + null, + quality, + ) + } + } + + override suspend fun load(url: String): LoadResponse { + val page = app.get(url) + val document = page.document + val title = document.selectFirst(" h1 > a")!!.text().replace("streaming", "") + val description = document.select("#sfull").toString().substringAfter("altadefinizione") + .substringBeforeLast("fonte trama").html().toString() + val rating = null + + val year = document.selectFirst("#details > li:nth-child(2)")!!.childNode(2).toString() + .filter { it.isDigit() }.toInt() + + val poster = fixUrl(document.selectFirst("div.thumbphoto > img")!!.attr("src")) + + val recomm = document.select("ul.related-list > li").map { + val href = it.selectFirst("a")!!.attr("href") + val posterUrl = mainUrl + it.selectFirst("img")!!.attr("src") + val name = it.selectFirst("img")!!.attr("alt") + MovieSearchResponse( + name, + href, + this.name, + TvType.Movie, + posterUrl, + null + ) + + } + + + val actors: List = + document.select("#staring > a").map { + ActorData(actor = Actor(it.text())) + } + + val tags: List = document.select("#details > li:nth-child(1) > a").map { it.text() } + + val trailerurl = document.selectFirst("#showtrailer > div > div > iframe")?.attr("src") + + return newMovieLoadResponse( + title, + url, + TvType.Movie, + url + ) { + posterUrl = fixUrlNull(poster) + this.year = year + this.plot = description + this.rating = rating + this.recommendations = recomm + this.duration = null + this.actors = actors + this.tags = tags + addTrailer(trailerurl) + } + } + + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + val doc = app.get(data).document + if (doc.select("div.guardahd-player").isNullOrEmpty()) { + val videoUrl = + doc.select("input").last { it.hasAttr("data-mirror") }.attr("value") + loadExtractor(videoUrl, data, subtitleCallback, callback) + doc.select("#mirrors > li > a").forEach { + loadExtractor(fixUrl(it.attr("data-target")), data, subtitleCallback, callback) + } + } else { + val pagelinks = doc.select("div.guardahd-player").select("iframe").attr("src") + val docLinks = app.get(pagelinks).document + docLinks.select("body > div > ul > li").forEach { + loadExtractor(fixUrl(it.attr("data-link")), data, subtitleCallback, callback) + } + } + + return true + } +} \ No newline at end of file diff --git a/AltadefinizioneProvider/src/main/kotlin/com/lagradost/AltadefinizioneProviderPlugin.kt b/AltadefinizioneProvider/src/main/kotlin/com/lagradost/AltadefinizioneProviderPlugin.kt new file mode 100644 index 0000000..1e9a49b --- /dev/null +++ b/AltadefinizioneProvider/src/main/kotlin/com/lagradost/AltadefinizioneProviderPlugin.kt @@ -0,0 +1,14 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class AltadefinizioneProviderPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(AltadefinizioneProvider()) + } +} \ No newline at end of file diff --git a/AniPlayProvider/build.gradle.kts b/AniPlayProvider/build.gradle.kts new file mode 100644 index 0000000..8929f13 --- /dev/null +++ b/AniPlayProvider/build.gradle.kts @@ -0,0 +1,28 @@ +// use an integer for version numbers +version = 2 + + +cloudstream { + language = "it" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + // authors = listOf("Cloudburst") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "AnimeMovie", + "Anime", + "OVA" + ) + + + iconUrl = "https://www.google.com/s2/favicons?domain=aniplay.it&sz=%size%" +} \ No newline at end of file diff --git a/AniPlayProvider/src/main/AndroidManifest.xml b/AniPlayProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/AniPlayProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/AniPlayProvider/src/main/kotlin/com/lagradost/AniPlayProvider.kt b/AniPlayProvider/src/main/kotlin/com/lagradost/AniPlayProvider.kt new file mode 100644 index 0000000..9c5b68b --- /dev/null +++ b/AniPlayProvider/src/main/kotlin/com/lagradost/AniPlayProvider.kt @@ -0,0 +1,216 @@ +package com.lagradost + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId +import com.lagradost.cloudstream3.LoadResponse.Companion.addDuration +import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId +import com.lagradost.cloudstream3.utils.AppUtils.parseJson +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.M3u8Helper +import com.lagradost.cloudstream3.utils.Qualities + +class AniPlayProvider : MainAPI() { + override var mainUrl = "https://aniplay.it" + override var name = "AniPlay" + override var lang = "it" + override val hasMainPage = true + private val dubIdentifier = " (ITA)" + + override val supportedTypes = setOf( + TvType.Anime, + TvType.AnimeMovie, + TvType.OVA + ) + + companion object { + fun getStatus(t: String?): ShowStatus? { + return when (t?.lowercase()) { + "completato" -> ShowStatus.Completed + "in corso" -> ShowStatus.Ongoing + else -> null // "annunciato" + } + } + fun getType(t: String?): TvType { + return when (t?.lowercase()) { + "ona" -> TvType.OVA + "movie" -> TvType.AnimeMovie + else -> TvType.Anime //"serie", "special" + } + } + } + + private fun isDub(title: String): Boolean{ + return title.contains(dubIdentifier) + } + + data class ApiPoster( + @JsonProperty("imageFull") val posterUrl: String + ) + + data class ApiMainPageAnime( + @JsonProperty("animeId") val id: Int, + @JsonProperty("episodeNumber") val episode: String?, + @JsonProperty("animeTitle") val title: String, + @JsonProperty("animeType") val type: String, + @JsonProperty("fullHd") val fullHD: Boolean, + @JsonProperty("animeVerticalImages") val posters: List + ) + + data class ApiSearchResult( + @JsonProperty("id") val id: Int, + @JsonProperty("title") val title: String, + @JsonProperty("status") val status: String, + @JsonProperty("type") val type: String, + @JsonProperty("verticalImages") val posters: List + ) + + data class ApiGenres( + @JsonProperty("description") val name: String + ) + data class ApiWebsite( + @JsonProperty("listWebsiteId") val websiteId: Int, + @JsonProperty("url") val url: String + ) + + data class ApiEpisode( + @JsonProperty("id") val id: Int, + @JsonProperty("title") val title: String?, + @JsonProperty("episodeNumber") val number: String, + ) + + private fun ApiEpisode.toEpisode() : Episode? { + val number = this.number.toIntOrNull() ?: return null + return Episode( + data = "$mainUrl/api/episode/${this.id}", + episode = number, + name = this.title + ) + } + + data class ApiSeason( + @JsonProperty("id") val id: Int, + @JsonProperty("name") val name: String + ) + + private suspend fun ApiSeason.toEpisodeList(url: String) : List { + return parseJson>(app.get("$url/season/${this.id}").text).mapNotNull { it.toEpisode() } + } + + data class ApiAnime( + @JsonProperty("title") val title: String, + @JsonProperty("alternativeTitle") val japTitle: String?, + @JsonProperty("episodeDuration") val duration: Int, + @JsonProperty("storyline") val plot: String, + @JsonProperty("type") val type: String, + @JsonProperty("status") val status: String, + @JsonProperty("genres") val genres: List, + @JsonProperty("verticalImages") val posters: List, + @JsonProperty("listWebsites") val websites: List, + @JsonProperty("episodes") val episodes: List, + @JsonProperty("seasons") val seasons: List? + ) + + data class ApiEpisodeUrl( + @JsonProperty("videoUrl") val url: String + ) + + override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { + val response = parseJson>(app.get("$mainUrl/api/home/latest-episodes?page=0").text) + + val results = response.map{ + val isDub = isDub(it.title) + newAnimeSearchResponse( + name = if (isDub) it.title.replace(dubIdentifier, "") else it.title, + url = "$mainUrl/api/anime/${it.id}", + type = getType(it.type), + ){ + addDubStatus(isDub, it.episode?.toIntOrNull()) + this.posterUrl = it.posters.first().posterUrl + this.quality = if (it.fullHD) SearchQuality.HD else null + } + } + return HomePageResponse(listOf(HomePageList("Ultime uscite",results))) + } + + override suspend fun search(query: String): List { + val response = parseJson>(app.get("$mainUrl/api/anime/advanced-search?page=0&size=36&query=$query").text) + + return response.map { + val isDub = isDub(it.title) + + newAnimeSearchResponse( + name = if (isDub) it.title.replace(dubIdentifier, "") else it.title, + url = "$mainUrl/api/anime/${it.id}", + type = getType(it.type), + ){ + addDubStatus(isDub) + this.posterUrl = it.posters.first().posterUrl + } + } + } + + override suspend fun load(url: String): LoadResponse { + + val response = parseJson(app.get(url).text) + + val tags: List = response.genres.map { it.name } + + val malId: Int? = response.websites.find { it.websiteId == 1 }?.url?.removePrefix("https://myanimelist.net/anime/")?.split("/")?.first()?.toIntOrNull() + val aniListId: Int? = response.websites.find { it.websiteId == 4 }?.url?.removePrefix("https://anilist.co/anime/")?.split("/")?.first()?.toIntOrNull() + + val episodes = if (response.seasons.isNullOrEmpty()) response.episodes.mapNotNull { it.toEpisode() } else response.seasons.map{ it.toEpisodeList(url) }.flatten() + val isDub = isDub(response.title) + + return newAnimeLoadResponse(response.title, url, getType(response.type)) { + this.name = if (isDub) response.title.replace(dubIdentifier, "") else response.title + this.japName = response.japTitle + this.plot = response.plot + this.tags = tags + this.showStatus = getStatus(response.status) + addPoster(response.posters.first().posterUrl) + addEpisodes(if (isDub) DubStatus.Dubbed else DubStatus.Subbed, episodes) + addMalId(malId) + addAniListId(aniListId) + addDuration(response.duration.toString()) + } + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + + val episode = parseJson(app.get(data).text) + + if(episode.url.contains(".m3u8")){ + val m3u8Helper = M3u8Helper() + val streams = m3u8Helper.m3u8Generation(M3u8Helper.M3u8Stream(episode.url,Qualities.Unknown.value), false) + + streams.forEach { + callback.invoke( + ExtractorLink( + name, + name, + it.streamUrl, + referer = mainUrl, + quality = it.quality ?: Qualities.Unknown.value, + isM3u8 = it.streamUrl.contains(".m3u8"))) } + return true + } + + callback.invoke( + ExtractorLink( + name, + name, + episode.url, + referer = mainUrl, + quality = Qualities.Unknown.value, + isM3u8 = false, + ) + ) + return true + } +} \ No newline at end of file diff --git a/AniPlayProvider/src/main/kotlin/com/lagradost/AniPlayProviderPlugin.kt b/AniPlayProvider/src/main/kotlin/com/lagradost/AniPlayProviderPlugin.kt new file mode 100644 index 0000000..de3dccb --- /dev/null +++ b/AniPlayProvider/src/main/kotlin/com/lagradost/AniPlayProviderPlugin.kt @@ -0,0 +1,14 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class AniPlayProviderPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(AniPlayProvider()) + } +} \ No newline at end of file diff --git a/AnimeIndoProvider/build.gradle.kts b/AnimeIndoProvider/build.gradle.kts new file mode 100644 index 0000000..6d96b54 --- /dev/null +++ b/AnimeIndoProvider/build.gradle.kts @@ -0,0 +1,27 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "id" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + // authors = listOf("Cloudburst") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "AnimeMovie", + "OVA", + "Anime", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=animeindo.sbs&sz=%size%" +} \ No newline at end of file diff --git a/AnimeIndoProvider/src/main/AndroidManifest.xml b/AnimeIndoProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/AnimeIndoProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/AnimeIndoProvider/src/main/kotlin/com/lagradost/AnimeIndoProvider.kt b/AnimeIndoProvider/src/main/kotlin/com/lagradost/AnimeIndoProvider.kt new file mode 100644 index 0000000..fb132b7 --- /dev/null +++ b/AnimeIndoProvider/src/main/kotlin/com/lagradost/AnimeIndoProvider.kt @@ -0,0 +1,192 @@ +package com.lagradost + +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.APIHolder.getCaptchaToken +import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.loadExtractor +import com.lagradost.nicehttp.NiceResponse +import org.jsoup.Jsoup +import org.jsoup.nodes.Element + +class AnimeIndoProvider : MainAPI() { + override var mainUrl = "https://animeindo.sbs" + override var name = "AnimeIndo" + override val hasMainPage = true + override var lang = "id" + override val hasDownloadSupport = true + + override val supportedTypes = setOf( + TvType.Anime, + TvType.AnimeMovie, + TvType.OVA + ) + + companion object { + fun getType(t: String): TvType { + return if (t.contains("OVA") || t.contains("Special")) TvType.OVA + else if (t.contains("Movie")) TvType.AnimeMovie + else TvType.Anime + } + + fun getStatus(t: String): ShowStatus { + return when (t) { + "Finished Airing" -> ShowStatus.Completed + "Currently Airing" -> ShowStatus.Ongoing + else -> ShowStatus.Completed + } + } + + private suspend fun request(url: String): NiceResponse { + val req = app.get( + url, + cookies = mapOf("recaptcha_cookie" to "#Asia/Jakarta#-420#win32#Windows#0,false,false#Google Inc. (Intel)~ANGLE (Intel, Intel(R) HD Graphics 400 Direct3D11 vs_5_0 ps_5_0)") + ) + if (req.isSuccessful) { + return req + } else { + val document = app.get(url).document + val captchaKey = + document.select("script[src*=https://www.google.com/recaptcha/api.js?render=]") + .attr("src").substringAfter("render=").substringBefore("&") + val token = getCaptchaToken(url, captchaKey) + return app.post( + url, + data = mapOf( + "action" to "recaptcha_for_all", + "token" to "$token", + "sitekey" to captchaKey + ) + ) + } + } + } + + override val mainPage = mainPageOf( + "$mainUrl/anime-terbaru/page/" to "Anime Terbaru", + "$mainUrl/donghua-terbaru/page/" to "Donghua Terbaru" + ) + + override suspend fun getMainPage( + page: Int, + request: MainPageRequest + ): HomePageResponse { + val document = request(request.data + page).document + val home = document.select("div.post-show > article").mapNotNull { + it.toSearchResult() + } + return newHomePageResponse(request.name, home) + } + + private fun getProperAnimeLink(uri: String): String { + return if (uri.contains("/anime/")) { + uri + } else { + var title = uri.substringAfter("$mainUrl/") + title = when { + (title.contains("-episode")) && !(title.contains("-movie")) -> Regex("(.+)-episode").find( + title + )?.groupValues?.get(1).toString() + (title.contains("-movie")) -> Regex("(.+)-movie").find(title)?.groupValues?.get( + 1 + ).toString() + else -> title + } + "$mainUrl/anime/$title" + } + } + + private fun Element.toSearchResult(): AnimeSearchResponse? { + val title = this.selectFirst("div.title")?.text()?.trim() ?: return null + val href = getProperAnimeLink(this.selectFirst("a")!!.attr("href")) + val posterUrl = this.select("img[itemprop=image]").attr("src").toString() + val type = getType(this.select("div.type").text().trim()) + val epNum = + this.selectFirst("span.episode")?.ownText()?.replace(Regex("[^0-9]"), "")?.trim() + ?.toIntOrNull() + return newAnimeSearchResponse(title, href, type) { + this.posterUrl = posterUrl + addSub(epNum) + } + + } + + override suspend fun search(query: String): List { + val link = "$mainUrl/?s=$query" + val document = request(link).document + + return document.select(".site-main.relat > article").map { + val title = it.selectFirst("div.title > h2")!!.ownText().trim() + val href = it.selectFirst("a")!!.attr("href") + val posterUrl = it.selectFirst("img")!!.attr("src").toString() + val type = getType(it.select("div.type").text().trim()) + newAnimeSearchResponse(title, href, type) { + this.posterUrl = posterUrl + } + } + } + + override suspend fun load(url: String): LoadResponse { + val document = request(url).document + + val title = document.selectFirst("h1.entry-title")?.text().toString().trim() + val poster = document.selectFirst("div.thumb > img[itemprop=image]")?.attr("src") + val tags = document.select("div.genxed > a").map { it.text() } + val type = getType( + document.selectFirst("div.info-content > div.spe > span:nth-child(6)")?.ownText() + .toString() + ) + val year = Regex("\\d, ([0-9]*)").find( + document.select("div.info-content > div.spe > span:nth-child(9) > time").text() + )?.groupValues?.get(1)?.toIntOrNull() + val status = getStatus( + document.selectFirst("div.info-content > div.spe > span:nth-child(1)")!!.ownText() + .trim() + ) + val description = document.select("div[itemprop=description] > p").text() + val trailer = document.selectFirst("div.player-embed iframe")?.attr("src") + val episodes = document.select("div.lstepsiode.listeps ul li").mapNotNull { + val header = it.selectFirst("span.lchx > a") ?: return@mapNotNull null + val name = header.text().trim() + val episode = header.text().trim().replace("Episode", "").trim().toIntOrNull() + val link = fixUrl(header.attr("href")) + Episode(link, name = name, episode = episode) + }.reversed() + + return newAnimeLoadResponse(title, url, type) { + engName = title + posterUrl = poster + this.year = year + addEpisodes(DubStatus.Subbed, episodes) + showStatus = status + plot = description + this.tags = tags + addTrailer(trailer) + } + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + + val document = request(data).document + document.select("div.itemleft > .mirror > option").mapNotNull { + fixUrl(Jsoup.parse(base64Decode(it.attr("value"))).select("iframe").attr("src")) + }.apmap { + if (it.startsWith("https://uservideo.xyz")) { + app.get(it, referer = "$mainUrl/").document.select("iframe").attr("src") + } else { + it + } + }.apmap { + loadExtractor(it, data, subtitleCallback, callback) + } + + return true + } + + +} \ No newline at end of file diff --git a/AnimeIndoProvider/src/main/kotlin/com/lagradost/AnimeIndoProviderPlugin.kt b/AnimeIndoProvider/src/main/kotlin/com/lagradost/AnimeIndoProviderPlugin.kt new file mode 100644 index 0000000..d1dab81 --- /dev/null +++ b/AnimeIndoProvider/src/main/kotlin/com/lagradost/AnimeIndoProviderPlugin.kt @@ -0,0 +1,14 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class AnimeIndoProviderPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(AnimeIndoProvider()) + } +} \ No newline at end of file diff --git a/AnimeSailProvider/build.gradle.kts b/AnimeSailProvider/build.gradle.kts new file mode 100644 index 0000000..7fcac9c --- /dev/null +++ b/AnimeSailProvider/build.gradle.kts @@ -0,0 +1,27 @@ +// use an integer for version numbers +version = 2 + + +cloudstream { + language = "id" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + authors = listOf("Hexated") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "AnimeMovie", + "Anime", + "OVA", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=111.90.143.42&sz=%size%" +} \ No newline at end of file diff --git a/AnimeSailProvider/src/main/AndroidManifest.xml b/AnimeSailProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/AnimeSailProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/AnimeSailProvider/src/main/kotlin/com/lagradost/AnimeSailProvider.kt b/AnimeSailProvider/src/main/kotlin/com/lagradost/AnimeSailProvider.kt new file mode 100644 index 0000000..f4018db --- /dev/null +++ b/AnimeSailProvider/src/main/kotlin/com/lagradost/AnimeSailProvider.kt @@ -0,0 +1,195 @@ +package com.lagradost + +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.mvvm.safeApiCall +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.Qualities +import com.lagradost.cloudstream3.utils.loadExtractor +import com.lagradost.nicehttp.NiceResponse +import org.jsoup.Jsoup +import org.jsoup.nodes.Element + +class AnimeSailProvider : MainAPI() { + override var mainUrl = "https://111.90.143.42" + override var name = "AnimeSail" + override val hasMainPage = true + override var lang = "id" + override val hasDownloadSupport = true + + override val supportedTypes = setOf( + TvType.Anime, + TvType.AnimeMovie, + TvType.OVA + ) + + companion object { + fun getType(t: String): TvType { + return if (t.contains("OVA") || t.contains("Special")) TvType.OVA + else if (t.contains("Movie")) TvType.AnimeMovie + else TvType.Anime + } + + fun getStatus(t: String): ShowStatus { + return when (t) { + "Completed" -> ShowStatus.Completed + "Ongoing" -> ShowStatus.Ongoing + else -> ShowStatus.Completed + } + } + } + + private suspend fun request(url: String, ref: String? = null): NiceResponse { + return app.get( + url, + headers = mapOf("Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"), + cookies = mapOf("_as_ipin_ct" to "ID"), + referer = ref + ) + } + + override val mainPage = mainPageOf( + "$mainUrl/page/" to "Episode Terbaru", + "$mainUrl/movie-terbaru/page/" to "Movie Terbaru", + "$mainUrl/genres/donghua/page/" to "Donghua" + ) + + override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { + val document = request(request.data + page).document + val home = document.select("article").map { + it.toSearchResult() + } + return newHomePageResponse(request.name, home) + } + + private fun getProperAnimeLink(uri: String): String { + return if (uri.contains("/anime/")) { + uri + } else { + var title = uri.substringAfter("$mainUrl/") + title = when { + (title.contains("-episode")) && !(title.contains("-movie")) -> title.substringBefore( + "-episode" + ) + (title.contains("-movie")) -> title.substringBefore("-movie") + else -> title + } + + "$mainUrl/anime/$title" + } + } + + private fun Element.toSearchResult(): AnimeSearchResponse { + val href = getProperAnimeLink(fixUrlNull(this.selectFirst("a")?.attr("href")).toString()) + val title = this.select(".tt > h2").text().trim() + val posterUrl = fixUrlNull(this.selectFirst("div.limit img")?.attr("src")) + val epNum = this.selectFirst(".tt > h2")?.text()?.let { + Regex("Episode\\s?([0-9]+)").find(it)?.groupValues?.getOrNull(1)?.toIntOrNull() + } + return newAnimeSearchResponse(title, href, TvType.Anime) { + this.posterUrl = posterUrl + addSub(epNum) + } + + } + + override suspend fun search(query: String): List { + val link = "$mainUrl/?s=$query" + val document = request(link).document + + return document.select("div.listupd article").map { + it.toSearchResult() + } + } + + override suspend fun load(url: String): LoadResponse { + val document = request(url).document + + val title = document.selectFirst("h1.entry-title")?.text().toString().trim() + val type = getType( + document.select("tbody th:contains(Tipe)").next().text() + ) + val episodes = document.select("ul.daftar > li").map { + val header = it.select("a").text().trim() + val name = + Regex("(Episode\\s?[0-9]+)").find(header)?.groupValues?.getOrNull(0) ?: header + val link = fixUrl(it.select("a").attr("href")) + Episode(link, name = name) + }.reversed() + + return newAnimeLoadResponse(title, url, type) { + posterUrl = document.selectFirst("div.entry-content > img")?.attr("src") + this.year = + document.select("tbody th:contains(Dirilis)").next().text().trim().toIntOrNull() + addEpisodes(DubStatus.Subbed, episodes) + showStatus = + getStatus(document.select("tbody th:contains(Status)").next().text().trim()) + plot = document.selectFirst("div.entry-content > p")?.text() + this.tags = + document.select("tbody th:contains(Genre)").next().select("a").map { it.text() } + } + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + + val document = request(data).document + + document.select(".mobius > .mirror > option").apmap { + safeApiCall { + val iframe = fixUrl( + Jsoup.parse(base64Decode(it.attr("data-em"))).select("iframe").attr("src") + ?: throw ErrorLoadingException("No iframe found") + ) + + when { + iframe.startsWith("$mainUrl/utils/player/arch/") || iframe.startsWith( + "$mainUrl/utils/player/race/" + ) -> request(iframe, ref = data).document.select("source").attr("src") + .let { link -> + val source = + when { + iframe.contains("/arch/") -> "Arch" + iframe.contains("/race/") -> "Race" + else -> this.name + } + val quality = + Regex("\\.([0-9]{3,4})\\.").find(link)?.groupValues?.get(1) + callback.invoke( + ExtractorLink( + source = source, + name = source, + url = link, + referer = mainUrl, + quality = quality?.toIntOrNull() ?: Qualities.Unknown.value + ) + ) + } +// skip for now +// iframe.startsWith("$mainUrl/utils/player/fichan/") -> "" +// iframe.startsWith("$mainUrl/utils/player/blogger/") -> "" + iframe.startsWith("https://aghanim.xyz/tools/redirect/") -> { + val link = "https://rasa-cintaku-semakin-berantai.xyz/v/${iframe.substringAfter("id=").substringBefore("&token")}" + loadExtractor(link, mainUrl, subtitleCallback, callback) + } + iframe.startsWith("$mainUrl/utils/player/framezilla/") || iframe.startsWith("https://uservideo.xyz") -> { + request(iframe, ref = data).document.select("iframe").attr("src") + .let { link -> + loadExtractor(fixUrl(link), mainUrl, subtitleCallback, callback) + } + } + else -> { + loadExtractor(iframe, mainUrl, subtitleCallback, callback) + } + } + } + } + + return true + } + + +} \ No newline at end of file diff --git a/AnimeSailProvider/src/main/kotlin/com/lagradost/AnimeSailProviderPlugin.kt b/AnimeSailProvider/src/main/kotlin/com/lagradost/AnimeSailProviderPlugin.kt new file mode 100644 index 0000000..6b2b8a9 --- /dev/null +++ b/AnimeSailProvider/src/main/kotlin/com/lagradost/AnimeSailProviderPlugin.kt @@ -0,0 +1,14 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class AnimeSailProviderPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(AnimeSailProvider()) + } +} \ No newline at end of file diff --git a/AnimeSaturnProvider/build.gradle.kts b/AnimeSaturnProvider/build.gradle.kts new file mode 100644 index 0000000..09242e2 --- /dev/null +++ b/AnimeSaturnProvider/build.gradle.kts @@ -0,0 +1,26 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "it" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + // authors = listOf("Cloudburst") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "Anime", + "AnimeMovie", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=www.animesaturn.cc&sz=%size%" +} \ No newline at end of file diff --git a/AnimeSaturnProvider/src/main/AndroidManifest.xml b/AnimeSaturnProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/AnimeSaturnProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/AnimeSaturnProvider/src/main/kotlin/com/lagradost/AnimeSaturnProvider.kt b/AnimeSaturnProvider/src/main/kotlin/com/lagradost/AnimeSaturnProvider.kt new file mode 100644 index 0000000..dadb2b5 --- /dev/null +++ b/AnimeSaturnProvider/src/main/kotlin/com/lagradost/AnimeSaturnProvider.kt @@ -0,0 +1,201 @@ +package com.lagradost + +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId +import com.lagradost.cloudstream3.LoadResponse.Companion.addDuration +import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId +import com.lagradost.cloudstream3.LoadResponse.Companion.addRating +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.Qualities +import org.jsoup.nodes.Element + +class AnimeSaturnProvider : MainAPI() { + override var mainUrl = "https://www.animesaturn.cc" + override var name = "AnimeSaturn" + override var lang = "it" + override val hasMainPage = true + + override val supportedTypes = setOf( + TvType.Anime, + TvType.AnimeMovie, + TvType.OVA + ) + + companion object { + fun getStatus(t: String?): ShowStatus? { + return when (t?.lowercase()) { + "finito" -> ShowStatus.Completed + "in corso" -> ShowStatus.Ongoing + else -> null + } + } + } + + private fun Element.toSearchResult(): AnimeSearchResponse { + + var title = this.select("a.badge-archivio").first()!!.text() + var isDubbed = false + + if (title.contains(" (ITA)")){ + title = title.replace(" (ITA)", "") + isDubbed = true + } + + val url = this.select("a.badge-archivio").first()!!.attr("href") + + val posterUrl = this.select("img.locandina-archivio[src]").first()!!.attr("src") + + return newAnimeSearchResponse(title, url, TvType.Anime) { + addDubStatus(isDubbed) + this.posterUrl = posterUrl + } + } + + private fun Element.toEpisode(): Episode? { + var episode = this.text().split(" ")[1] + if(episode.contains(".")) return null + if(episode.contains("-")) + episode = episode.split("-")[0] + + return Episode( + data = this.attr("href"), + episode = episode.toInt() + ) + + } + + override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { + val document = app.get(mainUrl).document + val list = ArrayList() + document.select("div.container:has(span.badge-saturn)").forEach { + val tabName = it.select("span.badge-saturn").first()!!.text() + if (tabName.equals("Ultimi episodi")) return@forEach + val results = ArrayList() + it.select(".main-anime-card").forEach { card -> + var title = card.select("a[title]").first()!!.attr("title") + var isDubbed = false + if(title.contains(" (ITA)")){ + title = title.replace(" (ITA)", "") + isDubbed = true + } + val posterUrl = card.select("img.new-anime").first()!!.attr("src") + val url = card.select("a").first()!!.attr("href") + + results.add(newAnimeSearchResponse(title, url, TvType.Anime){ + addDubStatus(isDubbed) + this.posterUrl = posterUrl + }) + } + list.add(HomePageList(tabName, results)) + } + return HomePageResponse(list) + } + + override suspend fun search(query: String): List { + val document = app.get("$mainUrl/animelist?search=$query").document + return document.select("div.item-archivio").map { + it.toSearchResult() + } + } + + override suspend fun load(url: String): LoadResponse { + + val document = app.get(url).document + + val title = document.select("img.cover-anime").first()!!.attr("alt") + val japTitle = document.select("div.box-trasparente-alternativo").first()!!.text() + val posterUrl = document.select("img.cover-anime[src]").first()!!.attr("src") + var malId : Int? = null + var aniListId : Int? = null + + document.select("[rel=\"noopener noreferrer\"]").forEach { + if(it.attr("href").contains("myanimelist")) + malId = it.attr("href").removeSuffix("/").split('/').last().toIntOrNull() + else + aniListId = it.attr("href").removeSuffix("/").split('/').last().toIntOrNull() + } + + val plot = document.select("div#shown-trama").first()?.text() + + val tags = document.select("a.generi-as").map { it.text() } + + val details : List? = document.select("div.container:contains(Stato: )").first()?.text()?.split(" ") + var status : String? = null + var duration : String? = null + var year : String? = null + var score : String? = null + + val isDubbed = document.select("div.anime-title-as").first()!!.text().contains("(ITA)") + + if (!details.isNullOrEmpty()) { + details.forEach { + val index = details.indexOf(it) +1 + when (it) { + "Stato:" -> status = details[index] + "episodi:" -> duration = details[index] + "uscita:" -> year = details[index + 2] + "Voto:" -> score = details[index].split("/")[0] + else -> return@forEach + } + } + } + + val episodes = document.select("a.bottone-ep").mapNotNull{ it.toEpisode() } + + return newAnimeLoadResponse(title, url, TvType.Anime) { + this.engName = title + this.japName = japTitle + this.year = year?.toIntOrNull() + this.plot = plot + this.tags = tags + this.showStatus = getStatus(status) + addPoster(posterUrl) + addRating(score) + addEpisodes(if (isDubbed) DubStatus.Dubbed else DubStatus.Subbed, episodes) + addMalId(malId) + addAniListId(aniListId) + addDuration(duration) + } + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + + val page = app.get(data).document + val episodeLink = page.select("div.card-body > a[href]").find {it1 -> + it1.attr("href").contains("watch?") + }?.attr("href") + + val episodePage = app.get(episodeLink!!).document + val episodeUrl: String? + var isM3U8 = false + + if(episodePage.select("video.afterglow > source").isNotEmpty()) //Old player + episodeUrl = episodePage.select("video.afterglow > source").first()!!.attr("src") + + else{ //New player + val script = episodePage.select("script").find { + it.toString().contains("jwplayer('player_hls').setup({") + }!!.toString() + episodeUrl = script.split(" ").find { it.contains(".m3u8") and !it.contains(".replace") }!!.replace("\"","").replace(",", "") + isM3U8 = true + } + + + callback.invoke( + ExtractorLink( + name, + name, + episodeUrl!!, + isM3u8 = isM3U8, + referer = "https://www.animesaturn.io/", //Some servers need the old host as referer, and the new ones accept it too + quality = Qualities.Unknown.value + ) + ) + return true + } +} diff --git a/AnimeSaturnProvider/src/main/kotlin/com/lagradost/AnimeSaturnProviderPlugin.kt b/AnimeSaturnProvider/src/main/kotlin/com/lagradost/AnimeSaturnProviderPlugin.kt new file mode 100644 index 0000000..048741d --- /dev/null +++ b/AnimeSaturnProvider/src/main/kotlin/com/lagradost/AnimeSaturnProviderPlugin.kt @@ -0,0 +1,14 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class AnimeSaturnProviderPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(AnimeSaturnProvider()) + } +} \ No newline at end of file diff --git a/AnimeWorldProvider/build.gradle.kts b/AnimeWorldProvider/build.gradle.kts new file mode 100644 index 0000000..35d5f26 --- /dev/null +++ b/AnimeWorldProvider/build.gradle.kts @@ -0,0 +1,27 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "it" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + // authors = listOf("Cloudburst") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "AnimeMovie", + "Anime", + "OVA", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=www.animeworld.tv&sz=%size%" +} \ No newline at end of file diff --git a/AnimeWorldProvider/src/main/AndroidManifest.xml b/AnimeWorldProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/AnimeWorldProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/AnimeWorldProvider/src/main/kotlin/com/lagradost/AnimeWorldProvider.kt b/AnimeWorldProvider/src/main/kotlin/com/lagradost/AnimeWorldProvider.kt new file mode 100644 index 0000000..3d454cb --- /dev/null +++ b/AnimeWorldProvider/src/main/kotlin/com/lagradost/AnimeWorldProvider.kt @@ -0,0 +1,265 @@ +package com.lagradost + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId +import com.lagradost.cloudstream3.LoadResponse.Companion.addDuration +import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId +import com.lagradost.cloudstream3.LoadResponse.Companion.addRating +import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.Qualities +import com.lagradost.nicehttp.NiceResponse +import org.jsoup.nodes.Element +import org.mozilla.javascript.ConsString +import org.mozilla.javascript.Context +import org.mozilla.javascript.Scriptable + +class AnimeWorldProvider : MainAPI() { + override var mainUrl = "https://www.animeworld.tv" + override var name = "AnimeWorld" + override var lang = "it" + override val hasMainPage = true + + override val supportedTypes = setOf( + TvType.Anime, + TvType.AnimeMovie, + TvType.OVA + ) + + companion object { + private var cookies = emptyMap() + + // Disabled authentication as site did + private suspend fun request(url: String): NiceResponse { +// if (cookies.isEmpty()) { +// cookies = getCookies(url) +// } + return app.get(url +// , cookies = cookies + ) + } + + private suspend fun getCookies(url: String): Map { + val rhino = Context.enter() + rhino.optimizationLevel = -1 + val scope: Scriptable = rhino.initSafeStandardObjects() + + val slowAes = app.get("https://www.animeworld.tv/aes.min.js").text +// val slowAes = """ +// var _0x2465=["\x72\x6F\x74\x61\x74\x65","\x73\x62\x6F\x78","\x52\x63\x6F\x6E","\x6E\x75\x6D\x62\x65\x72\x4F\x66\x52\x6F\x75\x6E\x64\x73","\x63\x6F\x72\x65","\x53\x49\x5A\x45\x5F\x32\x35\x36","\x6B\x65\x79\x53\x69\x7A\x65","\x72\x73\x62\x6F\x78","\x73\x68\x69\x66\x74\x52\x6F\x77","\x6D\x69\x78\x43\x6F\x6C\x75\x6D\x6E","\x67\x61\x6C\x6F\x69\x73\x5F\x6D\x75\x6C\x74\x69\x70\x6C\x69\x63\x61\x74\x69\x6F\x6E","\x73\x75\x62\x42\x79\x74\x65\x73","\x73\x68\x69\x66\x74\x52\x6F\x77\x73","\x6D\x69\x78\x43\x6F\x6C\x75\x6D\x6E\x73","\x61\x64\x64\x52\x6F\x75\x6E\x64\x4B\x65\x79","\x63\x72\x65\x61\x74\x65\x52\x6F\x75\x6E\x64\x4B\x65\x79","\x72\x6F\x75\x6E\x64","\x69\x6E\x76\x52\x6F\x75\x6E\x64","\x53\x49\x5A\x45\x5F\x31\x32\x38","\x53\x49\x5A\x45\x5F\x31\x39\x32","\x65\x78\x70\x61\x6E\x64\x4B\x65\x79","\x6D\x61\x69\x6E","\x69\x6E\x76\x4D\x61\x69\x6E","\x73\x6C\x69\x63\x65","\x6C\x65\x6E\x67\x74\x68","\x69\x76\x20\x6C\x65\x6E\x67\x74\x68\x20\x6D\x75\x73\x74\x20\x62\x65\x20\x31\x32\x38\x20\x62\x69\x74\x73\x2E","\x43\x42\x43","\x6D\x6F\x64\x65\x4F\x66\x4F\x70\x65\x72\x61\x74\x69\x6F\x6E","\x70\x61\x64\x42\x79\x74\x65\x73\x49\x6E","\x63\x65\x69\x6C","\x67\x65\x74\x42\x6C\x6F\x63\x6B","\x43\x46\x42","\x65\x6E\x63\x72\x79\x70\x74","\x61\x65\x73","\x70\x75\x73\x68","\x4F\x46\x42","\x64\x65\x63\x72\x79\x70\x74","\x75\x6E\x70\x61\x64\x42\x79\x74\x65\x73\x4F\x75\x74","\x73\x70\x6C\x69\x63\x65"];var slowAES={aes:{keySize:{SIZE_128:16,SIZE_192:24,SIZE_256:32},sbox:[0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16],rsbox:[0x52,0x09,0x6a,0xd5,0x30,0x36,0xa5,0x38,0xbf,0x40,0xa3,0x9e,0x81,0xf3,0xd7,0xfb,0x7c,0xe3,0x39,0x82,0x9b,0x2f,0xff,0x87,0x34,0x8e,0x43,0x44,0xc4,0xde,0xe9,0xcb,0x54,0x7b,0x94,0x32,0xa6,0xc2,0x23,0x3d,0xee,0x4c,0x95,0x0b,0x42,0xfa,0xc3,0x4e,0x08,0x2e,0xa1,0x66,0x28,0xd9,0x24,0xb2,0x76,0x5b,0xa2,0x49,0x6d,0x8b,0xd1,0x25,0x72,0xf8,0xf6,0x64,0x86,0x68,0x98,0x16,0xd4,0xa4,0x5c,0xcc,0x5d,0x65,0xb6,0x92,0x6c,0x70,0x48,0x50,0xfd,0xed,0xb9,0xda,0x5e,0x15,0x46,0x57,0xa7,0x8d,0x9d,0x84,0x90,0xd8,0xab,0x00,0x8c,0xbc,0xd3,0x0a,0xf7,0xe4,0x58,0x05,0xb8,0xb3,0x45,0x06,0xd0,0x2c,0x1e,0x8f,0xca,0x3f,0x0f,0x02,0xc1,0xaf,0xbd,0x03,0x01,0x13,0x8a,0x6b,0x3a,0x91,0x11,0x41,0x4f,0x67,0xdc,0xea,0x97,0xf2,0xcf,0xce,0xf0,0xb4,0xe6,0x73,0x96,0xac,0x74,0x22,0xe7,0xad,0x35,0x85,0xe2,0xf9,0x37,0xe8,0x1c,0x75,0xdf,0x6e,0x47,0xf1,0x1a,0x71,0x1d,0x29,0xc5,0x89,0x6f,0xb7,0x62,0x0e,0xaa,0x18,0xbe,0x1b,0xfc,0x56,0x3e,0x4b,0xc6,0xd2,0x79,0x20,0x9a,0xdb,0xc0,0xfe,0x78,0xcd,0x5a,0xf4,0x1f,0xdd,0xa8,0x33,0x88,0x07,0xc7,0x31,0xb1,0x12,0x10,0x59,0x27,0x80,0xec,0x5f,0x60,0x51,0x7f,0xa9,0x19,0xb5,0x4a,0x0d,0x2d,0xe5,0x7a,0x9f,0x93,0xc9,0x9c,0xef,0xa0,0xe0,0x3b,0x4d,0xae,0x2a,0xf5,0xb0,0xc8,0xeb,0xbb,0x3c,0x83,0x53,0x99,0x61,0x17,0x2b,0x04,0x7e,0xba,0x77,0xd6,0x26,0xe1,0x69,0x14,0x63,0x55,0x21,0x0c,0x7d],rotate:function(_0x85f6x2){var _0x85f6x3=_0x85f6x2[0];for(var _0x85f6x4=0;_0x85f6x4< 3;_0x85f6x4++){_0x85f6x2[_0x85f6x4]= _0x85f6x2[_0x85f6x4+ 1]};_0x85f6x2[3]= _0x85f6x3;return _0x85f6x2},Rcon:[0x8d,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36,0x6c,0xd8,0xab,0x4d,0x9a,0x2f,0x5e,0xbc,0x63,0xc6,0x97,0x35,0x6a,0xd4,0xb3,0x7d,0xfa,0xef,0xc5,0x91,0x39,0x72,0xe4,0xd3,0xbd,0x61,0xc2,0x9f,0x25,0x4a,0x94,0x33,0x66,0xcc,0x83,0x1d,0x3a,0x74,0xe8,0xcb,0x8d,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36,0x6c,0xd8,0xab,0x4d,0x9a,0x2f,0x5e,0xbc,0x63,0xc6,0x97,0x35,0x6a,0xd4,0xb3,0x7d,0xfa,0xef,0xc5,0x91,0x39,0x72,0xe4,0xd3,0xbd,0x61,0xc2,0x9f,0x25,0x4a,0x94,0x33,0x66,0xcc,0x83,0x1d,0x3a,0x74,0xe8,0xcb,0x8d,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36,0x6c,0xd8,0xab,0x4d,0x9a,0x2f,0x5e,0xbc,0x63,0xc6,0x97,0x35,0x6a,0xd4,0xb3,0x7d,0xfa,0xef,0xc5,0x91,0x39,0x72,0xe4,0xd3,0xbd,0x61,0xc2,0x9f,0x25,0x4a,0x94,0x33,0x66,0xcc,0x83,0x1d,0x3a,0x74,0xe8,0xcb,0x8d,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36,0x6c,0xd8,0xab,0x4d,0x9a,0x2f,0x5e,0xbc,0x63,0xc6,0x97,0x35,0x6a,0xd4,0xb3,0x7d,0xfa,0xef,0xc5,0x91,0x39,0x72,0xe4,0xd3,0xbd,0x61,0xc2,0x9f,0x25,0x4a,0x94,0x33,0x66,0xcc,0x83,0x1d,0x3a,0x74,0xe8,0xcb,0x8d,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36,0x6c,0xd8,0xab,0x4d,0x9a,0x2f,0x5e,0xbc,0x63,0xc6,0x97,0x35,0x6a,0xd4,0xb3,0x7d,0xfa,0xef,0xc5,0x91,0x39,0x72,0xe4,0xd3,0xbd,0x61,0xc2,0x9f,0x25,0x4a,0x94,0x33,0x66,0xcc,0x83,0x1d,0x3a,0x74,0xe8,0xcb],G2X:[0x00,0x02,0x04,0x06,0x08,0x0a,0x0c,0x0e,0x10,0x12,0x14,0x16,0x18,0x1a,0x1c,0x1e,0x20,0x22,0x24,0x26,0x28,0x2a,0x2c,0x2e,0x30,0x32,0x34,0x36,0x38,0x3a,0x3c,0x3e,0x40,0x42,0x44,0x46,0x48,0x4a,0x4c,0x4e,0x50,0x52,0x54,0x56,0x58,0x5a,0x5c,0x5e,0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6e,0x70,0x72,0x74,0x76,0x78,0x7a,0x7c,0x7e,0x80,0x82,0x84,0x86,0x88,0x8a,0x8c,0x8e,0x90,0x92,0x94,0x96,0x98,0x9a,0x9c,0x9e,0xa0,0xa2,0xa4,0xa6,0xa8,0xaa,0xac,0xae,0xb0,0xb2,0xb4,0xb6,0xb8,0xba,0xbc,0xbe,0xc0,0xc2,0xc4,0xc6,0xc8,0xca,0xcc,0xce,0xd0,0xd2,0xd4,0xd6,0xd8,0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xee,0xf0,0xf2,0xf4,0xf6,0xf8,0xfa,0xfc,0xfe,0x1b,0x19,0x1f,0x1d,0x13,0x11,0x17,0x15,0x0b,0x09,0x0f,0x0d,0x03,0x01,0x07,0x05,0x3b,0x39,0x3f,0x3d,0x33,0x31,0x37,0x35,0x2b,0x29,0x2f,0x2d,0x23,0x21,0x27,0x25,0x5b,0x59,0x5f,0x5d,0x53,0x51,0x57,0x55,0x4b,0x49,0x4f,0x4d,0x43,0x41,0x47,0x45,0x7b,0x79,0x7f,0x7d,0x73,0x71,0x77,0x75,0x6b,0x69,0x6f,0x6d,0x63,0x61,0x67,0x65,0x9b,0x99,0x9f,0x9d,0x93,0x91,0x97,0x95,0x8b,0x89,0x8f,0x8d,0x83,0x81,0x87,0x85,0xbb,0xb9,0xbf,0xbd,0xb3,0xb1,0xb7,0xb5,0xab,0xa9,0xaf,0xad,0xa3,0xa1,0xa7,0xa5,0xdb,0xd9,0xdf,0xdd,0xd3,0xd1,0xd7,0xd5,0xcb,0xc9,0xcf,0xcd,0xc3,0xc1,0xc7,0xc5,0xfb,0xf9,0xff,0xfd,0xf3,0xf1,0xf7,0xf5,0xeb,0xe9,0xef,0xed,0xe3,0xe1,0xe7,0xe5],G3X:[0x00,0x03,0x06,0x05,0x0c,0x0f,0x0a,0x09,0x18,0x1b,0x1e,0x1d,0x14,0x17,0x12,0x11,0x30,0x33,0x36,0x35,0x3c,0x3f,0x3a,0x39,0x28,0x2b,0x2e,0x2d,0x24,0x27,0x22,0x21,0x60,0x63,0x66,0x65,0x6c,0x6f,0x6a,0x69,0x78,0x7b,0x7e,0x7d,0x74,0x77,0x72,0x71,0x50,0x53,0x56,0x55,0x5c,0x5f,0x5a,0x59,0x48,0x4b,0x4e,0x4d,0x44,0x47,0x42,0x41,0xc0,0xc3,0xc6,0xc5,0xcc,0xcf,0xca,0xc9,0xd8,0xdb,0xde,0xdd,0xd4,0xd7,0xd2,0xd1,0xf0,0xf3,0xf6,0xf5,0xfc,0xff,0xfa,0xf9,0xe8,0xeb,0xee,0xed,0xe4,0xe7,0xe2,0xe1,0xa0,0xa3,0xa6,0xa5,0xac,0xaf,0xaa,0xa9,0xb8,0xbb,0xbe,0xbd,0xb4,0xb7,0xb2,0xb1,0x90,0x93,0x96,0x95,0x9c,0x9f,0x9a,0x99,0x88,0x8b,0x8e,0x8d,0x84,0x87,0x82,0x81,0x9b,0x98,0x9d,0x9e,0x97,0x94,0x91,0x92,0x83,0x80,0x85,0x86,0x8f,0x8c,0x89,0x8a,0xab,0xa8,0xad,0xae,0xa7,0xa4,0xa1,0xa2,0xb3,0xb0,0xb5,0xb6,0xbf,0xbc,0xb9,0xba,0xfb,0xf8,0xfd,0xfe,0xf7,0xf4,0xf1,0xf2,0xe3,0xe0,0xe5,0xe6,0xef,0xec,0xe9,0xea,0xcb,0xc8,0xcd,0xce,0xc7,0xc4,0xc1,0xc2,0xd3,0xd0,0xd5,0xd6,0xdf,0xdc,0xd9,0xda,0x5b,0x58,0x5d,0x5e,0x57,0x54,0x51,0x52,0x43,0x40,0x45,0x46,0x4f,0x4c,0x49,0x4a,0x6b,0x68,0x6d,0x6e,0x67,0x64,0x61,0x62,0x73,0x70,0x75,0x76,0x7f,0x7c,0x79,0x7a,0x3b,0x38,0x3d,0x3e,0x37,0x34,0x31,0x32,0x23,0x20,0x25,0x26,0x2f,0x2c,0x29,0x2a,0x0b,0x08,0x0d,0x0e,0x07,0x04,0x01,0x02,0x13,0x10,0x15,0x16,0x1f,0x1c,0x19,0x1a],G9X:[0x00,0x09,0x12,0x1b,0x24,0x2d,0x36,0x3f,0x48,0x41,0x5a,0x53,0x6c,0x65,0x7e,0x77,0x90,0x99,0x82,0x8b,0xb4,0xbd,0xa6,0xaf,0xd8,0xd1,0xca,0xc3,0xfc,0xf5,0xee,0xe7,0x3b,0x32,0x29,0x20,0x1f,0x16,0x0d,0x04,0x73,0x7a,0x61,0x68,0x57,0x5e,0x45,0x4c,0xab,0xa2,0xb9,0xb0,0x8f,0x86,0x9d,0x94,0xe3,0xea,0xf1,0xf8,0xc7,0xce,0xd5,0xdc,0x76,0x7f,0x64,0x6d,0x52,0x5b,0x40,0x49,0x3e,0x37,0x2c,0x25,0x1a,0x13,0x08,0x01,0xe6,0xef,0xf4,0xfd,0xc2,0xcb,0xd0,0xd9,0xae,0xa7,0xbc,0xb5,0x8a,0x83,0x98,0x91,0x4d,0x44,0x5f,0x56,0x69,0x60,0x7b,0x72,0x05,0x0c,0x17,0x1e,0x21,0x28,0x33,0x3a,0xdd,0xd4,0xcf,0xc6,0xf9,0xf0,0xeb,0xe2,0x95,0x9c,0x87,0x8e,0xb1,0xb8,0xa3,0xaa,0xec,0xe5,0xfe,0xf7,0xc8,0xc1,0xda,0xd3,0xa4,0xad,0xb6,0xbf,0x80,0x89,0x92,0x9b,0x7c,0x75,0x6e,0x67,0x58,0x51,0x4a,0x43,0x34,0x3d,0x26,0x2f,0x10,0x19,0x02,0x0b,0xd7,0xde,0xc5,0xcc,0xf3,0xfa,0xe1,0xe8,0x9f,0x96,0x8d,0x84,0xbb,0xb2,0xa9,0xa0,0x47,0x4e,0x55,0x5c,0x63,0x6a,0x71,0x78,0x0f,0x06,0x1d,0x14,0x2b,0x22,0x39,0x30,0x9a,0x93,0x88,0x81,0xbe,0xb7,0xac,0xa5,0xd2,0xdb,0xc0,0xc9,0xf6,0xff,0xe4,0xed,0x0a,0x03,0x18,0x11,0x2e,0x27,0x3c,0x35,0x42,0x4b,0x50,0x59,0x66,0x6f,0x74,0x7d,0xa1,0xa8,0xb3,0xba,0x85,0x8c,0x97,0x9e,0xe9,0xe0,0xfb,0xf2,0xcd,0xc4,0xdf,0xd6,0x31,0x38,0x23,0x2a,0x15,0x1c,0x07,0x0e,0x79,0x70,0x6b,0x62,0x5d,0x54,0x4f,0x46],GBX:[0x00,0x0b,0x16,0x1d,0x2c,0x27,0x3a,0x31,0x58,0x53,0x4e,0x45,0x74,0x7f,0x62,0x69,0xb0,0xbb,0xa6,0xad,0x9c,0x97,0x8a,0x81,0xe8,0xe3,0xfe,0xf5,0xc4,0xcf,0xd2,0xd9,0x7b,0x70,0x6d,0x66,0x57,0x5c,0x41,0x4a,0x23,0x28,0x35,0x3e,0x0f,0x04,0x19,0x12,0xcb,0xc0,0xdd,0xd6,0xe7,0xec,0xf1,0xfa,0x93,0x98,0x85,0x8e,0xbf,0xb4,0xa9,0xa2,0xf6,0xfd,0xe0,0xeb,0xda,0xd1,0xcc,0xc7,0xae,0xa5,0xb8,0xb3,0x82,0x89,0x94,0x9f,0x46,0x4d,0x50,0x5b,0x6a,0x61,0x7c,0x77,0x1e,0x15,0x08,0x03,0x32,0x39,0x24,0x2f,0x8d,0x86,0x9b,0x90,0xa1,0xaa,0xb7,0xbc,0xd5,0xde,0xc3,0xc8,0xf9,0xf2,0xef,0xe4,0x3d,0x36,0x2b,0x20,0x11,0x1a,0x07,0x0c,0x65,0x6e,0x73,0x78,0x49,0x42,0x5f,0x54,0xf7,0xfc,0xe1,0xea,0xdb,0xd0,0xcd,0xc6,0xaf,0xa4,0xb9,0xb2,0x83,0x88,0x95,0x9e,0x47,0x4c,0x51,0x5a,0x6b,0x60,0x7d,0x76,0x1f,0x14,0x09,0x02,0x33,0x38,0x25,0x2e,0x8c,0x87,0x9a,0x91,0xa0,0xab,0xb6,0xbd,0xd4,0xdf,0xc2,0xc9,0xf8,0xf3,0xee,0xe5,0x3c,0x37,0x2a,0x21,0x10,0x1b,0x06,0x0d,0x64,0x6f,0x72,0x79,0x48,0x43,0x5e,0x55,0x01,0x0a,0x17,0x1c,0x2d,0x26,0x3b,0x30,0x59,0x52,0x4f,0x44,0x75,0x7e,0x63,0x68,0xb1,0xba,0xa7,0xac,0x9d,0x96,0x8b,0x80,0xe9,0xe2,0xff,0xf4,0xc5,0xce,0xd3,0xd8,0x7a,0x71,0x6c,0x67,0x56,0x5d,0x40,0x4b,0x22,0x29,0x34,0x3f,0x0e,0x05,0x18,0x13,0xca,0xc1,0xdc,0xd7,0xe6,0xed,0xf0,0xfb,0x92,0x99,0x84,0x8f,0xbe,0xb5,0xa8,0xa3],GDX:[0x00,0x0d,0x1a,0x17,0x34,0x39,0x2e,0x23,0x68,0x65,0x72,0x7f,0x5c,0x51,0x46,0x4b,0xd0,0xdd,0xca,0xc7,0xe4,0xe9,0xfe,0xf3,0xb8,0xb5,0xa2,0xaf,0x8c,0x81,0x96,0x9b,0xbb,0xb6,0xa1,0xac,0x8f,0x82,0x95,0x98,0xd3,0xde,0xc9,0xc4,0xe7,0xea,0xfd,0xf0,0x6b,0x66,0x71,0x7c,0x5f,0x52,0x45,0x48,0x03,0x0e,0x19,0x14,0x37,0x3a,0x2d,0x20,0x6d,0x60,0x77,0x7a,0x59,0x54,0x43,0x4e,0x05,0x08,0x1f,0x12,0x31,0x3c,0x2b,0x26,0xbd,0xb0,0xa7,0xaa,0x89,0x84,0x93,0x9e,0xd5,0xd8,0xcf,0xc2,0xe1,0xec,0xfb,0xf6,0xd6,0xdb,0xcc,0xc1,0xe2,0xef,0xf8,0xf5,0xbe,0xb3,0xa4,0xa9,0x8a,0x87,0x90,0x9d,0x06,0x0b,0x1c,0x11,0x32,0x3f,0x28,0x25,0x6e,0x63,0x74,0x79,0x5a,0x57,0x40,0x4d,0xda,0xd7,0xc0,0xcd,0xee,0xe3,0xf4,0xf9,0xb2,0xbf,0xa8,0xa5,0x86,0x8b,0x9c,0x91,0x0a,0x07,0x10,0x1d,0x3e,0x33,0x24,0x29,0x62,0x6f,0x78,0x75,0x56,0x5b,0x4c,0x41,0x61,0x6c,0x7b,0x76,0x55,0x58,0x4f,0x42,0x09,0x04,0x13,0x1e,0x3d,0x30,0x27,0x2a,0xb1,0xbc,0xab,0xa6,0x85,0x88,0x9f,0x92,0xd9,0xd4,0xc3,0xce,0xed,0xe0,0xf7,0xfa,0xb7,0xba,0xad,0xa0,0x83,0x8e,0x99,0x94,0xdf,0xd2,0xc5,0xc8,0xeb,0xe6,0xf1,0xfc,0x67,0x6a,0x7d,0x70,0x53,0x5e,0x49,0x44,0x0f,0x02,0x15,0x18,0x3b,0x36,0x21,0x2c,0x0c,0x01,0x16,0x1b,0x38,0x35,0x22,0x2f,0x64,0x69,0x7e,0x73,0x50,0x5d,0x4a,0x47,0xdc,0xd1,0xc6,0xcb,0xe8,0xe5,0xf2,0xff,0xb4,0xb9,0xae,0xa3,0x80,0x8d,0x9a,0x97],GEX:[0x00,0x0e,0x1c,0x12,0x38,0x36,0x24,0x2a,0x70,0x7e,0x6c,0x62,0x48,0x46,0x54,0x5a,0xe0,0xee,0xfc,0xf2,0xd8,0xd6,0xc4,0xca,0x90,0x9e,0x8c,0x82,0xa8,0xa6,0xb4,0xba,0xdb,0xd5,0xc7,0xc9,0xe3,0xed,0xff,0xf1,0xab,0xa5,0xb7,0xb9,0x93,0x9d,0x8f,0x81,0x3b,0x35,0x27,0x29,0x03,0x0d,0x1f,0x11,0x4b,0x45,0x57,0x59,0x73,0x7d,0x6f,0x61,0xad,0xa3,0xb1,0xbf,0x95,0x9b,0x89,0x87,0xdd,0xd3,0xc1,0xcf,0xe5,0xeb,0xf9,0xf7,0x4d,0x43,0x51,0x5f,0x75,0x7b,0x69,0x67,0x3d,0x33,0x21,0x2f,0x05,0x0b,0x19,0x17,0x76,0x78,0x6a,0x64,0x4e,0x40,0x52,0x5c,0x06,0x08,0x1a,0x14,0x3e,0x30,0x22,0x2c,0x96,0x98,0x8a,0x84,0xae,0xa0,0xb2,0xbc,0xe6,0xe8,0xfa,0xf4,0xde,0xd0,0xc2,0xcc,0x41,0x4f,0x5d,0x53,0x79,0x77,0x65,0x6b,0x31,0x3f,0x2d,0x23,0x09,0x07,0x15,0x1b,0xa1,0xaf,0xbd,0xb3,0x99,0x97,0x85,0x8b,0xd1,0xdf,0xcd,0xc3,0xe9,0xe7,0xf5,0xfb,0x9a,0x94,0x86,0x88,0xa2,0xac,0xbe,0xb0,0xea,0xe4,0xf6,0xf8,0xd2,0xdc,0xce,0xc0,0x7a,0x74,0x66,0x68,0x42,0x4c,0x5e,0x50,0x0a,0x04,0x16,0x18,0x32,0x3c,0x2e,0x20,0xec,0xe2,0xf0,0xfe,0xd4,0xda,0xc8,0xc6,0x9c,0x92,0x80,0x8e,0xa4,0xaa,0xb8,0xb6,0x0c,0x02,0x10,0x1e,0x34,0x3a,0x28,0x26,0x7c,0x72,0x60,0x6e,0x44,0x4a,0x58,0x56,0x37,0x39,0x2b,0x25,0x0f,0x01,0x13,0x1d,0x47,0x49,0x5b,0x55,0x7f,0x71,0x63,0x6d,0xd7,0xd9,0xcb,0xc5,0xef,0xe1,0xf3,0xfd,0xa7,0xa9,0xbb,0xb5,0x9f,0x91,0x83,0x8d],core:function(_0x85f6x2,_0x85f6x5){_0x85f6x2= this[_0x2465[0]](_0x85f6x2);for(var _0x85f6x4=0;_0x85f6x4< 4;++_0x85f6x4){_0x85f6x2[_0x85f6x4]= this[_0x2465[1]][_0x85f6x2[_0x85f6x4]]};_0x85f6x2[0]= _0x85f6x2[0]^ this[_0x2465[2]][_0x85f6x5];return _0x85f6x2},expandKey:function(_0x85f6x6,_0x85f6x7){var _0x85f6x8=(16* (this[_0x2465[3]](_0x85f6x7)+ 1));var _0x85f6x9=0;var _0x85f6xa=1;var _0x85f6xb=[];var _0x85f6xc=[];for(var _0x85f6x4=0;_0x85f6x4< _0x85f6x8;_0x85f6x4++){_0x85f6xc[_0x85f6x4]= 0};for(var _0x85f6xd=0;_0x85f6xd< _0x85f6x7;_0x85f6xd++){_0x85f6xc[_0x85f6xd]= _0x85f6x6[_0x85f6xd]};_0x85f6x9+= _0x85f6x7;while(_0x85f6x9< _0x85f6x8){for(var _0x85f6xe=0;_0x85f6xe< 4;_0x85f6xe++){_0x85f6xb[_0x85f6xe]= _0x85f6xc[(_0x85f6x9- 4)+ _0x85f6xe]};if(_0x85f6x9% _0x85f6x7== 0){_0x85f6xb= this[_0x2465[4]](_0x85f6xb,_0x85f6xa++)};if(_0x85f6x7== this[_0x2465[6]][_0x2465[5]]&& ((_0x85f6x9% _0x85f6x7)== 16)){for(var _0x85f6xf=0;_0x85f6xf< 4;_0x85f6xf++){_0x85f6xb[_0x85f6xf]= this[_0x2465[1]][_0x85f6xb[_0x85f6xf]]}};for(var _0x85f6x10=0;_0x85f6x10< 4;_0x85f6x10++){_0x85f6xc[_0x85f6x9]= _0x85f6xc[_0x85f6x9- _0x85f6x7]^ _0x85f6xb[_0x85f6x10];_0x85f6x9++}};return _0x85f6xc},addRoundKey:function(_0x85f6x11,_0x85f6x12){for(var _0x85f6x4=0;_0x85f6x4< 16;_0x85f6x4++){_0x85f6x11[_0x85f6x4]^= _0x85f6x12[_0x85f6x4]};return _0x85f6x11},createRoundKey:function(_0x85f6xc,_0x85f6x13){var _0x85f6x12=[];for(var _0x85f6x4=0;_0x85f6x4< 4;_0x85f6x4++){for(var _0x85f6xd=0;_0x85f6xd< 4;_0x85f6xd++){_0x85f6x12[_0x85f6xd* 4+ _0x85f6x4]= _0x85f6xc[_0x85f6x13+ _0x85f6x4* 4+ _0x85f6xd]}};return _0x85f6x12},subBytes:function(_0x85f6x11,_0x85f6x14){for(var _0x85f6x4=0;_0x85f6x4< 16;_0x85f6x4++){_0x85f6x11[_0x85f6x4]= _0x85f6x14?this[_0x2465[7]][_0x85f6x11[_0x85f6x4]]:this[_0x2465[1]][_0x85f6x11[_0x85f6x4]]};return _0x85f6x11},shiftRows:function(_0x85f6x11,_0x85f6x14){for(var _0x85f6x4=0;_0x85f6x4< 4;_0x85f6x4++){_0x85f6x11= this[_0x2465[8]](_0x85f6x11,_0x85f6x4* 4,_0x85f6x4,_0x85f6x14)};return _0x85f6x11},shiftRow:function(_0x85f6x11,_0x85f6x15,_0x85f6x16,_0x85f6x14){for(var _0x85f6x4=0;_0x85f6x4< _0x85f6x16;_0x85f6x4++){if(_0x85f6x14){var _0x85f6x17=_0x85f6x11[_0x85f6x15+ 3];for(var _0x85f6xd=3;_0x85f6xd> 0;_0x85f6xd--){_0x85f6x11[_0x85f6x15+ _0x85f6xd]= _0x85f6x11[_0x85f6x15+ _0x85f6xd- 1]};_0x85f6x11[_0x85f6x15]= _0x85f6x17}else {var _0x85f6x17=_0x85f6x11[_0x85f6x15];for(var _0x85f6xd=0;_0x85f6xd< 3;_0x85f6xd++){_0x85f6x11[_0x85f6x15+ _0x85f6xd]= _0x85f6x11[_0x85f6x15+ _0x85f6xd+ 1]};_0x85f6x11[_0x85f6x15+ 3]= _0x85f6x17}};return _0x85f6x11},galois_multiplication:function(_0x85f6x18,_0x85f6x19){var _0x85f6x1a=0;for(var _0x85f6x1b=0;_0x85f6x1b< 8;_0x85f6x1b++){if((_0x85f6x19& 1)== 1){_0x85f6x1a^= _0x85f6x18};if(_0x85f6x1a> 0x100){_0x85f6x1a^= 0x100};var _0x85f6x1c=(_0x85f6x18& 0x80);_0x85f6x18<<= 1;if(_0x85f6x18> 0x100){_0x85f6x18^= 0x100};if(_0x85f6x1c== 0x80){_0x85f6x18^= 0x1b};if(_0x85f6x18> 0x100){_0x85f6x18^= 0x100};_0x85f6x19>>= 1;if(_0x85f6x19> 0x100){_0x85f6x19^= 0x100}};return _0x85f6x1a},mixColumns:function(_0x85f6x11,_0x85f6x14){var _0x85f6x1d=[];for(var _0x85f6x4=0;_0x85f6x4< 4;_0x85f6x4++){for(var _0x85f6xd=0;_0x85f6xd< 4;_0x85f6xd++){_0x85f6x1d[_0x85f6xd]= _0x85f6x11[(_0x85f6xd* 4)+ _0x85f6x4]};_0x85f6x1d= this[_0x2465[9]](_0x85f6x1d,_0x85f6x14);for(var _0x85f6xe=0;_0x85f6xe< 4;_0x85f6xe++){_0x85f6x11[(_0x85f6xe* 4)+ _0x85f6x4]= _0x85f6x1d[_0x85f6xe]}};return _0x85f6x11},mixColumn:function(_0x85f6x1d,_0x85f6x14){var _0x85f6x1e=[];if(_0x85f6x14){_0x85f6x1e= [14,9,13,11]}else {_0x85f6x1e= [2,1,1,3]};var _0x85f6x1f=[];for(var _0x85f6x4=0;_0x85f6x4< 4;_0x85f6x4++){_0x85f6x1f[_0x85f6x4]= _0x85f6x1d[_0x85f6x4]};_0x85f6x1d[0]= this[_0x2465[10]](_0x85f6x1f[0],_0x85f6x1e[0])^ this[_0x2465[10]](_0x85f6x1f[3],_0x85f6x1e[1])^ this[_0x2465[10]](_0x85f6x1f[2],_0x85f6x1e[2])^ this[_0x2465[10]](_0x85f6x1f[1],_0x85f6x1e[3]);_0x85f6x1d[1]= this[_0x2465[10]](_0x85f6x1f[1],_0x85f6x1e[0])^ this[_0x2465[10]](_0x85f6x1f[0],_0x85f6x1e[1])^ this[_0x2465[10]](_0x85f6x1f[3],_0x85f6x1e[2])^ this[_0x2465[10]](_0x85f6x1f[2],_0x85f6x1e[3]);_0x85f6x1d[2]= this[_0x2465[10]](_0x85f6x1f[2],_0x85f6x1e[0])^ this[_0x2465[10]](_0x85f6x1f[1],_0x85f6x1e[1])^ this[_0x2465[10]](_0x85f6x1f[0],_0x85f6x1e[2])^ this[_0x2465[10]](_0x85f6x1f[3],_0x85f6x1e[3]);_0x85f6x1d[3]= this[_0x2465[10]](_0x85f6x1f[3],_0x85f6x1e[0])^ this[_0x2465[10]](_0x85f6x1f[2],_0x85f6x1e[1])^ this[_0x2465[10]](_0x85f6x1f[1],_0x85f6x1e[2])^ this[_0x2465[10]](_0x85f6x1f[0],_0x85f6x1e[3]);return _0x85f6x1d},round:function(_0x85f6x11,_0x85f6x12){_0x85f6x11= this[_0x2465[11]](_0x85f6x11,false);_0x85f6x11= this[_0x2465[12]](_0x85f6x11,false);_0x85f6x11= this[_0x2465[13]](_0x85f6x11,false);_0x85f6x11= this[_0x2465[14]](_0x85f6x11,_0x85f6x12);return _0x85f6x11},invRound:function(_0x85f6x11,_0x85f6x12){_0x85f6x11= this[_0x2465[12]](_0x85f6x11,true);_0x85f6x11= this[_0x2465[11]](_0x85f6x11,true);_0x85f6x11= this[_0x2465[14]](_0x85f6x11,_0x85f6x12);_0x85f6x11= this[_0x2465[13]](_0x85f6x11,true);return _0x85f6x11},main:function(_0x85f6x11,_0x85f6xc,_0x85f6x20){_0x85f6x11= this[_0x2465[14]](_0x85f6x11,this[_0x2465[15]](_0x85f6xc,0));for(var _0x85f6x4=1;_0x85f6x4< _0x85f6x20;_0x85f6x4++){_0x85f6x11= this[_0x2465[16]](_0x85f6x11,this[_0x2465[15]](_0x85f6xc,16* _0x85f6x4))};_0x85f6x11= this[_0x2465[11]](_0x85f6x11,false);_0x85f6x11= this[_0x2465[12]](_0x85f6x11,false);_0x85f6x11= this[_0x2465[14]](_0x85f6x11,this[_0x2465[15]](_0x85f6xc,16* _0x85f6x20));return _0x85f6x11},invMain:function(_0x85f6x11,_0x85f6xc,_0x85f6x20){_0x85f6x11= this[_0x2465[14]](_0x85f6x11,this[_0x2465[15]](_0x85f6xc,16* _0x85f6x20));for(var _0x85f6x4=_0x85f6x20- 1;_0x85f6x4> 0;_0x85f6x4--){_0x85f6x11= this[_0x2465[17]](_0x85f6x11,this[_0x2465[15]](_0x85f6xc,16* _0x85f6x4))};_0x85f6x11= this[_0x2465[12]](_0x85f6x11,true);_0x85f6x11= this[_0x2465[11]](_0x85f6x11,true);_0x85f6x11= this[_0x2465[14]](_0x85f6x11,this[_0x2465[15]](_0x85f6xc,0));return _0x85f6x11},numberOfRounds:function(_0x85f6x7){var _0x85f6x20;switch(_0x85f6x7){case this[_0x2465[6]][_0x2465[18]]:_0x85f6x20= 10;break;case this[_0x2465[6]][_0x2465[19]]:_0x85f6x20= 12;break;case this[_0x2465[6]][_0x2465[5]]:_0x85f6x20= 14;break;default:return null;break};return _0x85f6x20},encrypt:function(_0x85f6x21,_0x85f6x6,_0x85f6x7){var _0x85f6x22=[];var _0x85f6x23=[];var _0x85f6x20=this[_0x2465[3]](_0x85f6x7);for(var _0x85f6x4=0;_0x85f6x4< 4;_0x85f6x4++){for(var _0x85f6xd=0;_0x85f6xd< 4;_0x85f6xd++){_0x85f6x23[(_0x85f6x4+ (_0x85f6xd* 4))]= _0x85f6x21[(_0x85f6x4* 4)+ _0x85f6xd]}};var _0x85f6xc=this[_0x2465[20]](_0x85f6x6,_0x85f6x7);_0x85f6x23= this[_0x2465[21]](_0x85f6x23,_0x85f6xc,_0x85f6x20);for(var _0x85f6xe=0;_0x85f6xe< 4;_0x85f6xe++){for(var _0x85f6xf=0;_0x85f6xf< 4;_0x85f6xf++){_0x85f6x22[(_0x85f6xe* 4)+ _0x85f6xf]= _0x85f6x23[(_0x85f6xe+ (_0x85f6xf* 4))]}};return _0x85f6x22},decrypt:function(_0x85f6x21,_0x85f6x6,_0x85f6x7){var _0x85f6x22=[];var _0x85f6x23=[];var _0x85f6x20=this[_0x2465[3]](_0x85f6x7);for(var _0x85f6x4=0;_0x85f6x4< 4;_0x85f6x4++){for(var _0x85f6xd=0;_0x85f6xd< 4;_0x85f6xd++){_0x85f6x23[(_0x85f6x4+ (_0x85f6xd* 4))]= _0x85f6x21[(_0x85f6x4* 4)+ _0x85f6xd]}};var _0x85f6xc=this[_0x2465[20]](_0x85f6x6,_0x85f6x7);_0x85f6x23= this[_0x2465[22]](_0x85f6x23,_0x85f6xc,_0x85f6x20);for(var _0x85f6xe=0;_0x85f6xe< 4;_0x85f6xe++){for(var _0x85f6xf=0;_0x85f6xf< 4;_0x85f6xf++){_0x85f6x22[(_0x85f6xe* 4)+ _0x85f6xf]= _0x85f6x23[(_0x85f6xe+ (_0x85f6xf* 4))]}};return _0x85f6x22}},modeOfOperation:{OFB:0,CFB:1,CBC:2},getBlock:function(_0x85f6x24,_0x85f6x25,_0x85f6x26,_0x85f6x27){if(_0x85f6x26- _0x85f6x25> 16){_0x85f6x26= _0x85f6x25+ 16};return _0x85f6x24[_0x2465[23]](_0x85f6x25,_0x85f6x26)},encrypt:function(_0x85f6x24,_0x85f6x27,_0x85f6x6,_0x85f6x28){var _0x85f6x7=_0x85f6x6[_0x2465[24]];if(_0x85f6x28[_0x2465[24]]% 16){throw _0x2465[25]};var _0x85f6x29=[];var _0x85f6x21=[];var _0x85f6x22=[];var _0x85f6x2a=[];var _0x85f6x2b=[];var _0x85f6x2c=true;if(_0x85f6x27== this[_0x2465[27]][_0x2465[26]]){this[_0x2465[28]](_0x85f6x24)};if(_0x85f6x24!== null){for(var _0x85f6xd=0;_0x85f6xd< Math[_0x2465[29]](_0x85f6x24[_0x2465[24]]/ 16);_0x85f6xd++){var _0x85f6x25=_0x85f6xd* 16;var _0x85f6x26=_0x85f6xd* 16+ 16;if(_0x85f6xd* 16+ 16> _0x85f6x24[_0x2465[24]]){_0x85f6x26= _0x85f6x24[_0x2465[24]]};_0x85f6x29= this[_0x2465[30]](_0x85f6x24,_0x85f6x25,_0x85f6x26,_0x85f6x27);if(_0x85f6x27== this[_0x2465[27]][_0x2465[31]]){if(_0x85f6x2c){_0x85f6x22= this[_0x2465[33]][_0x2465[32]](_0x85f6x28,_0x85f6x6,_0x85f6x7);_0x85f6x2c= false}else {_0x85f6x22= this[_0x2465[33]][_0x2465[32]](_0x85f6x21,_0x85f6x6,_0x85f6x7)};for(var _0x85f6x4=0;_0x85f6x4< 16;_0x85f6x4++){_0x85f6x2a[_0x85f6x4]= _0x85f6x29[_0x85f6x4]^ _0x85f6x22[_0x85f6x4]};for(var _0x85f6xe=0;_0x85f6xe< _0x85f6x26- _0x85f6x25;_0x85f6xe++){_0x85f6x2b[_0x2465[34]](_0x85f6x2a[_0x85f6xe])};_0x85f6x21= _0x85f6x2a}else {if(_0x85f6x27== this[_0x2465[27]][_0x2465[35]]){if(_0x85f6x2c){_0x85f6x22= this[_0x2465[33]][_0x2465[32]](_0x85f6x28,_0x85f6x6,_0x85f6x7);_0x85f6x2c= false}else {_0x85f6x22= this[_0x2465[33]][_0x2465[32]](_0x85f6x21,_0x85f6x6,_0x85f6x7)};for(var _0x85f6x4=0;_0x85f6x4< 16;_0x85f6x4++){_0x85f6x2a[_0x85f6x4]= _0x85f6x29[_0x85f6x4]^ _0x85f6x22[_0x85f6x4]};for(var _0x85f6xe=0;_0x85f6xe< _0x85f6x26- _0x85f6x25;_0x85f6xe++){_0x85f6x2b[_0x2465[34]](_0x85f6x2a[_0x85f6xe])};_0x85f6x21= _0x85f6x22}else {if(_0x85f6x27== this[_0x2465[27]][_0x2465[26]]){for(var _0x85f6x4=0;_0x85f6x4< 16;_0x85f6x4++){_0x85f6x21[_0x85f6x4]= _0x85f6x29[_0x85f6x4]^ ((_0x85f6x2c)?_0x85f6x28[_0x85f6x4]:_0x85f6x2a[_0x85f6x4])};_0x85f6x2c= false;_0x85f6x2a= this[_0x2465[33]][_0x2465[32]](_0x85f6x21,_0x85f6x6,_0x85f6x7);for(var _0x85f6xe=0;_0x85f6xe< 16;_0x85f6xe++){_0x85f6x2b[_0x2465[34]](_0x85f6x2a[_0x85f6xe])}}}}}};return _0x85f6x2b},decrypt:function(_0x85f6x2d,_0x85f6x27,_0x85f6x6,_0x85f6x28){var _0x85f6x7=_0x85f6x6[_0x2465[24]];if(_0x85f6x28[_0x2465[24]]% 16){throw _0x2465[25]};var _0x85f6x2a=[];var _0x85f6x21=[];var _0x85f6x22=[];var _0x85f6x29=[];var _0x85f6x2e=[];var _0x85f6x2c=true;if(_0x85f6x2d!== null){for(var _0x85f6xd=0;_0x85f6xd< Math[_0x2465[29]](_0x85f6x2d[_0x2465[24]]/ 16);_0x85f6xd++){var _0x85f6x25=_0x85f6xd* 16;var _0x85f6x26=_0x85f6xd* 16+ 16;if(_0x85f6xd* 16+ 16> _0x85f6x2d[_0x2465[24]]){_0x85f6x26= _0x85f6x2d[_0x2465[24]]};_0x85f6x2a= this[_0x2465[30]](_0x85f6x2d,_0x85f6x25,_0x85f6x26,_0x85f6x27);if(_0x85f6x27== this[_0x2465[27]][_0x2465[31]]){if(_0x85f6x2c){_0x85f6x22= this[_0x2465[33]][_0x2465[32]](_0x85f6x28,_0x85f6x6,_0x85f6x7);_0x85f6x2c= false}else {_0x85f6x22= this[_0x2465[33]][_0x2465[32]](_0x85f6x21,_0x85f6x6,_0x85f6x7)};for(i= 0;i< 16;i++){_0x85f6x29[i]= _0x85f6x22[i]^ _0x85f6x2a[i]};for(var _0x85f6xe=0;_0x85f6xe< _0x85f6x26- _0x85f6x25;_0x85f6xe++){_0x85f6x2e[_0x2465[34]](_0x85f6x29[_0x85f6xe])};_0x85f6x21= _0x85f6x2a}else {if(_0x85f6x27== this[_0x2465[27]][_0x2465[35]]){if(_0x85f6x2c){_0x85f6x22= this[_0x2465[33]][_0x2465[32]](_0x85f6x28,_0x85f6x6,_0x85f6x7);_0x85f6x2c= false}else {_0x85f6x22= this[_0x2465[33]][_0x2465[32]](_0x85f6x21,_0x85f6x6,_0x85f6x7)};for(i= 0;i< 16;i++){_0x85f6x29[i]= _0x85f6x22[i]^ _0x85f6x2a[i]};for(var _0x85f6xe=0;_0x85f6xe< _0x85f6x26- _0x85f6x25;_0x85f6xe++){_0x85f6x2e[_0x2465[34]](_0x85f6x29[_0x85f6xe])};_0x85f6x21= _0x85f6x22}else {if(_0x85f6x27== this[_0x2465[27]][_0x2465[26]]){_0x85f6x22= this[_0x2465[33]][_0x2465[36]](_0x85f6x2a,_0x85f6x6,_0x85f6x7);for(i= 0;i< 16;i++){_0x85f6x29[i]= ((_0x85f6x2c)?_0x85f6x28[i]:_0x85f6x21[i])^ _0x85f6x22[i]};_0x85f6x2c= false;for(var _0x85f6xe=0;_0x85f6xe< _0x85f6x26- _0x85f6x25;_0x85f6xe++){_0x85f6x2e[_0x2465[34]](_0x85f6x29[_0x85f6xe])};_0x85f6x21= _0x85f6x2a}}}};if(_0x85f6x27== this[_0x2465[27]][_0x2465[26]]){this[_0x2465[37]](_0x85f6x2e)}};return _0x85f6x2e},padBytesIn:function(_0x85f6x2f){var _0x85f6x30=_0x85f6x2f[_0x2465[24]];var _0x85f6x31=16- (_0x85f6x30% 16);for(var _0x85f6x4=0;_0x85f6x4< _0x85f6x31;_0x85f6x4++){_0x85f6x2f[_0x2465[34]](_0x85f6x31)}},unpadBytesOut:function(_0x85f6x2f){var _0x85f6x32=0;var _0x85f6x31=-1;var _0x85f6x33=16;if(_0x85f6x2f[_0x2465[24]]> 16){for(var _0x85f6x4=_0x85f6x2f[_0x2465[24]]- 1;_0x85f6x4>= _0x85f6x2f[_0x2465[24]]- 1- _0x85f6x33;_0x85f6x4--){if(_0x85f6x2f[_0x85f6x4]<= _0x85f6x33){if(_0x85f6x31== -1){_0x85f6x31= _0x85f6x2f[_0x85f6x4]};if(_0x85f6x2f[_0x85f6x4]!= _0x85f6x31){_0x85f6x32= 0;break};_0x85f6x32++}else {break};if(_0x85f6x32== _0x85f6x31){break}};if(_0x85f6x32> 0){_0x85f6x2f[_0x2465[38]](_0x85f6x2f[_0x2465[24]]- _0x85f6x32,_0x85f6x32)}}}} +// """.trimIndent() + val decodeBase64 = "atob = function(s) {\n" + + " var e={},i,b=0,c,x,l=0,a,r='',w=String.fromCharCode,L=s.length;\n" + + " var A=\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n" + + " for(i=0;i<64;i++){e[A.charAt(i)]=i;}\n" + + " for(x=0;x=8){((a=(b>>>(l-=8))&0xff)||(x<(L-2)))&&(r+=w(a));}\n" + + " }\n" + + " return r;\n" + + "};" + val doc = "var document = {};" + + val siteScriptRegex = Regex("""script>(.*)\+";\s*path""") + val html = app.get(url).text + val siteScript = siteScriptRegex.find(html)?.groupValues?.getOrNull(1) + + rhino.evaluateString( + scope, + "$doc$decodeBase64$slowAes;$siteScript;", + "JavaScript", + 1, + null + ) + val jsEval = scope.get("document", scope) as? Scriptable + val cookies = jsEval?.get("cookie", jsEval) as? ConsString + return cookies?.split(";")?.associate { + val split = it.split("=") + (split.getOrNull(0)?.trim() ?: "") to (split.getOrNull(1)?.trim() ?: "") + }?.filter { it.key.isNotBlank() && it.value.isNotBlank() } ?: emptyMap() + } + + fun getType(t: String?): TvType { + return when (t?.lowercase()) { + "movie" -> TvType.AnimeMovie + "ova" -> TvType.OVA + else -> TvType.Anime + } + } + + fun getStatus(t: String?): ShowStatus? { + return when (t?.lowercase()) { + "finito" -> ShowStatus.Completed + "in corso" -> ShowStatus.Ongoing + else -> null + } + } + } + + private fun Element.toSearchResult(showEpisode: Boolean = true): AnimeSearchResponse { + fun String.parseHref(): String { + val h = this.split('.').toMutableList() + h[1] = h[1].substringBeforeLast('/') + return h.joinToString(".") + } + + val anchor = this.select("a.name").firstOrNull() ?: throw ErrorLoadingException("Error") + val title = anchor.text().removeSuffix(" (ITA)") + val otherTitle = anchor.attr("data-jtitle").removeSuffix(" (ITA)") + + val url = fixUrl(anchor.attr("href").parseHref()) + val poster = this.select("a.poster img").attr("src") + + val statusElement = this.select("div.status") // .first() + val dub = statusElement.select(".dub").isNotEmpty() + + val episode = if (showEpisode) statusElement.select(".ep").text().split(' ').last() + .toIntOrNull() else null + val type = when { + statusElement.select(".movie").isNotEmpty() -> TvType.AnimeMovie + statusElement.select(".ova").isNotEmpty() -> TvType.OVA + else -> TvType.Anime + } + + return newAnimeSearchResponse(title, url, type) { + addDubStatus(dub, episode) + this.otherName = otherTitle + this.posterUrl = poster + } + } + + override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { + val document = request(mainUrl).document + val list = ArrayList() + + val widget = document.select(".widget.hotnew") + widget.select(".tabs [data-name=\"sub\"], .tabs [data-name=\"dub\"]").forEach { tab -> + val tabId = tab.attr("data-name") + val tabName = tab.text().removeSuffix("-ITA") + val animeList = widget.select("[data-name=\"$tabId\"] .film-list .item").map { + it.toSearchResult() + } + list.add(HomePageList(tabName, animeList)) + } + widget.select(".tabs [data-name=\"trending\"]").forEach { tab -> + val tabId = tab.attr("data-name") + val tabName = tab.text() + val animeList = widget.select("[data-name=\"$tabId\"] .film-list .item").map { + it.toSearchResult(showEpisode = false) + }.distinctBy { it.url } + list.add(HomePageList(tabName, animeList)) + } + return HomePageResponse(list) + } + + override suspend fun search(query: String): List { + val document = request("$mainUrl/search?keyword=$query").document + return document.select(".film-list > .item").map { + it.toSearchResult(showEpisode = false) + } + } + + override suspend fun load(url: String): LoadResponse { + val document = request(url).document + + val widget = document.select("div.widget.info") + val title = widget.select(".info .title").text().removeSuffix(" (ITA)") + val otherTitle = widget.select(".info .title").attr("data-jtitle").removeSuffix(" (ITA)") + val description = + widget.select(".desc .long").first()?.text() ?: widget.select(".desc").text() + val poster = document.select(".thumb img").attr("src") + + val type: TvType = getType(widget.select("dd").first()?.text()) + val genres = widget.select(".meta").select("a[href*=\"/genre/\"]").map { it.text() } + val rating = widget.select("#average-vote").text() + + val trailerUrl = document.select(".trailer[data-url]").attr("data-url") + val malId = document.select("#mal-button").attr("href") + .split('/').last().toIntOrNull() + val anlId = document.select("#anilist-button").attr("href") + .split('/').last().toIntOrNull() + + var dub = false + var year: Int? = null + var status: ShowStatus? = null + var duration: String? = null + + for (meta in document.select(".meta dt, .meta dd")) { + val text = meta.text() + if (text.contains("Audio")) + dub = meta.nextElementSibling()?.text() == "Italiano" + else if (year == null && text.contains("Data")) + year = meta.nextElementSibling()?.text()?.split(' ')?.last()?.toIntOrNull() + else if (status == null && text.contains("Stato")) + status = getStatus(meta.nextElementSibling()?.text()) + else if (status == null && text.contains("Durata")) + duration = meta.nextElementSibling()?.text() + } + + val servers = document.select(".widget.servers") + val episodes = servers.select(".server[data-name=\"9\"] .episode").map { + val id = it.select("a").attr("data-id") + val number = it.select("a").attr("data-episode-num").toIntOrNull() + Episode( + "$mainUrl/api/episode/info?id=$id", + episode = number + ) + } + val comingSoon = episodes.isEmpty() + + val recommendations = document.select(".film-list.interesting .item").map { + it.toSearchResult(showEpisode = false) + } + + return newAnimeLoadResponse(title, url, type) { + engName = title + japName = otherTitle + addPoster(poster) + this.year = year + addEpisodes(if (dub) DubStatus.Dubbed else DubStatus.Subbed, episodes) + showStatus = status + plot = description + tags = genres + addMalId(malId) + addAniListId(anlId) + addRating(rating) + addDuration(duration) + addTrailer(trailerUrl) + this.recommendations = recommendations + this.comingSoon = comingSoon + } + } + + data class Json( + @JsonProperty("grabber") val grabber: String, + @JsonProperty("name") val name: String, + @JsonProperty("target") val target: String, + ) + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + val url = tryParseJson( + request(data).text + )?.grabber + + if (url.isNullOrEmpty()) + return false + + callback.invoke( + ExtractorLink( + name, + name, + url, + referer = mainUrl, + quality = Qualities.Unknown.value + ) + ) + return true + } +} diff --git a/AnimeWorldProvider/src/main/kotlin/com/lagradost/AnimeWorldProviderPlugin.kt b/AnimeWorldProvider/src/main/kotlin/com/lagradost/AnimeWorldProviderPlugin.kt new file mode 100644 index 0000000..07f9f19 --- /dev/null +++ b/AnimeWorldProvider/src/main/kotlin/com/lagradost/AnimeWorldProviderPlugin.kt @@ -0,0 +1,14 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class AnimeWorldProviderPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(AnimeWorldProvider()) + } +} \ No newline at end of file diff --git a/AnimefenixProvider/build.gradle.kts b/AnimefenixProvider/build.gradle.kts new file mode 100644 index 0000000..9f21860 --- /dev/null +++ b/AnimefenixProvider/build.gradle.kts @@ -0,0 +1,27 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "es" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + // authors = listOf("Cloudburst") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "AnimeMovie", + "OVA", + "Anime", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=animefenix.com&sz=%size%" +} \ No newline at end of file diff --git a/AnimefenixProvider/src/main/AndroidManifest.xml b/AnimefenixProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/AnimefenixProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/AnimefenixProvider/src/main/kotlin/com/lagradost/AnimefenixProvider.kt b/AnimefenixProvider/src/main/kotlin/com/lagradost/AnimefenixProvider.kt new file mode 100644 index 0000000..203fa8d --- /dev/null +++ b/AnimefenixProvider/src/main/kotlin/com/lagradost/AnimefenixProvider.kt @@ -0,0 +1,249 @@ +package com.lagradost + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.AppUtils.parseJson +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.Qualities +import com.lagradost.cloudstream3.utils.loadExtractor +import org.jsoup.Jsoup +import java.util.* + + +class AnimefenixProvider:MainAPI() { + + override var mainUrl = "https://animefenix.com" + override var name = "Animefenix" + override var lang = "es" + override val hasMainPage = true + override val hasChromecastSupport = true + override val hasDownloadSupport = true + override val supportedTypes = setOf( + TvType.AnimeMovie, + TvType.OVA, + TvType.Anime, + ) + + fun getDubStatus(title: String): DubStatus { + return if (title.contains("Latino") || title.contains("Castellano")) + DubStatus.Dubbed + else DubStatus.Subbed + } + + override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { + val urls = listOf( + Pair("$mainUrl/", "Animes"), + Pair("$mainUrl/animes?type[]=movie&order=default", "Peliculas", ), + Pair("$mainUrl/animes?type[]=ova&order=default", "OVA's", ), + ) + + val items = ArrayList() + + items.add( + HomePageList( + "Últimos episodios", + app.get(mainUrl).document.select(".capitulos-grid div.item").map { + val title = it.selectFirst("div.overtitle")?.text() + val poster = it.selectFirst("a img")?.attr("src") + val epRegex = Regex("(-(\\d+)\$|-(\\d+)\\.(\\d+))") + val url = it.selectFirst("a")?.attr("href")?.replace(epRegex,"") + ?.replace("/ver/","/") + val epNum = it.selectFirst(".is-size-7")?.text()?.replace("Episodio ","")?.toIntOrNull() + newAnimeSearchResponse(title!!, url!!) { + this.posterUrl = poster + addDubStatus(getDubStatus(title), epNum) + } + }) + ) + + urls.apmap { (url, name) -> + val response = app.get(url) + val soup = Jsoup.parse(response.text) + val home = soup.select(".list-series article").map { + val title = it.selectFirst("h3 a")?.text() + val poster = it.selectFirst("figure img")?.attr("src") + AnimeSearchResponse( + title!!, + it.selectFirst("a")?.attr("href") ?: "", + this.name, + TvType.Anime, + poster, + null, + if (title.contains("Latino")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of(DubStatus.Subbed), + ) + } + + items.add(HomePageList(name, home)) + } + + if (items.size <= 0) throw ErrorLoadingException() + return HomePageResponse(items) + } + + override suspend fun search(query: String): List { + return app.get("$mainUrl/animes?q=$query").document.select(".list-series article").map { + val title = it.selectFirst("h3 a")?.text() + val href = it.selectFirst("a")?.attr("href") + val image = it.selectFirst("figure img")?.attr("src") + AnimeSearchResponse( + title!!, + href!!, + this.name, + TvType.Anime, + fixUrl(image ?: ""), + null, + if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of( + DubStatus.Subbed), + ) + } + } + + override suspend fun load(url: String): LoadResponse { + val doc = Jsoup.parse(app.get(url, timeout = 120).text) + val poster = doc.selectFirst(".image > img")?.attr("src") + val title = doc.selectFirst("h1.title.has-text-orange")?.text() + val description = doc.selectFirst("p.has-text-light")?.text() + val genres = doc.select(".genres a").map { it.text() } + val status = when (doc.selectFirst(".is-narrow-desktop a.button")?.text()) { + "Emisión" -> ShowStatus.Ongoing + "Finalizado" -> ShowStatus.Completed + else -> null + } + val episodes = doc.select(".anime-page__episode-list li").map { + val name = it.selectFirst("span")?.text() + val link = it.selectFirst("a")?.attr("href") + Episode(link!!, name) + }.reversed() + val type = if (doc.selectFirst("ul.has-text-light")?.text() + !!.contains("Película") && episodes.size == 1 + ) TvType.AnimeMovie else TvType.Anime + return newAnimeLoadResponse(title!!, url, type) { + japName = null + engName = title + posterUrl = poster + addEpisodes(DubStatus.Subbed, episodes) + plot = description + tags = genres + showStatus = status + } + } + + private fun cleanStreamID(input: String): String = input.replace(Regex("player=.*&code=|&"),"") + + data class Amazon ( + @JsonProperty("file") var file : String? = null, + @JsonProperty("type") var type : String? = null, + @JsonProperty("label") var label : String? = null + ) + + private fun cleanExtractor( + source: String, + name: String, + url: String, + callback: (ExtractorLink) -> Unit + ): Boolean { + callback( + ExtractorLink( + source, + name, + url, + "", + Qualities.Unknown.value, + false + ) + ) + return true + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + val soup = app.get(data).document + val script = soup.selectFirst(".player-container script")?.data() + if (script!!.contains("var tabsArray =")) { + val sourcesRegex = Regex("player=.*&code(.*)&") + val test = sourcesRegex.findAll(script).toList() + test.apmap { + val codestream = it.value + val links = when { + codestream.contains("player=2&") -> "https://embedsito.com/v/"+cleanStreamID(codestream) + codestream.contains("player=3&") -> "https://www.mp4upload.com/embed-"+cleanStreamID(codestream)+".html" + codestream.contains("player=6&") -> "https://www.yourupload.com/embed/"+cleanStreamID(codestream) + codestream.contains("player=12&") -> "http://ok.ru/videoembed/"+cleanStreamID(codestream) + codestream.contains("player=4&") -> "https://sendvid.com/"+cleanStreamID(codestream) + codestream.contains("player=9&") -> "AmaNormal https://www.animefenix.com/stream/amz.php?v="+cleanStreamID(codestream) + codestream.contains("player=11&") -> "AmazonES https://www.animefenix.com/stream/amz.php?v="+cleanStreamID(codestream) + codestream.contains("player=22&") -> "Fireload https://www.animefenix.com/stream/fl.php?v="+cleanStreamID(codestream) + + else -> "" + } + loadExtractor(links, data, subtitleCallback, callback) + + argamap({ + if (links.contains("AmaNormal")) { + val doc = app.get(links.replace("AmaNormal ","")).document + doc.select("script").map { script -> + if (script.data().contains("sources: [{\"file\"")) { + val text = script.data().substringAfter("sources:").substringBefore("]").replace("[","") + val json = parseJson(text) + if (json.file != null) { + cleanExtractor( + "Amazon", + "Amazon ${json.label}", + json.file!!, + callback + ) + } + } + } + } + + if (links.contains("AmazonES")) { + val amazonES = links.replace("AmazonES ", "") + val doc = app.get("$amazonES&ext=es").document + doc.select("script").map { script -> + if (script.data().contains("sources: [{\"file\"")) { + val text = script.data().substringAfter("sources:").substringBefore("]").replace("[","") + val json = parseJson(text) + if (json.file != null) { + cleanExtractor( + "AmazonES", + "AmazonES ${json.label}", + json.file!!, + callback + ) + } + } + } + } + if (links.contains("Fireload")) { + val doc = app.get(links.replace("Fireload ", "")).document + doc.select("script").map { script -> + if (script.data().contains("sources: [{\"file\"")) { + val text = script.data().substringAfter("sources:").substringBefore("]").replace("[","") + val json = parseJson(text) + val testurl = if (json.file?.contains("fireload") == true) { + app.get("https://${json.file}").text + } else null + if (testurl?.contains("error") == true) { + // + } else if (json.file?.contains("fireload") == true) { + cleanExtractor( + "Fireload", + "Fireload ${json.label}", + "https://"+json.file!!, + callback + ) + } + } + } + } + }) + } + } + return true + } +} \ No newline at end of file diff --git a/AnimefenixProvider/src/main/kotlin/com/lagradost/AnimefenixProviderPlugin.kt b/AnimefenixProvider/src/main/kotlin/com/lagradost/AnimefenixProviderPlugin.kt new file mode 100644 index 0000000..459bd29 --- /dev/null +++ b/AnimefenixProvider/src/main/kotlin/com/lagradost/AnimefenixProviderPlugin.kt @@ -0,0 +1,14 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class AnimefenixProviderPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(AnimefenixProvider()) + } +} \ No newline at end of file diff --git a/AnimeflvIOProvider/build.gradle.kts b/AnimeflvIOProvider/build.gradle.kts new file mode 100644 index 0000000..7ec5544 --- /dev/null +++ b/AnimeflvIOProvider/build.gradle.kts @@ -0,0 +1,27 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "es" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + // authors = listOf("Cloudburst") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "AnimeMovie", + "Anime", + "OVA", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=animeflv.io&sz=%size%" +} \ No newline at end of file diff --git a/AnimeflvIOProvider/src/main/AndroidManifest.xml b/AnimeflvIOProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/AnimeflvIOProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/AnimeflvIOProvider/src/main/kotlin/com/lagradost/AnimeflvIOProvider.kt b/AnimeflvIOProvider/src/main/kotlin/com/lagradost/AnimeflvIOProvider.kt new file mode 100644 index 0000000..5b74053 --- /dev/null +++ b/AnimeflvIOProvider/src/main/kotlin/com/lagradost/AnimeflvIOProvider.kt @@ -0,0 +1,239 @@ +package com.lagradost + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.AppUtils.parseJson +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8 +import com.lagradost.cloudstream3.utils.Qualities +import com.lagradost.cloudstream3.utils.getQualityFromName +import com.lagradost.cloudstream3.utils.loadExtractor +import java.util.* + +class AnimeflvIOProvider : MainAPI() { + override var mainUrl = "https://animeflv.io" //Also scrapes from animeid.to + override var name = "Animeflv.io" + override var lang = "es" + override val hasMainPage = true + override val hasChromecastSupport = true + override val hasDownloadSupport = true + override val supportedTypes = setOf( + TvType.AnimeMovie, + TvType.OVA, + TvType.Anime, + ) + + override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { + val items = ArrayList() + val urls = listOf( + Pair("$mainUrl/series", "Series actualizadas"), + Pair("$mainUrl/peliculas", "Peliculas actualizadas"), + ) + items.add( + HomePageList( + "Estrenos", + app.get(mainUrl).document.select("div#owl-demo-premiere-movies .pull-left").map { + val title = it.selectFirst("p")?.text() ?: "" + AnimeSearchResponse( + title, + fixUrl(it.selectFirst("a")?.attr("href") ?: ""), + this.name, + TvType.Anime, + it.selectFirst("img")?.attr("src"), + it.selectFirst("span.year").toString().toIntOrNull(), + EnumSet.of(DubStatus.Subbed), + ) + }) + ) + urls.apmap { (url, name) -> + val soup = app.get(url).document + val home = soup.select("div.item-pelicula").map { + val title = it.selectFirst(".item-detail p")?.text() ?: "" + val poster = it.selectFirst("figure img")?.attr("src") + AnimeSearchResponse( + title, + fixUrl(it.selectFirst("a")?.attr("href") ?: ""), + this.name, + TvType.Anime, + poster, + null, + if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of( + DubStatus.Dubbed + ) else EnumSet.of(DubStatus.Subbed), + ) + } + + items.add(HomePageList(name, home)) + } + + if (items.size <= 0) throw ErrorLoadingException() + return HomePageResponse(items) + } + + override suspend fun search(query: String): List { + val headers = mapOf( + "Host" to "animeflv.io", + "User-Agent" to USER_AGENT, + "X-Requested-With" to "XMLHttpRequest", + "DNT" to "1", + "Alt-Used" to "animeflv.io", + "Connection" to "keep-alive", + "Referer" to "https://animeflv.io", + ) + val url = "$mainUrl/search.html?keyword=$query" + val document = app.get( + url, + headers = headers + ).document + return document.select(".item-pelicula.pull-left").map { + val title = it.selectFirst("div.item-detail p")?.text() ?: "" + val href = fixUrl(it.selectFirst("a")?.attr("href") ?: "") + var image = it.selectFirst("figure img")?.attr("src") ?: "" + val isMovie = href.contains("/pelicula/") + if (image.contains("/static/img/picture.png")) { + image = "" + } + if (isMovie) { + MovieSearchResponse( + title, + href, + this.name, + TvType.AnimeMovie, + image, + null + ) + } else { + AnimeSearchResponse( + title, + href, + this.name, + TvType.Anime, + image, + null, + EnumSet.of(DubStatus.Subbed), + ) + } + } + } + + override suspend fun load(url: String): LoadResponse? { + // Gets the url returned from searching. + val soup = app.get(url).document + val title = soup.selectFirst(".info-content h1")?.text() + val description = soup.selectFirst("span.sinopsis")?.text()?.trim() + val poster: String? = soup.selectFirst(".poster img")?.attr("src") + val episodes = soup.select(".item-season-episodes a").map { li -> + val href = fixUrl(li.selectFirst("a")?.attr("href") ?: "") + val name = li.selectFirst("a")?.text() ?: "" + Episode( + href, name, + ) + }.reversed() + + val year = Regex("(\\d*)").find(soup.select(".info-half").text()) + + val tvType = if (url.contains("/pelicula/")) TvType.AnimeMovie else TvType.Anime + val genre = soup.select(".content-type-a a") + .map { it?.text()?.trim().toString().replace(", ", "") } + val duration = Regex("""(\d*)""").find( + soup.select("p.info-half:nth-child(4)").text() + ) + + return when (tvType) { + TvType.Anime -> { + return newAnimeLoadResponse(title ?: "", url, tvType) { + japName = null + engName = title + posterUrl = poster + this.year = null + addEpisodes(DubStatus.Subbed, episodes) + plot = description + tags = genre + + showStatus = null + } + } + TvType.AnimeMovie -> { + MovieLoadResponse( + title ?: "", + url, + this.name, + tvType, + url, + poster, + year.toString().toIntOrNull(), + description, + null, + genre, + duration.toString().toIntOrNull(), + ) + } + else -> null + } + } + + data class MainJson( + @JsonProperty("source") val source: List, + @JsonProperty("source_bk") val sourceBk: String?, + @JsonProperty("track") val track: List?, + @JsonProperty("advertising") val advertising: List?, + @JsonProperty("linkiframe") val linkiframe: String? + ) + + data class Source( + @JsonProperty("file") val file: String, + @JsonProperty("label") val label: String, + @JsonProperty("default") val default: String, + @JsonProperty("type") val type: String + ) + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + app.get(data).document.select("li.tab-video").apmap { + val url = fixUrl(it.attr("data-video")) + if (url.contains("animeid")) { + val ajaxurl = url.replace("streaming.php", "ajax.php") + val ajaxurltext = app.get(ajaxurl).text + val json = parseJson(ajaxurltext) + json.source.forEach { source -> + if (source.file.contains("m3u8")) { + generateM3u8( + "Animeflv.io", + source.file, + "https://animeid.to", + headers = mapOf("Referer" to "https://animeid.to") + ).apmap { + callback( + ExtractorLink( + "Animeflv.io", + "Animeflv.io", + it.url, + "https://animeid.to", + getQualityFromName(it.quality.toString()), + it.url.contains("m3u8") + ) + ) + } + } else { + callback( + ExtractorLink( + name, + "$name ${source.label}", + source.file, + "https://animeid.to", + Qualities.Unknown.value, + isM3u8 = source.file.contains("m3u8") + ) + ) + } + } + } + loadExtractor(url, data, subtitleCallback, callback) + } + return true + } +} \ No newline at end of file diff --git a/AnimeflvIOProvider/src/main/kotlin/com/lagradost/AnimeflvIOProviderPlugin.kt b/AnimeflvIOProvider/src/main/kotlin/com/lagradost/AnimeflvIOProviderPlugin.kt new file mode 100644 index 0000000..f0fa600 --- /dev/null +++ b/AnimeflvIOProvider/src/main/kotlin/com/lagradost/AnimeflvIOProviderPlugin.kt @@ -0,0 +1,14 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class AnimeflvIOProviderPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(AnimeflvIOProvider()) + } +} \ No newline at end of file diff --git a/AnimeflvnetProvider/build.gradle.kts b/AnimeflvnetProvider/build.gradle.kts new file mode 100644 index 0000000..d864802 --- /dev/null +++ b/AnimeflvnetProvider/build.gradle.kts @@ -0,0 +1,27 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "es" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + // authors = listOf("Cloudburst") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "AnimeMovie", + "Anime", + "OVA", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=www3.animeflv.net&sz=%size%" +} \ No newline at end of file diff --git a/AnimeflvnetProvider/src/main/AndroidManifest.xml b/AnimeflvnetProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/AnimeflvnetProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/AnimeflvnetProvider/src/main/kotlin/com/lagradost/AnimeflvnetProvider.kt b/AnimeflvnetProvider/src/main/kotlin/com/lagradost/AnimeflvnetProvider.kt new file mode 100644 index 0000000..c30076f --- /dev/null +++ b/AnimeflvnetProvider/src/main/kotlin/com/lagradost/AnimeflvnetProvider.kt @@ -0,0 +1,182 @@ +package com.lagradost + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.AppUtils.parseJson +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.loadExtractor +import java.util.* + +class AnimeflvnetProvider : MainAPI() { + companion object { + fun getType(t: String): TvType { + return if (t.contains("OVA") || t.contains("Especial")) TvType.OVA + else if (t.contains("Película")) TvType.AnimeMovie + else TvType.Anime + } + + fun getDubStatus(title: String): DubStatus { + return if (title.contains("Latino") || title.contains("Castellano")) + DubStatus.Dubbed + else DubStatus.Subbed + } + } + + override var mainUrl = "https://www3.animeflv.net" + override var name = "Animeflv.net" + override var lang = "es" + override val hasMainPage = true + override val hasChromecastSupport = true + override val hasDownloadSupport = true + override val supportedTypes = setOf( + TvType.AnimeMovie, + TvType.OVA, + TvType.Anime, + ) + + override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { + val urls = listOf( + Pair("$mainUrl/browse?type[]=movie&order=updated", "Películas"), + Pair("$mainUrl/browse?status[]=2&order=default", "Animes"), + Pair("$mainUrl/browse?status[]=1&order=rating", "En emision"), + ) + val items = ArrayList() + items.add( + HomePageList( + "Últimos episodios", + app.get(mainUrl).document.select("main.Main ul.ListEpisodios li").mapNotNull { + val title = it.selectFirst("strong.Title")?.text() ?: return@mapNotNull null + val poster = it.selectFirst("span img")?.attr("src") ?: return@mapNotNull null + val epRegex = Regex("(-(\\d+)\$)") + val url = it.selectFirst("a")?.attr("href")?.replace(epRegex, "") + ?.replace("ver/", "anime/") ?: return@mapNotNull null + val epNum = + it.selectFirst("span.Capi")?.text()?.replace("Episodio ", "")?.toIntOrNull() + newAnimeSearchResponse(title, url) { + this.posterUrl = fixUrl(poster) + addDubStatus(getDubStatus(title), epNum) + } + }) + ) + for ((url, name) in urls) { + try { + val doc = app.get(url).document + val home = doc.select("ul.ListAnimes li article").mapNotNull { + val title = it.selectFirst("h3.Title")?.text() ?: return@mapNotNull null + val poster = it.selectFirst("figure img")?.attr("src") ?: return@mapNotNull null + newAnimeSearchResponse( + title, + fixUrl(it.selectFirst("a")?.attr("href") ?: return@mapNotNull null) + ) { + this.posterUrl = fixUrl(poster) + addDubStatus(getDubStatus(title)) + } + } + + items.add(HomePageList(name, home)) + } catch (e: Exception) { + e.printStackTrace() + } + } + if (items.size <= 0) throw ErrorLoadingException() + return HomePageResponse(items) + } + + data class SearchObject( + @JsonProperty("id") val id: String, + @JsonProperty("title") val title: String, + @JsonProperty("type") val type: String, + @JsonProperty("last_id") val lastId: String, + @JsonProperty("slug") val slug: String + ) + + override suspend fun search(query: String): List { + val response = app.post( + "https://www3.animeflv.net/api/animes/search", + data = mapOf(Pair("value", query)) + ).text + val json = parseJson>(response) + return json.map { searchr -> + val title = searchr.title + val href = "$mainUrl/anime/${searchr.slug}" + val image = "$mainUrl/uploads/animes/covers/${searchr.id}.jpg" + AnimeSearchResponse( + title, + href, + this.name, + TvType.Anime, + fixUrl(image), + null, + if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of( + DubStatus.Subbed + ), + ) + } + } + + override suspend fun load(url: String): LoadResponse { + val doc = app.get(url).document + val episodes = ArrayList() + val title = doc.selectFirst("h1.Title")!!.text() + val poster = doc.selectFirst("div.AnimeCover div.Image figure img")?.attr("src")!! + val description = doc.selectFirst("div.Description p")?.text() + val type = doc.selectFirst("span.Type")?.text() ?: "" + val status = when (doc.selectFirst("p.AnmStts span")?.text()) { + "En emision" -> ShowStatus.Ongoing + "Finalizado" -> ShowStatus.Completed + else -> null + } + val genre = doc.select("nav.Nvgnrs a") + .map { it?.text()?.trim().toString() } + + doc.select("script").map { script -> + if (script.data().contains("var episodes = [")) { + val data = script.data().substringAfter("var episodes = [").substringBefore("];") + data.split("],").forEach { + val epNum = it.removePrefix("[").substringBefore(",") + // val epthumbid = it.removePrefix("[").substringAfter(",").substringBefore("]") + val animeid = doc.selectFirst("div.Strs.RateIt")?.attr("data-id") + val epthumb = "https://cdn.animeflv.net/screenshots/$animeid/$epNum/th_3.jpg" + val link = url.replace("/anime/", "/ver/") + "-$epNum" + episodes.add( + Episode( + link, + null, + posterUrl = epthumb, + episode = epNum.toIntOrNull() + ) + ) + } + } + } + return newAnimeLoadResponse(title, url, getType(type)) { + posterUrl = fixUrl(poster) + addEpisodes(DubStatus.Subbed, episodes.reversed()) + showStatus = status + plot = description + tags = genre + } + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + app.get(data).document.select("script").apmap { script -> + if (script.data().contains("var videos = {") || script.data() + .contains("var anime_id =") || script.data().contains("server") + ) { + val videos = script.data().replace("\\/", "/") + fetchUrls(videos).map { + it.replace("https://embedsb.com/e/", "https://watchsb.com/e/") + .replace("https://ok.ru", "http://ok.ru") + }.apmap { + loadExtractor(it, data, subtitleCallback, callback) + } + } + } + return true + } +} diff --git a/AnimeflvnetProvider/src/main/kotlin/com/lagradost/AnimeflvnetProviderPlugin.kt b/AnimeflvnetProvider/src/main/kotlin/com/lagradost/AnimeflvnetProviderPlugin.kt new file mode 100644 index 0000000..9f45880 --- /dev/null +++ b/AnimeflvnetProvider/src/main/kotlin/com/lagradost/AnimeflvnetProviderPlugin.kt @@ -0,0 +1,14 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class AnimeflvnetProviderPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(AnimeflvnetProvider()) + } +} \ No newline at end of file diff --git a/CalcioStreamingProvider/build.gradle.kts b/CalcioStreamingProvider/build.gradle.kts new file mode 100644 index 0000000..7bf0bc2 --- /dev/null +++ b/CalcioStreamingProvider/build.gradle.kts @@ -0,0 +1,25 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "it" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + // authors = listOf("Cloudburst") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "Live", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=calciostreaming.live&sz=%size%" +} \ No newline at end of file diff --git a/CalcioStreamingProvider/src/main/AndroidManifest.xml b/CalcioStreamingProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/CalcioStreamingProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/CalcioStreamingProvider/src/main/kotlin/com/lagradost/CalcioStreamingProvider.kt b/CalcioStreamingProvider/src/main/kotlin/com/lagradost/CalcioStreamingProvider.kt new file mode 100644 index 0000000..857d054 --- /dev/null +++ b/CalcioStreamingProvider/src/main/kotlin/com/lagradost/CalcioStreamingProvider.kt @@ -0,0 +1,107 @@ +package com.lagradost + +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* + + +class CalcioStreamingProvider : MainAPI() { + override var lang = "it" + override var mainUrl = "https://calciostreaming.live" + override var name = "CalcioStreaming" + override val hasMainPage = true + override val hasChromecastSupport = true + override val supportedTypes = setOf( + TvType.Live, + + ) + override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { + val document = app.get(mainUrl+"/partite-streaming.html").document + val sections = document.select("div.slider-title").filter {it -> it.select("div.item").isNotEmpty()} + + if (sections.isEmpty()) throw ErrorLoadingException() + + return HomePageResponse(sections.map { it -> + val categoryname = it.selectFirst("h2 > strong")!!.text() + val shows = it.select("div.item").map { + val href = it.selectFirst("a")!!.attr("href") + val name = it.selectFirst("a > div > h1")!!.text() + val posterurl = fixUrl(it.selectFirst("a > img")!!.attr("src")) + LiveSearchResponse( + name, + href, + this@CalcioStreamingProvider.name, + TvType.Live, + posterurl, + ) + } + HomePageList( + categoryname, + shows, + isHorizontalImages = true + ) + + }) + + } + + + override suspend fun load(url: String): LoadResponse { + + val document = app.get(url).document + val poster = fixUrl(document.select("#title-single > div").attr("style").substringAfter("url(").substringBeforeLast(")")) + val Matchstart = document.select("div.info-wrap > div").textNodes().joinToString("").trim() + return LiveStreamLoadResponse( + document.selectFirst(" div.info-t > h1")!!.text(), + url, + this.name, + url, + poster, + plot = Matchstart + ) + + + } + + + + + private suspend fun extractVideoLinks( + url: String, + callback: (ExtractorLink) -> Unit + ) { + val document = app.get(url).document + document.select("button.btn").forEach { button -> + val link1 = button.attr("data-link") + val doc2 = app.get(link1).document + val truelink = doc2.selectFirst("iframe")!!.attr("src") + val newpage = app.get(truelink, referer = link1).document + val streamurl = Regex(""""((.|\n)*?).";""").find( + getAndUnpack( + newpage.select("script")[6].childNode(0).toString() + ))!!.value.replace("""src="""", "").replace(""""""", "").replace(";", "") + + callback( + ExtractorLink( + this.name, + button.text(), + streamurl, + truelink, + quality = 0, + true + ) + ) + } + } + + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + extractVideoLinks(data, callback) + + return true + } +} diff --git a/CalcioStreamingProvider/src/main/kotlin/com/lagradost/CalcioStreamingProviderPlugin.kt b/CalcioStreamingProvider/src/main/kotlin/com/lagradost/CalcioStreamingProviderPlugin.kt new file mode 100644 index 0000000..a7ecfca --- /dev/null +++ b/CalcioStreamingProvider/src/main/kotlin/com/lagradost/CalcioStreamingProviderPlugin.kt @@ -0,0 +1,14 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class CalcioStreamingProviderPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(CalcioStreamingProvider()) + } +} \ No newline at end of file diff --git a/CineBlogProvider/build.gradle.kts b/CineBlogProvider/build.gradle.kts new file mode 100644 index 0000000..88ebf30 --- /dev/null +++ b/CineBlogProvider/build.gradle.kts @@ -0,0 +1,27 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "it" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + // authors = listOf("Cloudburst") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "TvSeries", + "Movie", + ) + + + iconUrl = "https://www.google.com/s2/favicons?domain=cb01.rip&sz=%size%" +} \ No newline at end of file diff --git a/CineBlogProvider/src/main/AndroidManifest.xml b/CineBlogProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/CineBlogProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/CineBlogProvider/src/main/kotlin/com/lagradost/CineBlogProvider.kt b/CineBlogProvider/src/main/kotlin/com/lagradost/CineBlogProvider.kt new file mode 100644 index 0000000..7ab87cd --- /dev/null +++ b/CineBlogProvider/src/main/kotlin/com/lagradost/CineBlogProvider.kt @@ -0,0 +1,187 @@ +package com.lagradost + +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.loadExtractor + + +class CineBlogProvider : MainAPI() { + override var lang = "it" + override var mainUrl = "https://cb01.rip" + override var name = "CineBlog" + override val hasMainPage = true + override val hasChromecastSupport = true + override val supportedTypes = setOf( + TvType.Movie, + TvType.TvSeries, + ) + override val mainPage = mainPageOf( + Pair("$mainUrl/popolari/page/number/?get=movies", "Film Popolari"), + Pair("$mainUrl/popolari/page/number/?get=tv", "Serie Tv Popolari"), + Pair("$mainUrl/i-piu-votati/page/number/?get=movies", "Film più votati"), + Pair("$mainUrl/i-piu-votati/page/number/?get=tv", "Serie Tv più votate"), + Pair("$mainUrl/anno/2022/page/number", "Ultime uscite"), + ) + + override suspend fun getMainPage( + page: Int, + request : MainPageRequest + ): HomePageResponse { + val url = request.data.replace("number", page.toString()) + val soup = app.get(url, referer = url.substringBefore("page")).document + val home = soup.select("article.item").map { + val title = it.selectFirst("div.data > h3 > a")!!.text().substringBefore("(") + val link = it.selectFirst("div.poster > a")!!.attr("href") + val quality = getQualityFromString(it.selectFirst("span.quality")?.text()) + TvSeriesSearchResponse( + title, + link, + this.name, + TvType.Movie, + it.selectFirst("img")!!.attr("src"), + null, + null, + quality = quality + ) + } + return newHomePageResponse(request.name, home) + } + + override suspend fun search(query: String): List { + val queryformatted = query.replace(" ", "+") + val url = "$mainUrl?s=$queryformatted" + val doc = app.get(url,referer= mainUrl ).document + return doc.select("div.result-item").map { + val href = it.selectFirst("div.image > div > a")!!.attr("href") + val poster = it.selectFirst("div.image > div > a > img")!!.attr("src") + val name = it.selectFirst("div.details > div.title > a")!!.text().substringBefore("(") + MovieSearchResponse( + name, + href, + this.name, + TvType.Movie, + poster + ) + + } + } + + override suspend fun load(url: String): LoadResponse { + val page = app.get(url) + val document = page.document + val type = if (url.contains("film")) TvType.Movie else TvType.TvSeries + val title = document.selectFirst("div.data > h1")!!.text().substringBefore("(") + val description = document.select("#info > div.wp-content > p").html().toString() + val rating = null + var year = document.selectFirst(" div.data > div.extra > span.date")!!.text().substringAfter(",") + .filter { it.isDigit() } + if (year.length > 4) { + year = year.dropLast(4) + } + + val poster = document.selectFirst("div.poster > img")!!.attr("src") + + val recomm = document.select("#single_relacionados >article").map { + val href = it.selectFirst("a")!!.attr("href") + val posterUrl = it.selectFirst("a > img")!!.attr("src") + val name = it.selectFirst("a > img")!!.attr("alt").substringBeforeLast("(") + MovieSearchResponse( + name, + href, + this.name, + TvType.Movie, + posterUrl + ) + + } + + + if (type == TvType.TvSeries) { + + val episodeList = ArrayList() + document.select("#seasons > div").reversed().map { element -> + val season = element.selectFirst("div.se-q > span.se-t")!!.text().toInt() + element.select("div.se-a > ul > li").filter { it -> it.text()!="There are still no episodes this season" }.map{ episode -> + val href = episode.selectFirst("div.episodiotitle > a")!!.attr("href") + val epNum =episode.selectFirst("div.numerando")!!.text().substringAfter("-").filter { it.isDigit() }.toIntOrNull() + val epTitle = episode.selectFirst("div.episodiotitle > a")!!.text() + val posterUrl = episode.selectFirst("div.imagen > img")!!.attr("src") + episodeList.add( + Episode( + href, + epTitle, + season, + epNum, + posterUrl, + ) + ) + } + } + return TvSeriesLoadResponse( + title, + url, + this.name, + type, + episodeList, + fixUrlNull(poster), + year.toIntOrNull(), + description, + null, + rating, + null, + null, + mutableListOf(), + recomm + ) + } else { + val actors: List = + document.select("div.person").filter{ it -> it.selectFirst("div.img > a > img")?.attr("src")!!.contains("/no/cast.png").not()}.map { actordata -> + val actorName = actordata.selectFirst("div.data > div.name > a")!!.text() + val actorImage : String? = actordata.selectFirst("div.img > a > img")?.attr("src") + val roleActor = actordata.selectFirst("div.data > div.caracter")!!.text() + ActorData(actor = Actor(actorName, image = actorImage), roleString = roleActor ) + } + return newMovieLoadResponse( + title, + url, + type, + url + ) { + posterUrl = fixUrlNull(poster) + this.year = year.toIntOrNull() + this.plot = description + this.rating = rating + this.recommendations = recomm + this.duration = null + this.actors = actors + } + } + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + val doc = app.get(data).document + val type = if( data.contains("film") ){"movie"} else {"tv"} + val idpost=doc.select("#player-option-1").attr("data-post") + val test = app.post("$mainUrl/wp-admin/admin-ajax.php", headers = mapOf( + "content-type" to "application/x-www-form-urlencoded; charset=UTF-8", + "accept" to "*/*", + "X-Requested-With" to "XMLHttpRequest", + ), data = mapOf( + "action" to "doo_player_ajax", + "post" to idpost, + "nume" to "1", + "type" to type, + )) + + val url2= Regex("""src='((.|\\n)*?)'""").find(test.text)?.groups?.get(1)?.value.toString() + val trueUrl = app.get(url2, headers = mapOf("referer" to mainUrl)).url + loadExtractor(trueUrl, data, subtitleCallback, callback) + + return true + } +} \ No newline at end of file diff --git a/CineBlogProvider/src/main/kotlin/com/lagradost/CineBlogProviderPlugin.kt b/CineBlogProvider/src/main/kotlin/com/lagradost/CineBlogProviderPlugin.kt new file mode 100644 index 0000000..c5c1694 --- /dev/null +++ b/CineBlogProvider/src/main/kotlin/com/lagradost/CineBlogProviderPlugin.kt @@ -0,0 +1,14 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class CineBlogProviderPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(CineBlogProvider()) + } +} \ No newline at end of file diff --git a/CinecalidadProvider/build.gradle.kts b/CinecalidadProvider/build.gradle.kts new file mode 100644 index 0000000..b3c765e --- /dev/null +++ b/CinecalidadProvider/build.gradle.kts @@ -0,0 +1,26 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "es" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + // authors = listOf("Cloudburst") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "TvSeries", + "Movie", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=cinecalidad.lol&sz=%size%" +} \ No newline at end of file diff --git a/CinecalidadProvider/src/main/AndroidManifest.xml b/CinecalidadProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/CinecalidadProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/CinecalidadProvider/src/main/kotlin/com/lagradost/CinecalidadProvider.kt b/CinecalidadProvider/src/main/kotlin/com/lagradost/CinecalidadProvider.kt new file mode 100644 index 0000000..fbeca40 --- /dev/null +++ b/CinecalidadProvider/src/main/kotlin/com/lagradost/CinecalidadProvider.kt @@ -0,0 +1,258 @@ +package com.lagradost + +import com.lagradost.cloudstream3.* +// import com.lagradost.cloudstream3.extractors.Cinestart +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.loadExtractor + +class CinecalidadProvider : MainAPI() { + override var mainUrl = "https://cinecalidad.lol" + override var name = "Cinecalidad" + override var lang = "es" + override val hasMainPage = true + override val hasChromecastSupport = true + override val hasDownloadSupport = true + override val supportedTypes = setOf( + TvType.Movie, + TvType.TvSeries, + ) + override val vpnStatus = VPNStatus.MightBeNeeded //Due to evoload sometimes not loading + + override val mainPage = mainPageOf( + Pair("$mainUrl/ver-serie/page/", "Series"), + Pair("$mainUrl/page/", "Peliculas"), + Pair("$mainUrl/genero-de-la-pelicula/peliculas-en-calidad-4k/page/", "4K UHD"), + ) + + override suspend fun getMainPage( + page: Int, + request : MainPageRequest + ): HomePageResponse { + val url = request.data + page + + val soup = app.get(url).document + val home = soup.select(".item.movies").map { + val title = it.selectFirst("div.in_title")!!.text() + val link = it.selectFirst("a")!!.attr("href") + TvSeriesSearchResponse( + title, + link, + this.name, + if (link.contains("/ver-pelicula/")) TvType.Movie else TvType.TvSeries, + it.selectFirst(".poster.custom img")!!.attr("data-src"), + null, + null, + ) + } + + return newHomePageResponse(request.name, home) + } + + override suspend fun search(query: String): List { + val url = "$mainUrl/?s=${query}" + val document = app.get(url).document + + return document.select("article").map { + val title = it.selectFirst("div.in_title")!!.text() + val href = it.selectFirst("a")!!.attr("href") + val image = it.selectFirst(".poster.custom img")!!.attr("data-src") + val isMovie = href.contains("/ver-pelicula/") + + if (isMovie) { + MovieSearchResponse( + title, + href, + this.name, + TvType.Movie, + image, + null + ) + } else { + TvSeriesSearchResponse( + title, + href, + this.name, + TvType.TvSeries, + image, + null, + null + ) + } + } + } + + + override suspend fun load(url: String): LoadResponse? { + val soup = app.get(url, timeout = 120).document + + val title = soup.selectFirst(".single_left h1")!!.text() + val description = soup.selectFirst("div.single_left table tbody tr td p")?.text()?.trim() + val poster: String? = soup.selectFirst(".alignnone")!!.attr("data-src") + val episodes = soup.select("div.se-c div.se-a ul.episodios li").map { li -> + val href = li.selectFirst("a")!!.attr("href") + val epThumb = li.selectFirst("img.lazy")!!.attr("data-src") + val name = li.selectFirst(".episodiotitle a")!!.text() + val seasonid = + li.selectFirst(".numerando")!!.text().replace(Regex("(S|E)"), "").let { str -> + str.split("-").mapNotNull { subStr -> subStr.toIntOrNull() } + } + val isValid = seasonid.size == 2 + val episode = if (isValid) seasonid.getOrNull(1) else null + val season = if (isValid) seasonid.getOrNull(0) else null + Episode( + href, + name, + season, + episode, + if (epThumb.contains("svg")) null else epThumb + ) + } + return when (val tvType = + if (url.contains("/ver-pelicula/")) TvType.Movie else TvType.TvSeries) { + TvType.TvSeries -> { + TvSeriesLoadResponse( + title, + url, + this.name, + tvType, + episodes, + poster, + null, + description, + ) + } + TvType.Movie -> { + MovieLoadResponse( + title, + url, + this.name, + tvType, + url, + poster, + null, + description, + ) + } + else -> null + } + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + + val datam = app.get(data) + val doc = datam.document + val datatext = datam.text + + doc.select(".dooplay_player_option").apmap { + val url = it.attr("data-option") +// if (url.startsWith("https://cinestart.net")) { +// val extractor = Cinestart() +// extractor.getSafeUrl(url, null, subtitleCallback, callback) +// } else { + loadExtractor(url, mainUrl, subtitleCallback, callback) +// } + if (url.startsWith("https://cinecalidad.lol")) { + val cineurlregex = + Regex("(https:\\/\\/cinecalidad\\.lol\\/play\\/\\?h=[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)") + cineurlregex.findAll(url).map { + it.value.replace("/play/", "/play/r.php") + }.toList().apmap { + app.get( + it, + headers = mapOf( + "Host" to "cinecalidad.lol", + "User-Agent" to USER_AGENT, + "Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", + "Accept-Language" to "en-US,en;q=0.5", + "DNT" to "1", + "Connection" to "keep-alive", + "Referer" to data, + "Upgrade-Insecure-Requests" to "1", + "Sec-Fetch-Dest" to "iframe", + "Sec-Fetch-Mode" to "navigate", + "Sec-Fetch-Site" to "same-origin", + "Sec-Fetch-User" to "?1", + ), + allowRedirects = false + ).okhttpResponse.headers.values("location").apmap { extractedurl -> + if (extractedurl.contains("cinestart")) { + loadExtractor(extractedurl, mainUrl, subtitleCallback, callback) + } + } + } + } + } + if (datatext.contains("en castellano")) app.get("$data?ref=es").document.select(".dooplay_player_option") + .apmap { + val url = it.attr("data-option") +// if (url.startsWith("https://cinestart.net")) { +// val extractor = Cinestart() +// extractor.getSafeUrl(url, null, subtitleCallback, callback) +// } else { + loadExtractor(url, mainUrl, subtitleCallback, callback) +// } + + if (url.startsWith("https://cinecalidad.lol")) { + val cineurlregex = + Regex("(https:\\/\\/cinecalidad\\.lol\\/play\\/\\?h=[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)") + cineurlregex.findAll(url).map { + it.value.replace("/play/", "/play/r.php") + }.toList().apmap { + app.get( + it, + headers = mapOf( + "Host" to "cinecalidad.lol", + "User-Agent" to USER_AGENT, + "Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", + "Accept-Language" to "en-US,en;q=0.5", + "DNT" to "1", + "Connection" to "keep-alive", + "Referer" to data, + "Upgrade-Insecure-Requests" to "1", + "Sec-Fetch-Dest" to "iframe", + "Sec-Fetch-Mode" to "navigate", + "Sec-Fetch-Site" to "same-origin", + "Sec-Fetch-User" to "?1", + ), + allowRedirects = false + ).okhttpResponse.headers.values("location").apmap { extractedurl -> + if (extractedurl.contains("cinestart")) { + loadExtractor(extractedurl, mainUrl, subtitleCallback, callback) + } + } + } + } + } + if (datatext.contains("Subtítulo LAT") || datatext.contains("Forzados LAT")) { + doc.select("#panel_descarga.pane a").apmap { + val link = + if (data.contains("serie") || data.contains("episodio")) "${data}${it.attr("href")}" + else it.attr("href") + val docsub = app.get(link) + val linksub = docsub.document + val validsub = docsub.text + if (validsub.contains("Subtítulo") || validsub.contains("Forzados")) { + val langregex = Regex("(Subtítulo.*\$|Forzados.*\$)") + val langdoc = linksub.selectFirst("div.titulo h3")!!.text() + val reallang = langregex.find(langdoc)?.destructured?.component1() + linksub.select("a.link").apmap { + val sublink = + if (data.contains("serie") || data.contains("episodio")) "${data}${ + it.attr("href") + }" + else it.attr("href") + subtitleCallback( + SubtitleFile(reallang!!, sublink) + ) + } + } + } + } + return true + } +} diff --git a/CinecalidadProvider/src/main/kotlin/com/lagradost/CinecalidadProviderPlugin.kt b/CinecalidadProvider/src/main/kotlin/com/lagradost/CinecalidadProviderPlugin.kt new file mode 100644 index 0000000..cf4a2ca --- /dev/null +++ b/CinecalidadProvider/src/main/kotlin/com/lagradost/CinecalidadProviderPlugin.kt @@ -0,0 +1,14 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class CinecalidadProviderPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(CinecalidadProvider()) + } +} \ No newline at end of file diff --git a/CuevanaProvider/build.gradle.kts b/CuevanaProvider/build.gradle.kts new file mode 100644 index 0000000..a94342d --- /dev/null +++ b/CuevanaProvider/build.gradle.kts @@ -0,0 +1,27 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "es" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + // authors = listOf("Cloudburst") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "Anime", + "TvSeries", + "Movie", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=cuevana3.me&sz=%size%" +} \ No newline at end of file diff --git a/CuevanaProvider/src/main/AndroidManifest.xml b/CuevanaProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/CuevanaProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/CuevanaProvider/src/main/kotlin/com/lagradost/CuevanaProvider.kt b/CuevanaProvider/src/main/kotlin/com/lagradost/CuevanaProvider.kt new file mode 100644 index 0000000..9aab603 --- /dev/null +++ b/CuevanaProvider/src/main/kotlin/com/lagradost/CuevanaProvider.kt @@ -0,0 +1,324 @@ +package com.lagradost + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.mvvm.logError +import com.lagradost.cloudstream3.utils.AppUtils.parseJson +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.loadExtractor + +class CuevanaProvider : MainAPI() { + override var mainUrl = "https://cuevana3.me" + override var name = "Cuevana" + override var lang = "es" + override val hasMainPage = true + override val hasChromecastSupport = true + override val hasDownloadSupport = true + override val supportedTypes = setOf( + TvType.Movie, + TvType.TvSeries, + ) + + override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { + val items = ArrayList() + val urls = listOf( + Pair(mainUrl, "Recientemente actualizadas"), + Pair("$mainUrl/estrenos/", "Estrenos"), + ) + items.add( + HomePageList( + "Series", + app.get("$mainUrl/serie", timeout = 120).document.select("section.home-series li") + .map { + val title = it.selectFirst("h2.Title")!!.text() + val poster = it.selectFirst("img.lazy")!!.attr("data-src") + val url = it.selectFirst("a")!!.attr("href") + TvSeriesSearchResponse( + title, + url, + this.name, + TvType.Anime, + poster, + null, + null, + ) + }) + ) + for ((url, name) in urls) { + try { + val soup = app.get(url).document + val home = soup.select("section li.xxx.TPostMv").map { + val title = it.selectFirst("h2.Title")!!.text() + val link = it.selectFirst("a")!!.attr("href") + TvSeriesSearchResponse( + title, + link, + this.name, + if (link.contains("/pelicula/")) TvType.Movie else TvType.TvSeries, + it.selectFirst("img.lazy")!!.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 + + return document.select("li.xxx.TPostMv").map { + val title = it.selectFirst("h2.Title")!!.text() + val href = it.selectFirst("a")!!.attr("href") + val image = it.selectFirst("img.lazy")!!.attr("data-src") + val isSerie = href.contains("/serie/") + + if (isSerie) { + TvSeriesSearchResponse( + title, + href, + this.name, + TvType.TvSeries, + image, + null, + null + ) + } else { + MovieSearchResponse( + title, + href, + this.name, + TvType.Movie, + image, + null + ) + } + } + } + + override suspend fun load(url: String): LoadResponse? { + val soup = app.get(url, timeout = 120).document + val title = soup.selectFirst("h1.Title")!!.text() + val description = soup.selectFirst(".Description p")?.text()?.trim() + val poster: String? = soup.selectFirst(".movtv-info div.Image img")!!.attr("data-src") + val year1 = soup.selectFirst("footer p.meta").toString() + val yearRegex = Regex("(\\d+)") + val yearf = + yearRegex.find(year1)?.destructured?.component1()?.replace(Regex("|"), "") + val year = if (yearf.isNullOrBlank()) null else yearf.toIntOrNull() + val episodes = soup.select(".all-episodes li.TPostMv article").map { li -> + val href = li.select("a").attr("href") + val epThumb = + li.selectFirst("div.Image img")?.attr("data-src") ?: li.selectFirst("img.lazy")!! + .attr("data-srcc") + val seasonid = li.selectFirst("span.Year")!!.text().let { str -> + str.split("x").mapNotNull { subStr -> subStr.toIntOrNull() } + } + val isValid = seasonid.size == 2 + val episode = if (isValid) seasonid.getOrNull(1) else null + val season = if (isValid) seasonid.getOrNull(0) else null + Episode( + href, + null, + season, + episode, + fixUrl(epThumb) + ) + } + val tags = soup.select("ul.InfoList li.AAIco-adjust:contains(Genero) a").map { it.text() } + val tvType = if (episodes.isEmpty()) TvType.Movie else TvType.TvSeries + val recelement = + if (tvType == TvType.TvSeries) "main section div.series_listado.series div.xxx" + else "main section ul.MovieList li" + val recommendations = + soup.select(recelement).mapNotNull { element -> + val recTitle = element.select("h2.Title").text() ?: return@mapNotNull null + val image = element.select("figure img")?.attr("data-src") + val recUrl = fixUrl(element.select("a").attr("href")) + MovieSearchResponse( + recTitle, + recUrl, + this.name, + TvType.Movie, + image, + year = null + ) + } + + return when (tvType) { + TvType.TvSeries -> { + TvSeriesLoadResponse( + title, + url, + this.name, + tvType, + episodes, + poster, + year, + description, + tags = tags, + recommendations = recommendations + ) + } + TvType.Movie -> { + MovieLoadResponse( + title, + url, + this.name, + tvType, + url, + poster, + year, + description, + tags = tags, + recommendations = recommendations + ) + } + else -> null + } + } + + data class Femcuevana( + @JsonProperty("url") val url: String, + ) + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + app.get(data).document.select("div.TPlayer.embed_div iframe").apmap { + val iframe = fixUrl(it.attr("data-src")) + if (iframe.contains("api.cuevana3.me/fembed/")) { + val femregex = + Regex("(https.\\/\\/api\\.cuevana3\\.me\\/fembed\\/\\?h=[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)") + femregex.findAll(iframe).map { femreg -> + femreg.value + }.toList().apmap { fem -> + val key = fem.replace("https://api.cuevana3.me/fembed/?h=", "") + val url = app.post( + "https://api.cuevana3.me/fembed/api.php", + allowRedirects = false, + headers = mapOf( + "Host" to "api.cuevana3.me", + "User-Agent" to USER_AGENT, + "Accept" to "application/json, text/javascript, */*; q=0.01", + "Accept-Language" to "en-US,en;q=0.5", + "Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8", + "X-Requested-With" to "XMLHttpRequest", + "Origin" to "https://api.cuevana3.me", + "DNT" to "1", + "Connection" to "keep-alive", + "Sec-Fetch-Dest" to "empty", + "Sec-Fetch-Mode" to "cors", + "Sec-Fetch-Site" to "same-origin", + ), + data = mapOf(Pair("h", key)) + ).text + val json = parseJson(url) + val link = json.url + if (link.contains("fembed")) { + loadExtractor(link, data, subtitleCallback, callback) + } + } + } + if (iframe.contains("tomatomatela")) { + val tomatoRegex = + Regex("(\\/\\/apialfa.tomatomatela.com\\/ir\\/player.php\\?h=[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)") + tomatoRegex.findAll(iframe).map { tomreg -> + tomreg.value + }.toList().apmap { tom -> + val tomkey = tom.replace("//apialfa.tomatomatela.com/ir/player.php?h=", "") + app.post( + "https://apialfa.tomatomatela.com/ir/rd.php", allowRedirects = false, + headers = mapOf( + "Host" to "apialfa.tomatomatela.com", + "User-Agent" to USER_AGENT, + "Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", + "Accept-Language" to "en-US,en;q=0.5", + "Content-Type" to "application/x-www-form-urlencoded", + "Origin" to "null", + "DNT" to "1", + "Connection" to "keep-alive", + "Upgrade-Insecure-Requests" to "1", + "Sec-Fetch-Dest" to "iframe", + "Sec-Fetch-Mode" to "navigate", + "Sec-Fetch-Site" to "same-origin", + ), + data = mapOf(Pair("url", tomkey)) + ).okhttpResponse.headers.values("location").apmap { loc -> + if (loc.contains("goto_ddh.php")) { + val gotoregex = + Regex("(\\/\\/api.cuevana3.me\\/ir\\/goto_ddh.php\\?h=[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)") + gotoregex.findAll(loc).map { goreg -> + goreg.value.replace("//api.cuevana3.me/ir/goto_ddh.php?h=", "") + }.toList().apmap { gotolink -> + app.post( + "https://api.cuevana3.me/ir/redirect_ddh.php", + allowRedirects = false, + headers = mapOf( + "Host" to "api.cuevana3.me", + "User-Agent" to USER_AGENT, + "Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", + "Accept-Language" to "en-US,en;q=0.5", + "Content-Type" to "application/x-www-form-urlencoded", + "Origin" to "null", + "DNT" to "1", + "Connection" to "keep-alive", + "Upgrade-Insecure-Requests" to "1", + "Sec-Fetch-Dest" to "iframe", + "Sec-Fetch-Mode" to "navigate", + "Sec-Fetch-Site" to "same-origin", + ), + data = mapOf(Pair("url", gotolink)) + ).okhttpResponse.headers.values("location").apmap { golink -> + loadExtractor(golink, data, subtitleCallback, callback) + } + } + } + if (loc.contains("index.php?h=")) { + val indexRegex = + Regex("(\\/\\/api.cuevana3.me\\/sc\\/index.php\\?h=[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)") + indexRegex.findAll(loc).map { indreg -> + indreg.value.replace("//api.cuevana3.me/sc/index.php?h=", "") + }.toList().apmap { inlink -> + app.post( + "https://api.cuevana3.me/sc/r.php", allowRedirects = false, + headers = mapOf( + "Host" to "api.cuevana3.me", + "User-Agent" to USER_AGENT, + "Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", + "Accept-Language" to "en-US,en;q=0.5", + "Accept-Encoding" to "gzip, deflate, br", + "Content-Type" to "application/x-www-form-urlencoded", + "Origin" to "null", + "DNT" to "1", + "Connection" to "keep-alive", + "Upgrade-Insecure-Requests" to "1", + "Sec-Fetch-Dest" to "iframe", + "Sec-Fetch-Mode" to "navigate", + "Sec-Fetch-Site" to "same-origin", + "Sec-Fetch-User" to "?1", + ), + data = mapOf(Pair("h", inlink)) + ).okhttpResponse.headers.values("location").apmap { link -> + loadExtractor(link, data, subtitleCallback, callback) + } + } + } + } + } + } + } + return true + } +} \ No newline at end of file diff --git a/CuevanaProvider/src/main/kotlin/com/lagradost/CuevanaProviderPlugin.kt b/CuevanaProvider/src/main/kotlin/com/lagradost/CuevanaProviderPlugin.kt new file mode 100644 index 0000000..a126075 --- /dev/null +++ b/CuevanaProvider/src/main/kotlin/com/lagradost/CuevanaProviderPlugin.kt @@ -0,0 +1,14 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class CuevanaProviderPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(CuevanaProvider()) + } +} \ No newline at end of file diff --git a/DoramasYTProvider/build.gradle.kts b/DoramasYTProvider/build.gradle.kts new file mode 100644 index 0000000..687312b --- /dev/null +++ b/DoramasYTProvider/build.gradle.kts @@ -0,0 +1,26 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "es" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + // authors = listOf("Cloudburst") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "Anime", + "OVA", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=doramasyt.com&sz=%size%" +} \ No newline at end of file diff --git a/DoramasYTProvider/src/main/AndroidManifest.xml b/DoramasYTProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/DoramasYTProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/DoramasYTProvider/src/main/kotlin/com/lagradost/DoramasYTProvider.kt b/DoramasYTProvider/src/main/kotlin/com/lagradost/DoramasYTProvider.kt new file mode 100644 index 0000000..8f3b5a6 --- /dev/null +++ b/DoramasYTProvider/src/main/kotlin/com/lagradost/DoramasYTProvider.kt @@ -0,0 +1,155 @@ +package com.lagradost + +import com.lagradost.cloudstream3.* +//import com.lagradost.cloudstream3.extractors.FEmbed +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.loadExtractor +import java.util.* + + +class DoramasYTProvider : MainAPI() { + companion object { + fun getType(t: String): TvType { + return if (t.contains("OVA") || t.contains("Especial")) TvType.OVA + else if (t.contains("Pelicula")) TvType.Movie + else TvType.TvSeries + } + fun getDubStatus(title: String): DubStatus { + return if (title.contains("Latino") || title.contains("Castellano")) + DubStatus.Dubbed + else DubStatus.Subbed + } + } + + override var mainUrl = "https://doramasyt.com" + override var name = "DoramasYT" + override var lang = "es" + override val hasMainPage = true + override val hasChromecastSupport = true + override val hasDownloadSupport = true + override val supportedTypes = setOf( + TvType.AsianDrama, + ) + + override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { + val urls = listOf( + Pair("$mainUrl/emision", "En emisión"), + Pair( + "$mainUrl/doramas?categoria=pelicula&genero=false&fecha=false&letra=false", + "Peliculas" + ), + Pair("$mainUrl/doramas", "Doramas"), + Pair( + "$mainUrl/doramas?categoria=live-action&genero=false&fecha=false&letra=false", + "Live Action" + ), + ) + + val items = ArrayList() + + items.add( + HomePageList( + "Capítulos actualizados", + app.get(mainUrl, timeout = 120).document.select(".col-6").map { + val title = it.selectFirst("p")!!.text() + val poster = it.selectFirst(".chapter img")!!.attr("src") + val epRegex = Regex("episodio-(\\d+)") + val url = it.selectFirst("a")!!.attr("href").replace("ver/", "dorama/") + .replace(epRegex, "sub-espanol") + val epNum = it.selectFirst("h3")!!.text().toIntOrNull() + newAnimeSearchResponse(title,url) { + this.posterUrl = fixUrl(poster) + addDubStatus(getDubStatus(title), epNum) + } + }) + ) + + for (i in urls) { + try { + val home = app.get(i.first, timeout = 120).document.select(".col-6").map { + val title = it.selectFirst(".animedtls p")!!.text() + val poster = it.selectFirst(".anithumb img")!!.attr("src") + newAnimeSearchResponse(title, fixUrl(it.selectFirst("a")!!.attr("href"))) { + this.posterUrl = fixUrl(poster) + addDubStatus(getDubStatus(title)) + } + } + + items.add(HomePageList(i.second, home)) + } catch (e: Exception) { + e.printStackTrace() + } + } + + if (items.size <= 0) throw ErrorLoadingException() + return HomePageResponse(items) + } + + override suspend fun search(query: String): List { + return app.get("$mainUrl/buscar?q=$query", timeout = 120).document.select(".col-6").map { + val title = it.selectFirst(".animedtls p")!!.text() + val href = it.selectFirst("a")!!.attr("href") + val image = it.selectFirst(".animes img")!!.attr("src") + AnimeSearchResponse( + title, + href, + this.name, + TvType.Anime, + image, + null, + if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of( + DubStatus.Dubbed + ) else EnumSet.of(DubStatus.Subbed), + ) + } + } + + override suspend fun load(url: String): LoadResponse { + val doc = app.get(url, timeout = 120).document + val poster = doc.selectFirst("div.flimimg img.img1")!!.attr("src") + val title = doc.selectFirst("h1")!!.text() + val type = doc.selectFirst("h4")!!.text() + val description = doc.selectFirst("p.textComplete")!!.text().replace("Ver menos", "") + val genres = doc.select(".nobel a").map { it.text() } + val status = when (doc.selectFirst(".state h6")?.text()) { + "Estreno" -> ShowStatus.Ongoing + "Finalizado" -> ShowStatus.Completed + else -> null + } + val episodes = doc.select(".heromain .col-item").map { + val name = it.selectFirst(".dtlsflim p")!!.text() + val link = it.selectFirst("a")!!.attr("href") + val epThumb = it.selectFirst(".flimimg img.img1")!!.attr("src") + Episode(link, name, posterUrl = epThumb) + } + return newAnimeLoadResponse(title, url, getType(type)) { + posterUrl = poster + addEpisodes(DubStatus.Subbed, episodes) + showStatus = status + plot = description + tags = genres + } + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + app.get(data).document.select("div.playother p").apmap { + val encodedurl = it.select("p").attr("data-player") + val urlDecoded = base64Decode(encodedurl) + val url = (urlDecoded).replace("https://doramasyt.com/reproductor?url=", "") + if (url.startsWith("https://www.fembed.com")) { + val extractor = FEmbed() + extractor.getUrl(url).forEach { link -> + callback.invoke(link) + } + } else { + loadExtractor(url, mainUrl, subtitleCallback, callback) + } + } + return true + } +} \ No newline at end of file diff --git a/DoramasYTProvider/src/main/kotlin/com/lagradost/DoramasYTProviderPlugin.kt b/DoramasYTProvider/src/main/kotlin/com/lagradost/DoramasYTProviderPlugin.kt new file mode 100644 index 0000000..e5652d9 --- /dev/null +++ b/DoramasYTProvider/src/main/kotlin/com/lagradost/DoramasYTProviderPlugin.kt @@ -0,0 +1,14 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class DoramasYTProviderPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(DoramasYTProvider()) + } +} \ No newline at end of file diff --git a/DoramasYTProvider/src/main/kotlin/com/lagradost/XStreamCdn.kt b/DoramasYTProvider/src/main/kotlin/com/lagradost/XStreamCdn.kt new file mode 100644 index 0000000..866aee0 --- /dev/null +++ b/DoramasYTProvider/src/main/kotlin/com/lagradost/XStreamCdn.kt @@ -0,0 +1,67 @@ +package com.lagradost + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.utils.AppUtils +import com.lagradost.cloudstream3.utils.ExtractorApi +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.getQualityFromName + +class FEmbed: XStreamCdn() { + override val name: String = "FEmbed" + override val mainUrl: String = "https://www.fembed.com" +} + +open class XStreamCdn : ExtractorApi() { + override val name: String = "XStreamCdn" + override val mainUrl: String = "https://embedsito.com" + override val requiresReferer = false + open var domainUrl: String = "embedsito.com" + + private data class ResponseData( + @JsonProperty("file") val file: String, + @JsonProperty("label") val label: String, + //val type: String // Mp4 + ) + + private data class ResponseJson( + @JsonProperty("success") val success: Boolean, + @JsonProperty("data") val data: List? + ) + + override fun getExtractorUrl(id: String): String { + return "$domainUrl/api/source/$id" + } + + override suspend fun getUrl(url: String, referer: String?): List { + val headers = mapOf( + "Referer" to url, + "User-Agent" to "Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0", + ) + val id = url.trimEnd('/').split("/").last() + val newUrl = "https://${domainUrl}/api/source/${id}" + val extractedLinksList: MutableList = mutableListOf() + with(app.post(newUrl, headers = headers)) { + if (this.code != 200) return listOf() + val text = this.text + if (text.isEmpty()) return listOf() + if (text == """{"success":false,"data":"Video not found or has been removed"}""") return listOf() + AppUtils.parseJson(text)?.let { + if (it.success && it.data != null) { + it.data.forEach { data -> + extractedLinksList.add( + ExtractorLink( + name, + name = name, + data.file, + url, + getQualityFromName(data.label), + ) + ) + } + } + } + } + return extractedLinksList + } +} \ No newline at end of file diff --git a/DramaidProvider/build.gradle.kts b/DramaidProvider/build.gradle.kts new file mode 100644 index 0000000..3fc2c3a --- /dev/null +++ b/DramaidProvider/build.gradle.kts @@ -0,0 +1,26 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "id" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + // authors = listOf("Cloudburst") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "AsianDrama", + "Movie", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=185.224.83.103&sz=%size%" +} \ No newline at end of file diff --git a/DramaidProvider/src/main/AndroidManifest.xml b/DramaidProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/DramaidProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/DramaidProvider/src/main/kotlin/com/lagradost/DramaidProvider.kt b/DramaidProvider/src/main/kotlin/com/lagradost/DramaidProvider.kt new file mode 100644 index 0000000..318a5ca --- /dev/null +++ b/DramaidProvider/src/main/kotlin/com/lagradost/DramaidProvider.kt @@ -0,0 +1,213 @@ +package com.lagradost + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.getQualityFromName +import com.lagradost.cloudstream3.utils.loadExtractor +import org.jsoup.Jsoup +import org.jsoup.nodes.Element + +class DramaidProvider : MainAPI() { + override var mainUrl = "https://185.224.83.103" + override var name = "DramaId" + override val hasQuickSearch = false + override val hasMainPage = true + override var lang = "id" + override val hasDownloadSupport = true + override val hasChromecastSupport = false + override val supportedTypes = setOf(TvType.AsianDrama) + + companion object { + fun getStatus(t: String): ShowStatus { + return when (t) { + "Completed" -> ShowStatus.Completed + "Ongoing" -> ShowStatus.Ongoing + else -> ShowStatus.Completed + } + } + } + + override val mainPage = mainPageOf( + "&status=&type=&order=update" to "Drama Terbaru", + "&order=latest" to "Baru Ditambahkan", + "&status=&type=&order=popular" to "Drama Popular", + ) + + override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { + val document = app.get("$mainUrl/series/?page=$page${request.data}").document + val home = document.select("article[itemscope=itemscope]").mapNotNull { + it.toSearchResult() + } + return newHomePageResponse(request.name, home) + } + + private fun getProperDramaLink(uri: String): String { + return if (uri.contains("/series/")) { + uri + } else { + "$mainUrl/series/" + Regex("$mainUrl/(.+)-ep.+").find(uri)?.groupValues?.get(1) + .toString() + } + } + + private fun Element.toSearchResult(): SearchResponse? { + val href = getProperDramaLink(this.selectFirst("a.tip")!!.attr("href")) + val title = this.selectFirst("h2[itemprop=headline]")?.text()?.trim() ?: return null + val posterUrl = fixUrlNull(this.selectFirst(".limit > noscript > img")?.attr("src")) + + return newTvSeriesSearchResponse(title, href, TvType.AsianDrama) { + this.posterUrl = posterUrl + } + } + + override suspend fun search(query: String): List { + val link = "$mainUrl/?s=$query" + val document = app.get(link).document + + return document.select("article[itemscope=itemscope]").map { + val title = it.selectFirst("h2[itemprop=headline]")!!.text().trim() + val poster = it.selectFirst(".limit > noscript > img")!!.attr("src") + val href = it.selectFirst("a.tip")!!.attr("href") + + newTvSeriesSearchResponse(title, href, TvType.AsianDrama) { + this.posterUrl = poster + } + } + } + + override suspend fun load(url: String): LoadResponse { + val document = app.get(url).document + + val title = document.selectFirst("h1.entry-title")!!.text().trim() + val poster = document.select(".thumb > noscript > img").attr("src") + val tags = document.select(".genxed > a").map { it.text() } + + val year = Regex("\\d, ([0-9]*)").find( + document.selectFirst(".info-content > .spe > span > time")!!.text().trim() + )?.groupValues?.get(1).toString().toIntOrNull() + val status = getStatus( + document.select(".info-content > .spe > span:nth-child(1)") + .text().trim().replace("Status: ", "") + ) + val description = document.select(".entry-content > p").text().trim() + + val episodes = document.select(".eplister > ul > li").map { + val name = it.selectFirst("a > .epl-title")!!.text().trim() + val link = it.select("a").attr("href") + val epNum = it.selectFirst("a > .epl-num")!!.text().trim().toIntOrNull() + newEpisode(link) { + this.name = name + this.episode = epNum + } + }.reversed() + + val recommendations = + document.select(".listupd > article[itemscope=itemscope]").map { rec -> + val epTitle = rec.selectFirst("h2[itemprop=headline]")!!.text().trim() + val epPoster = rec.selectFirst(".limit > noscript > img")!!.attr("src") + val epHref = fixUrl(rec.selectFirst("a.tip")!!.attr("href")) + + newTvSeriesSearchResponse(epTitle, epHref, TvType.AsianDrama) { + this.posterUrl = epPoster + } + } + + if (episodes.size == 1) { + return newMovieLoadResponse(title, url, TvType.Movie, episodes[0].data) { + posterUrl = poster + this.year = year + plot = description + this.tags = tags + this.recommendations = recommendations + } + } else { + return newTvSeriesLoadResponse(title, url, TvType.AsianDrama, episodes = episodes) { + posterUrl = poster + this.year = year + showStatus = status + plot = description + this.tags = tags + this.recommendations = recommendations + } + } + + } + + private data class Sources( + @JsonProperty("file") val file: String, + @JsonProperty("label") val label: String, + @JsonProperty("type") val type: String, + @JsonProperty("default") val default: Boolean? + ) + + private data class Tracks( + @JsonProperty("file") val file: String, + @JsonProperty("label") val label: String, + @JsonProperty("kind") val type: String, + @JsonProperty("default") val default: Boolean? + ) + + private suspend fun invokeDriveSource( + url: String, + name: String, + subCallback: (SubtitleFile) -> Unit, + sourceCallback: (ExtractorLink) -> Unit + ) { + val server = app.get(url).document.selectFirst(".picasa")?.nextElementSibling()?.data() + + val source = "[${server!!.substringAfter("sources: [").substringBefore("],")}]".trimIndent() + val trackers = server.substringAfter("tracks:[").substringBefore("],") + .replace("//language", "") + .replace("file", "\"file\"") + .replace("label", "\"label\"") + .replace("kind", "\"kind\"").trimIndent() + + tryParseJson>(source)?.map { + sourceCallback( + ExtractorLink( + name, + "Drive", + fixUrl(it.file), + referer = "https://motonews.club/", + quality = getQualityFromName(it.label) + ) + ) + } + + tryParseJson(trackers)?.let { + subCallback.invoke( + SubtitleFile( + if (it.label.contains("Indonesia")) "${it.label}n" else it.label, + it.file + ) + ) + } + + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + val document = app.get(data).document + val sources = document.select(".mobius > .mirror > option").mapNotNull { + fixUrl(Jsoup.parse(base64Decode(it.attr("value"))).select("iframe").attr("src")) + } + + sources.map { + it.replace("https://ndrama.xyz", "https://www.fembed.com") + }.apmap { + when { + it.contains("motonews.club") -> invokeDriveSource(it, this.name, subtitleCallback, callback) + else -> loadExtractor(it, data, subtitleCallback, callback) + } + } + + return true + } + +} diff --git a/DramaidProvider/src/main/kotlin/com/lagradost/DramaidProviderPlugin.kt b/DramaidProvider/src/main/kotlin/com/lagradost/DramaidProviderPlugin.kt new file mode 100644 index 0000000..4ee73cb --- /dev/null +++ b/DramaidProvider/src/main/kotlin/com/lagradost/DramaidProviderPlugin.kt @@ -0,0 +1,14 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class DramaidProviderPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(DramaidProvider()) + } +} \ No newline at end of file diff --git a/DubokuProvider/build.gradle.kts b/DubokuProvider/build.gradle.kts new file mode 100644 index 0000000..065e848 --- /dev/null +++ b/DubokuProvider/build.gradle.kts @@ -0,0 +1,27 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "zh" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + // authors = listOf("Cloudburst") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "AsianDrama", + "TvSeries", + "Movie", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=www.duboku.tv&sz=%size%" +} \ No newline at end of file diff --git a/DubokuProvider/src/main/AndroidManifest.xml b/DubokuProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/DubokuProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/DubokuProvider/src/main/kotlin/com/lagradost/DubokuProvider.kt b/DubokuProvider/src/main/kotlin/com/lagradost/DubokuProvider.kt new file mode 100644 index 0000000..a71d781 --- /dev/null +++ b/DubokuProvider/src/main/kotlin/com/lagradost/DubokuProvider.kt @@ -0,0 +1,133 @@ +package com.lagradost + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.LoadResponse.Companion.addActors +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.M3u8Helper +import org.jsoup.nodes.Element + +class DubokuProvider : MainAPI() { + override var mainUrl = "https://www.duboku.tv" + override var name = "Duboku" + override val hasMainPage = true + override var lang = "zh" + override val hasDownloadSupport = true + override val supportedTypes = setOf( + TvType.Movie, + TvType.TvSeries, + TvType.AsianDrama, + ) + + override val mainPage = mainPageOf( + "$mainUrl/vodshow/2--time------" to "连续剧 时间", + "$mainUrl/vodshow/2--hits------" to "连续剧 人气", + "$mainUrl/vodshow/13--time------" to "陆剧 时间", + "$mainUrl/vodshow/13--hits------" to "陆剧 人气", + "$mainUrl/vodshow/15--time------" to "日韩剧 时间", + "$mainUrl/vodshow/15--hits------" to "日韩剧 人气", + "$mainUrl/vodshow/21--time------" to "短剧 时间", + "$mainUrl/vodshow/21--hits------" to "短剧 人气", + "$mainUrl/vodshow/16--time------" to "英美剧 时间", + "$mainUrl/vodshow/16--hits------" to "英美剧 人气", + "$mainUrl/vodshow/14--time------" to "台泰剧 时间", + "$mainUrl/vodshow/14--hits------" to "台泰剧 人气", + "$mainUrl/vodshow/20--time------" to "港剧 时间", + "$mainUrl/vodshow/20--hits------" to "港剧 人气", + ) + + override suspend fun getMainPage( + page: Int, + request: MainPageRequest + ): HomePageResponse { + val document = app.get("${request.data}$page---.html").document + val home = document.select("ul.myui-vodlist.clearfix li").mapNotNull { + it.toSearchResult() + } + return newHomePageResponse(request.name, home) + } + + private fun Element.toSearchResult(): SearchResponse? { + val title = this.selectFirst("h4.title a")?.text()?.trim() ?: return null + val href = fixUrl(this.selectFirst("a")?.attr("href").toString()) + val posterUrl = fixUrlNull(this.selectFirst("a")?.attr("data-original")) + val episode = this.selectFirst("span.pic-text.text-right")?.text()?.filter { it.isDigit() } + ?.toIntOrNull() + + return newAnimeSearchResponse(title, href, TvType.Movie) { + this.posterUrl = posterUrl + addSub(episode) + } + } + + override suspend fun search(query: String): List { + val document = app.get("$mainUrl/vodsearch/-------------.html?wd=$query&submit=").document + + return document.select("ul#searchList li").mapNotNull { + it.toSearchResult() + } + } + + override suspend fun load(url: String): LoadResponse? { + val document = app.get(url).document + + val title = document.selectFirst("h1.title")?.text()?.trim() ?: return null + val tvType = if (document.select("ul.myui-content__list li").size == 1 + ) TvType.Movie else TvType.TvSeries + val actors = document.select("p.data")[2].select("a").map { it.text() } + + val episodes = document.select("ul.myui-content__list li").map { + val href = fixUrl(it.select("a").attr("href")) + val name = it.select("a").text().trim() + Episode( + data = href, + name = name, + ) + } + return newTvSeriesLoadResponse(title, url, tvType, episodes) { + this.posterUrl = fixUrlNull( + document.selectFirst("a.myui-vodlist__thumb.picture img")?.attr("data-original") + ) + this.year = + document.select("p.data")[0].select("a").last()?.text()?.trim()?.toIntOrNull() + this.plot = document.selectFirst("span.sketch.content")?.text()?.trim() + this.tags = document.select("p.data")[0].select("a").map { it.text() } + this.rating = document.select("div#rating span.branch").text().toRatingInt() + addActors(actors) + } + + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + + app.get(data).document.select("script").map { script -> + if (script.data().contains("var player_data={")) { + val dataJson = + script.data().substringAfter("var player_data={").substringBefore("}") + tryParseJson("{$dataJson}")?.let { source -> + M3u8Helper.generateM3u8( + this.name, + source.url ?: return@map, + referer = "https://w.duboku.io/", + headers = mapOf("Origin" to "https://w.duboku.io") + ).forEach(callback) + } + } + } + + + return true + } + + data class Sources( + @JsonProperty("url") val url: String?, + ) + + +} \ No newline at end of file diff --git a/DubokuProvider/src/main/kotlin/com/lagradost/DubokuProviderPlugin.kt b/DubokuProvider/src/main/kotlin/com/lagradost/DubokuProviderPlugin.kt new file mode 100644 index 0000000..4c77005 --- /dev/null +++ b/DubokuProvider/src/main/kotlin/com/lagradost/DubokuProviderPlugin.kt @@ -0,0 +1,14 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class DubokuProviderPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(DubokuProvider()) + } +} \ No newline at end of file diff --git a/EgyBestProvider/build.gradle.kts b/EgyBestProvider/build.gradle.kts new file mode 100644 index 0000000..6dcfa6f --- /dev/null +++ b/EgyBestProvider/build.gradle.kts @@ -0,0 +1,27 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "ar" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + // authors = listOf("Cloudburst") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "Anime", + "TvSeries", + "Movie", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=www.egy.best&sz=%size%" +} \ 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..29aec9d --- /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/lagradost/EgyBestProvider.kt b/EgyBestProvider/src/main/kotlin/com/lagradost/EgyBestProvider.kt new file mode 100644 index 0000000..933b0f2 --- /dev/null +++ b/EgyBestProvider/src/main/kotlin/com/lagradost/EgyBestProvider.kt @@ -0,0 +1,237 @@ +package com.lagradost + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.AppUtils.parseJson +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer +import org.jsoup.nodes.Element + +class EgyBestProvider : 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@EgyBestProvider.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 + ) + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + /*val baseURL = data.split("/")[0] + "//" + data.split("/")[2] + val episodeSoup = app.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 { + callback.invoke(ExtractorLink(this.name,this.name,it,"",Qualities.Unknown.value,it.contains(".m3u8"))) + } ?: run { + var jsCode = videoSoup.select("script")[1].data() + + val verificationToken = Regex("{'[0-9a-zA-Z_]*':'ok'}").findAll(jsCode)[0][2:-7] + val encodedAdLinkVar = Regex("([0-9a-zA-Z_]{2,12}\[Math").findAll(jsCode)[0][1:-5] + val encodingArraysRegEx = Regex(",[0-9a-zA-Z_]{2,12}=\[\]").findAll(jsCode) + val firstEncodingArray = encodingArraysRegEx[1][1:-3] + val secondEncodingArray = encodingArraysRegEx[2][1:-3] + + jsCode = Regex("^