diff --git a/README.md b/README.md index d72962c7..bd3202a1 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ It merely scrapes 3rd-party websites that are publicly accessable via any regula - [pelismart.com](https://pelismart.com) - [gogoanime.wiki](https://gogoanime.wiki) - [allanime.site](https://allanime.site) +- [animekisa.in](https://animekisa.in) - [animeflick.net](https://animeflick.net) - [m.animeflv.net](https://m.animeflv.net) - [tenshi.moe](https://tenshi.moe) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index 537e041d..f60cbba6 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -34,6 +34,7 @@ object APIHolder { PeliSmartProvider(), GogoanimeProvider(), AllAnimeProvider(), + AnimekisaProvider(), //ShiroProvider(), // v2 fucked me //AnimePaheProvider(), //ddos guard AnimeFlickProvider(), diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimekisaProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimekisaProvider.kt new file mode 100644 index 00000000..d108b4e8 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimekisaProvider.kt @@ -0,0 +1,127 @@ +package com.lagradost.cloudstream3.animeproviders + +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 org.jsoup.Jsoup +import java.util.* +import kotlin.collections.ArrayList + + +class AnimekisaProvider : MainAPI() { + + override val mainUrl = "https://animekisa.in" + override val name = "Animekisa" + override val hasMainPage = true + override val hasChromecastSupport = true + override val hasDownloadSupport = true + override val supportedTypes = setOf( + TvType.AnimeMovie, + TvType.OVA, + TvType.Anime, + ) + + data class Response ( + @JsonProperty("html") val html: String + ) + + override suspend fun getMainPage(): HomePageResponse { + val urls = listOf( + Pair("$mainUrl/ajax/list/views?type=all", "All animes"), + Pair("$mainUrl/ajax/list/views?type=day", "Trending now"), + Pair("$mainUrl/ajax/list/views?type=week", "Trending by week"), + Pair("$mainUrl/ajax/list/views?type=month", "Trending by month"), + + ) + + val items = ArrayList() + + for ((url, name) in urls) { + try { + val home = Jsoup.parse( + parseJson( + app.get( + url + ).text + ).html + ).select("div.flw-item").map { + val title = it.selectFirst("h3.title a").text() + val link = it.selectFirst("a").attr("href") + val poster = it.selectFirst("img.lazyload").attr("data-src") + AnimeSearchResponse( + title, + link, + this.name, + TvType.Anime, + poster, + null, + if (title.contains("(DUB)") || title.contains("(Dub)")) EnumSet.of( + DubStatus.Dubbed + ) else EnumSet.of(DubStatus.Subbed), + ) + } + + items.add(HomePageList(name, 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/search/?keyword=$query").document.select("div.flw-item").map { + val title = it.selectFirst("h3 a").text() + val url = it.selectFirst("a.film-poster-ahref").attr("href") + .replace("watch/","anime/").replace(Regex("(-episode-(\\d+)\\/\$|-episode-(\\d+)\$|-episode-full|-episode-.*-.(\\/|))"),"") + val poster = it.selectFirst(".film-poster img").attr("data-src") + AnimeSearchResponse( + title, + url, + this.name, + TvType.Anime, + poster, + null, + if (title.contains("(DUB)") || title.contains("(Dub)")) EnumSet.of( + DubStatus.Dubbed + ) else EnumSet.of(DubStatus.Subbed), + ) + }.toList() + } + override suspend fun load(url: String): LoadResponse { + val doc = app.get(url, timeout = 120).document + val poster = doc.selectFirst(".mb-2 img").attr("src") ?: doc.selectFirst("head meta[property=og:image]").attr("content") + val title = doc.selectFirst("h1.heading-name a").text() + val description = doc.selectFirst("div.description p").text().trim() + val genres = doc.select("div.row-line a").map { it.text() } + val test = if (doc.selectFirst("div.dp-i-c-right").toString().contains("Airing")) ShowStatus.Ongoing else ShowStatus.Completed + val episodes = doc.select("div.tab-content ul li.nav-item").map { + val link = it.selectFirst("a").attr("href") + AnimeEpisode(link) + } + val type = if (doc.selectFirst(".dp-i-stats").toString().contains("Movies")) TvType.AnimeMovie else TvType.Anime + return newAnimeLoadResponse(title, url, type) { + posterUrl = poster + addEpisodes(DubStatus.Subbed, episodes) + showStatus = test + plot = description + tags = genres + } + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + app.get(data).document.select("#servers-list ul.nav li a").apmap { + val server = it.attr("data-embed") + loadExtractor(server, data, callback) + } + return true + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/WcoProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/WcoProvider.kt index 7f0f99fd..5f78caa2 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/WcoProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/WcoProvider.kt @@ -1,6 +1,7 @@ package com.lagradost.cloudstream3.animeproviders import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.extractors.Mcloud import com.lagradost.cloudstream3.extractors.WcoStream import com.lagradost.cloudstream3.utils.ExtractorLink import org.json.JSONObject @@ -242,6 +243,7 @@ class WcoProvider : MainAPI() { for (server in servers) { WcoStream().getSafeUrl(server["link"].toString(), "")?.forEach(callback) + Mcloud().getSafeUrl(server["link"].toString(), "")?.forEach(callback) } return true } diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Mcloud.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Mcloud.kt new file mode 100644 index 00000000..c47d9b70 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Mcloud.kt @@ -0,0 +1,75 @@ +package com.lagradost.cloudstream3.extractors + +import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.app +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.USER_AGENT +import com.lagradost.cloudstream3.apmap +import com.lagradost.cloudstream3.utils.AppUtils.parseJson + + +open class Mcloud : ExtractorApi() { + override val name = "Mcloud" + override val mainUrl = "https://mcloud.to" + override val requiresReferer = true + val headers = mapOf( + "Host" to "mcloud.to", + "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", + "Upgrade-Insecure-Requests" to "1", + "Sec-Fetch-Dest" to "iframe", + "Sec-Fetch-Mode" to "navigate", + "Sec-Fetch-Site" to "cross-site", + "Referer" to "https://animekisa.in/", //Referer works for wco and animekisa, probably with others too + "Pragma" to "no-cache", + "Cache-Control" to "no-cache",) + override suspend fun getUrl(url: String, referer: String?): List? { + val link = url.replace("$mainUrl/e/","$mainUrl/info/") + val response = app.get(link, headers = headers).text + + data class Sources ( + @JsonProperty("file") val file: String + ) + + data class Media ( + @JsonProperty("sources") val sources: List + ) + + data class JsonMcloud ( + @JsonProperty("success") val success: Boolean, + @JsonProperty("media") val media: Media, + ) + + val mapped = response.let { parseJson(it) } + val sources = mutableListOf() + + if (mapped.success) + mapped.media.sources.apmap { + if (it.file.contains("m3u8")) { + M3u8Helper().m3u8Generation( + M3u8Helper.M3u8Stream( + it.file, + headers = app.get(url).headers.toMap() + ), true + ) + .map { stream -> + val qualityString = if ((stream.quality ?: 0) == 0) "" else "${stream.quality}p" + sources.add( + ExtractorLink( + name, + "$name $qualityString", + stream.streamUrl, + url, + getQualityFromName(stream.quality.toString()), + true + ) + ) + } + } + } + return sources + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/WcoStream.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/WcoStream.kt index 1755066b..c17d9c19 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/WcoStream.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/WcoStream.kt @@ -7,7 +7,7 @@ import com.lagradost.cloudstream3.mapper import com.lagradost.cloudstream3.utils.* class WcoStream : ExtractorApi() { - override val name = "WcoStream" + override val name = "VidStream" //Cause works for animekisa and wco override val mainUrl = "https://vidstream.pro" override val requiresReferer = false private val hlsHelper = M3u8Helper() diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt index d6be7ba9..040bc694 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -97,6 +97,7 @@ val extractorApis: Array = arrayOf( Mp4Upload(), StreamTape(), MixDrop(), + Mcloud(), XStreamCdn(), StreamSB(), Streamhub(),