diff --git a/AllAnimeProvider/build.gradle.kts b/AllAnimeProvider/build.gradle.kts deleted file mode 100644 index 7bb2284..0000000 --- a/AllAnimeProvider/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=allanime.site&sz=24" -} \ No newline at end of file diff --git a/AllAnimeProvider/src/main/AndroidManifest.xml b/AllAnimeProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/AllAnimeProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/AllAnimeProvider/src/main/kotlin/com/lagradost/AllAnimeProvider.kt b/AllAnimeProvider/src/main/kotlin/com/lagradost/AllAnimeProvider.kt deleted file mode 100644 index 6a340e5..0000000 --- a/AllAnimeProvider/src/main/kotlin/com/lagradost/AllAnimeProvider.kt +++ /dev/null @@ -1,404 +0,0 @@ -package com.lagradost - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addActors -import com.lagradost.cloudstream3.mvvm.safeApiCall -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 -import org.jsoup.Jsoup -import org.mozilla.javascript.Context -import org.mozilla.javascript.Scriptable -import java.net.URI -import java.net.URLDecoder - - -class AllAnimeProvider : MainAPI() { - override var mainUrl = "https://allanime.site" - override var name = "AllAnime" - override val hasQuickSearch = false - override val hasMainPage = true - - private fun getStatus(t: String): ShowStatus { - return when (t) { - "Finished" -> ShowStatus.Completed - "Releasing" -> ShowStatus.Ongoing - else -> ShowStatus.Completed - } - } - - override val supportedTypes = setOf(TvType.Anime, TvType.AnimeMovie) - - private data class Data( - @JsonProperty("shows") val shows: Shows - ) - - private data class Shows( - @JsonProperty("pageInfo") val pageInfo: PageInfo, - @JsonProperty("edges") val edges: List, - @JsonProperty("__typename") val _typename: String - ) - - private data class Edges( - @JsonProperty("_id") val Id: String?, - @JsonProperty("name") val name: String, - @JsonProperty("englishName") val englishName: String?, - @JsonProperty("nativeName") val nativeName: String?, - @JsonProperty("thumbnail") val thumbnail: String?, - @JsonProperty("type") val type: String?, - @JsonProperty("season") val season: Season?, - @JsonProperty("score") val score: Double?, - @JsonProperty("airedStart") val airedStart: AiredStart?, - @JsonProperty("availableEpisodes") val availableEpisodes: AvailableEpisodes?, - @JsonProperty("availableEpisodesDetail") val availableEpisodesDetail: AvailableEpisodesDetail?, - @JsonProperty("studios") val studios: List?, - @JsonProperty("description") val description: String?, - @JsonProperty("status") val status: String?, - ) - - private data class AvailableEpisodes( - @JsonProperty("sub") val sub: Int, - @JsonProperty("dub") val dub: Int, - @JsonProperty("raw") val raw: Int - ) - - private data class AiredStart( - @JsonProperty("year") val year: Int, - @JsonProperty("month") val month: Int, - @JsonProperty("date") val date: Int - ) - - private data class Season( - @JsonProperty("quarter") val quarter: String, - @JsonProperty("year") val year: Int - ) - - private data class PageInfo( - @JsonProperty("total") val total: Int, - @JsonProperty("__typename") val _typename: String - ) - - private data class AllAnimeQuery( - @JsonProperty("data") val data: Data - ) - - data class RandomMain( - @JsonProperty("data") var data: DataRan? = DataRan() - ) - - data class DataRan( - @JsonProperty("queryRandomRecommendation") var queryRandomRecommendation: ArrayList = arrayListOf() - ) - - data class QueryRandomRecommendation( - @JsonProperty("_id") val Id: String? = null, - @JsonProperty("name") val name: String? = null, - @JsonProperty("englishName") val englishName: String? = null, - @JsonProperty("nativeName") val nativeName: String? = null, - @JsonProperty("thumbnail") val thumbnail: String? = null, - @JsonProperty("airedStart") val airedStart: String? = null, - @JsonProperty("availableChapters") val availableChapters: String? = null, - @JsonProperty("availableEpisodes") val availableEpisodes: String? = null, - @JsonProperty("__typename") val _typename: String? = null - ) - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val items = ArrayList() - val urls = listOf( -// Pair( -// "Top Anime", -// """$mainUrl/graphql?variables={"type":"anime","size":30,"dateRange":30}&extensions={"persistedQuery":{"version":1,"sha256Hash":"276d52ba09ca48ce2b8beb3affb26d9d673b22f9d1fd4892aaa39524128bc745"}}""" -// ), - // "countryOrigin":"JP" for Japanese only - Pair( - "Recently updated", - """$mainUrl/graphql?variables={"search":{"allowAdult":false,"allowUnknown":false},"limit":30,"page":1,"translationType":"dub","countryOrigin":"ALL"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"d2670e3e27ee109630991152c8484fce5ff5e280c523378001f9a23dc1839068"}}""" - ), - ) - - val random = - """$mainUrl/graphql?variables={"format":"anime"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"21ac672633498a3698e8f6a93ce6c2b3722b29a216dcca93363bf012c360cd54"}}""" - val ranlink = app.get(random).text - val jsonran = parseJson(ranlink) - val ranhome = jsonran.data?.queryRandomRecommendation?.map { - newAnimeSearchResponse(it.name!!, "$mainUrl/anime/${it.Id}", fix = false) { - this.posterUrl = it.thumbnail - this.otherName = it.nativeName - } - } - - items.add(HomePageList("Random", ranhome!!)) - - urls.apmap { (HomeName, url) -> - val test = app.get(url).text - val json = parseJson(test) - val home = ArrayList() - val results = json.data.shows.edges.filter { - // filtering in case there is an anime with 0 episodes available on the site. - !(it.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0) - } - results.map { - home.add( - newAnimeSearchResponse(it.name, "$mainUrl/anime/${it.Id}", fix = false) { - this.posterUrl = it.thumbnail - this.year = it.airedStart?.year - this.otherName = it.englishName - addDub(it.availableEpisodes?.dub) - addSub(it.availableEpisodes?.sub) - }) - } - items.add(HomePageList(HomeName, home)) - } - - if (items.size <= 0) throw ErrorLoadingException() - return HomePageResponse(items) - } - - override suspend fun search(query: String): List { - val link = - """ $mainUrl/graphql?variables={"search":{"allowAdult":false,"allowUnknown":false,"query":"$query"},"limit":26,"page":1,"translationType":"dub","countryOrigin":"ALL"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"d2670e3e27ee109630991152c8484fce5ff5e280c523378001f9a23dc1839068"}}""" - var res = app.get(link).text - if (res.contains("PERSISTED_QUERY_NOT_FOUND")) { - res = app.get(link).text - if (res.contains("PERSISTED_QUERY_NOT_FOUND")) return emptyList() - } - val response = parseJson(res) - - val results = response.data.shows.edges.filter { - // filtering in case there is an anime with 0 episodes available on the site. - !(it.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0) - } - - return results.map { - newAnimeSearchResponse(it.name, "$mainUrl/anime/${it.Id}", fix = false) { - this.posterUrl = it.thumbnail - this.year = it.airedStart?.year - this.otherName = it.englishName - addDub(it.availableEpisodes?.dub) - addSub(it.availableEpisodes?.sub) - } - } - } - - private data class AvailableEpisodesDetail( - @JsonProperty("sub") val sub: List, - @JsonProperty("dub") val dub: List, - @JsonProperty("raw") val raw: List - ) - - - override suspend fun load(url: String): LoadResponse? { - val rhino = Context.enter() - rhino.initStandardObjects() - rhino.optimizationLevel = -1 - val scope: Scriptable = rhino.initStandardObjects() - - val html = app.get(url).text - val soup = Jsoup.parse(html) - - val script = soup.select("script").firstOrNull { - it.html().contains("window.__NUXT__") - } ?: return null - - val js = """ - const window = {} - ${script.html()} - const returnValue = JSON.stringify(window.__NUXT__.fetch[0].show) - """.trimIndent() - - rhino.evaluateString(scope, js, "JavaScript", 1, null) - val jsEval = scope.get("returnValue", scope) ?: return null - val showData = parseJson(jsEval as String) - - val title = showData.name - val description = showData.description - val poster = showData.thumbnail - - val episodes = showData.availableEpisodes.let { - if (it == null) return@let Pair(null, null) - Pair(if (it.sub != 0) ((1..it.sub).map { epNum -> - Episode( - "$mainUrl/anime/${showData.Id}/episodes/sub/$epNum", episode = epNum - ) - }) else null, if (it.dub != 0) ((1..it.dub).map { epNum -> - Episode( - "$mainUrl/anime/${showData.Id}/episodes/dub/$epNum", episode = epNum - ) - }) else null) - } - - val characters = soup.select("div.character > div.card-character-box").mapNotNull { - val img = it?.selectFirst("img")?.attr("src") ?: return@mapNotNull null - val name = it.selectFirst("div > a")?.ownText() ?: return@mapNotNull null - val role = when (it.selectFirst("div > .text-secondary")?.text()?.trim()) { - "Main" -> ActorRole.Main - "Supporting" -> ActorRole.Supporting - "Background" -> ActorRole.Background - else -> null - } - Pair(Actor(name, img), role) - } - - // bruh, they use graphql - //val recommendations = soup.select("#suggesction > div > div.p > .swipercard")?.mapNotNull { - // val recTitle = it?.selectFirst(".showname > a") ?: return@mapNotNull null - // val recName = recTitle.text() ?: return@mapNotNull null - // val href = fixUrlNull(recTitle.attr("href")) ?: return@mapNotNull null - // val img = it.selectFirst(".image > img").attr("src") ?: return@mapNotNull null - // AnimeSearchResponse(recName, href, this.name, TvType.Anime, img) - //} - - return newAnimeLoadResponse(title, url, TvType.Anime) { - posterUrl = poster - year = showData.airedStart?.year - - addEpisodes(DubStatus.Subbed, episodes.first) - addEpisodes(DubStatus.Dubbed, episodes.second) - addActors(characters) - //this.recommendations = recommendations - - showStatus = getStatus(showData.status.toString()) - - plot = description?.replace(Regex("""<(.*?)>"""), "") - } - } - - private val embedBlackList = listOf( - "https://mp4upload.com/", - "https://streamsb.net/", - "https://dood.to/", - "https://videobin.co/", - "https://ok.ru", - "https://streamlare.com", - ) - - private fun embedIsBlacklisted(url: String): Boolean { - embedBlackList.forEach { - if (it.javaClass.name == "kotlin.text.Regex") { - if ((it as Regex).matches(url)) { - return true - } - } else { - if (url.contains(it)) { - return true - } - } - } - return false - } - - private fun String.sanitize(): String { - var out = this - listOf(Pair("\\u002F", "/")).forEach { - out = out.replace(it.first, it.second) - } - return out - } - - private data class Links( - @JsonProperty("link") val link: String, - @JsonProperty("hls") val hls: Boolean?, - @JsonProperty("resolutionStr") val resolutionStr: String, - @JsonProperty("src") val src: String? - ) - - private data class AllAnimeVideoApiResponse( - @JsonProperty("links") val links: List - ) - - private data class ApiEndPoint( - @JsonProperty("episodeIframeHead") val episodeIframeHead: String - ) - - private suspend fun getM3u8Qualities( - m3u8Link: String, - referer: String, - qualityName: String, - ): List { - return M3u8Helper.generateM3u8( - this.name, - m3u8Link, - referer, - name = "${this.name} - $qualityName" - ) - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - var apiEndPoint = - parseJson(app.get("$mainUrl/getVersion").text).episodeIframeHead - if (apiEndPoint.endsWith("/")) apiEndPoint = - apiEndPoint.slice(0 until apiEndPoint.length - 1) - - val html = app.get(data).text - - val sources = Regex("""sourceUrl[:=]"(.+?)"""").findAll(html).toList() - .map { URLDecoder.decode(it.destructured.component1().sanitize(), "UTF-8") } - sources.apmap { - safeApiCall { - var link = it.replace(" ", "%20") - if (URI(link).isAbsolute || link.startsWith("//")) { - if (link.startsWith("//")) link = "https:$it" - - if (Regex("""streaming\.php\?""").matches(link)) { - // for now ignore - } else if (!embedIsBlacklisted(link)) { - if (URI(link).path.contains(".m3u")) { - getM3u8Qualities(link, data, URI(link).host).forEach(callback) - } else { - callback( - ExtractorLink( - "AllAnime - " + URI(link).host, - "", - link, - data, - Qualities.P1080.value, - false - ) - ) - } - } - } else { - link = apiEndPoint + URI(link).path + ".json?" + URI(link).query - val response = app.get(link) - - if (response.code < 400) { - val links = parseJson(response.text).links - links.forEach { server -> - if (server.hls != null && server.hls) { - getM3u8Qualities( - server.link, - "$apiEndPoint/player?uri=" + (if (URI(server.link).host.isNotEmpty()) server.link else apiEndPoint + URI( - server.link - ).path), - server.resolutionStr - ).forEach(callback) - } else { - callback( - ExtractorLink( - "AllAnime - " + URI(server.link).host, - server.resolutionStr, - server.link, - "$apiEndPoint/player?uri=" + (if (URI(server.link).host.isNotEmpty()) server.link else apiEndPoint + URI( - server.link - ).path), - Qualities.P1080.value, - false - ) - ) - } - } - } - } - } - } - return true - } - -} diff --git a/AllAnimeProvider/src/main/kotlin/com/lagradost/AllAnimeProviderPlugin.kt b/AllAnimeProvider/src/main/kotlin/com/lagradost/AllAnimeProviderPlugin.kt deleted file mode 100644 index 8b2169a..0000000 --- a/AllAnimeProvider/src/main/kotlin/com/lagradost/AllAnimeProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class AllAnimeProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(AllAnimeProvider()) - } -} \ No newline at end of file diff --git a/AllMoviesForYouProvider/build.gradle.kts b/AllMoviesForYouProvider/build.gradle.kts deleted file mode 100644 index 6de4950..0000000 --- a/AllMoviesForYouProvider/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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=allmoviesforyou.net&sz=24" -} \ No newline at end of file diff --git a/AllMoviesForYouProvider/src/main/AndroidManifest.xml b/AllMoviesForYouProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/AllMoviesForYouProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/AllMoviesForYouProvider/src/main/kotlin/com/lagradost/AllMoviesForYouProvider.kt b/AllMoviesForYouProvider/src/main/kotlin/com/lagradost/AllMoviesForYouProvider.kt deleted file mode 100644 index 5cda4d2..0000000 --- a/AllMoviesForYouProvider/src/main/kotlin/com/lagradost/AllMoviesForYouProvider.kt +++ /dev/null @@ -1,206 +0,0 @@ -package com.lagradost - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addDuration -import com.lagradost.cloudstream3.mvvm.logError -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.Jsoup - -class AllMoviesForYouProvider : MainAPI() { - companion object { - fun getType(t: String): TvType { - return when { - t.contains("series") -> TvType.TvSeries - t.contains("movies") -> TvType.Movie - else -> TvType.Movie - } - } - } - - // Fetching movies will not work if this link is outdated. - override var mainUrl = "https://allmoviesforyou.net" - override var name = "AllMoviesForYou" - override val hasMainPage = true - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries - ) - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val items = ArrayList() - val soup = app.get(mainUrl).document - val urls = listOf( - Pair("Movies", "section[data-id=movies] article.TPost.B"), - Pair("TV Series", "section[data-id=series] article.TPost.B"), - ) - for ((name, element) in urls) { - try { - val home = soup.select(element).map { - val title = it.selectFirst("h2.title")!!.text() - val link = it.selectFirst("a")!!.attr("href") - TvSeriesSearchResponse( - title, - link, - this.name, - TvType.Movie, - fixUrl(it.selectFirst("figure img")!!.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 - - val items = document.select("ul.MovieList > li > article > a") - return items.map { item -> - val href = item.attr("href") - val title = item.selectFirst("> h2.Title")!!.text() - val img = fixUrl(item.selectFirst("> div.Image > figure > img")!!.attr("data-src")) - val type = getType(href) - if (type == TvType.Movie) { - MovieSearchResponse(title, href, this.name, type, img, null) - } else { - TvSeriesSearchResponse( - title, - href, - this.name, - type, - img, - null, - null - ) - } - } - } - -// private fun getLink(document: Document): List? { -// val list = ArrayList() -// Regex("iframe src=\"(.*?)\"").find(document.html())?.groupValues?.get(1)?.let { -// list.add(it) -// } -// document.select("div.OptionBx")?.forEach { element -> -// val baseElement = element.selectFirst("> a.Button") -// val elementText = element.selectFirst("> p.AAIco-dns")?.text() -// if (elementText == "Streamhub" || elementText == "Dood") { -// baseElement?.attr("href")?.let { href -> -// list.add(href) -// } -// } -// } -// -// return if (list.isEmpty()) null else list -// } - - override suspend fun load(url: String): LoadResponse { - val type = getType(url) - - val document = app.get(url).document - - val title = document.selectFirst("h1.Title")!!.text() - val descipt = document.selectFirst("div.Description > p")!!.text() - val rating = - document.selectFirst("div.Vote > div.post-ratings > span")?.text()?.toRatingInt() - val year = document.selectFirst("span.Date")?.text() - val duration = document.selectFirst("span.Time")!!.text() - val backgroundPoster = - fixUrlNull(document.selectFirst("div.Image > figure > img")?.attr("data-src")) - - if (type == TvType.TvSeries) { - val list = ArrayList>() - - document.select("main > section.SeasonBx > div > div.Title > a").forEach { element -> - val season = element.selectFirst("> span")?.text()?.toIntOrNull() - val href = element.attr("href") - if (season != null && season > 0 && !href.isNullOrBlank()) { - list.add(Pair(season, fixUrl(href))) - } - } - if (list.isEmpty()) throw ErrorLoadingException("No Seasons Found") - - val episodeList = ArrayList() - - for (season in list) { - val seasonResponse = app.get(season.second).text - val seasonDocument = Jsoup.parse(seasonResponse) - val episodes = seasonDocument.select("table > tbody > tr") - if (episodes.isNotEmpty()) { - episodes.forEach { episode -> - val epNum = episode.selectFirst("> td > span.Num")?.text()?.toIntOrNull() - val poster = episode.selectFirst("> td.MvTbImg > a > img")?.attr("data-src") - val aName = episode.selectFirst("> td.MvTbTtl > a") - val name = aName!!.text() - val href = aName.attr("href") - val date = episode.selectFirst("> td.MvTbTtl > span")?.text() - - episodeList.add( - newEpisode(href) { - this.name = name - this.season = season.first - this.episode = epNum - this.posterUrl = fixUrlNull(poster) - addDate(date) - } - ) - } - } - } - return TvSeriesLoadResponse( - title, - url, - this.name, - type, - episodeList, - backgroundPoster, - year?.toIntOrNull(), - descipt, - null, - rating - ) - } else { - return newMovieLoadResponse( - title, - url, - type, - fixUrl(url) - ) { - posterUrl = backgroundPoster - this.year = year?.toIntOrNull() - this.plot = descipt - this.rating = rating - addDuration(duration) - } - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val doc = app.get(data).document - val iframe = doc.select("body iframe").map { fixUrl(it.attr("src")) } - iframe.apmap { id -> - if (id.contains("trembed")) { - val soup = app.get(id).document - soup.select("body iframe").map { - val link = fixUrl(it.attr("src").replace("streamhub.to/d/", "streamhub.to/e/")) - loadExtractor(link, data, subtitleCallback, callback) - } - } else loadExtractor(id, data, subtitleCallback, callback) - } - return true - } -} diff --git a/AllMoviesForYouProvider/src/main/kotlin/com/lagradost/AllMoviesForYouProviderPlugin.kt b/AllMoviesForYouProvider/src/main/kotlin/com/lagradost/AllMoviesForYouProviderPlugin.kt deleted file mode 100644 index bc59d5f..0000000 --- a/AllMoviesForYouProvider/src/main/kotlin/com/lagradost/AllMoviesForYouProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class AllMoviesForYouProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(AllMoviesForYouProvider()) - } -} \ No newline at end of file diff --git a/AniflixProvider/build.gradle.kts b/AniflixProvider/build.gradle.kts deleted file mode 100644 index dcf2ba3..0000000 --- a/AniflixProvider/build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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", - "OVA", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=aniflix.pro&sz=24" -} \ No newline at end of file diff --git a/AniflixProvider/src/main/AndroidManifest.xml b/AniflixProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/AniflixProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/AniflixProvider/src/main/kotlin/com/lagradost/AniflixProvider.kt b/AniflixProvider/src/main/kotlin/com/lagradost/AniflixProvider.kt deleted file mode 100644 index c62d891..0000000 --- a/AniflixProvider/src/main/kotlin/com/lagradost/AniflixProvider.kt +++ /dev/null @@ -1,274 +0,0 @@ -package com.lagradost - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.getQualityFromName -import java.net.URLDecoder - -class AniflixProvider : MainAPI() { - override var mainUrl = "https://aniflix.pro" - override var name = "Aniflix" - override val hasMainPage = true - - override val supportedTypes = setOf( - TvType.AnimeMovie, - TvType.OVA, - TvType.Anime, - ) - - companion object { - var token: String? = null - } - - private suspend fun getToken(): String { - return token ?: run { - Regex("([^/]*)/_buildManifest\\.js").find(app.get(mainUrl).text)?.groupValues?.getOrNull( - 1 - ) - ?.also { - token = it - } - ?: throw ErrorLoadingException("No token found") - } - } - - private fun Anime.toSearchResponse(): SearchResponse? { - return newAnimeSearchResponse( - title?.english ?: title?.romaji ?: return null, - "$mainUrl/anime/${id ?: return null}" - ) { - posterUrl = coverImage?.large ?: coverImage?.medium - } - } - - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val items = ArrayList() - val soup = app.get(mainUrl).document - val elements = listOf( - Pair("Trending Now", "div:nth-child(3) > div a"), - Pair("Popular", "div:nth-child(4) > div a"), - Pair("Top Rated", "div:nth-child(5) > div a"), - ) - - elements.map { (name, element) -> - val home = soup.select(element).map { - val href = it.attr("href") - val title = it.selectFirst("p.mt-2")!!.text() - val image = it.selectFirst("img.rounded-md[sizes]")!!.attr("src").replace("/_next/image?url=","") - .replace(Regex("\\&.*\$"),"") - val realposter = URLDecoder.decode(image, "UTF-8") - newAnimeSearchResponse(title, fixUrl(href)) { - this.posterUrl = realposter - } - } - items.add(HomePageList(name, home)) - } - - return HomePageResponse(items) - } - - override suspend fun search(query: String): List? { - val token = getToken() - val url = "$mainUrl/_next/data/$token/search.json?keyword=$query" - val response = app.get(url) - val searchResponse = - response.parsedSafe() - ?: throw ErrorLoadingException("No Media") - return searchResponse.pageProps?.searchResults?.Page?.media?.mapNotNull { media -> - media.toSearchResponse() - } - } - - override suspend fun load(url: String): LoadResponse { - val token = getToken() - - val id = Regex("$mainUrl/anime/([0-9]*)").find(url)?.groupValues?.getOrNull(1) - ?: throw ErrorLoadingException("Error parsing link for id") - - val res = app.get("https://aniflix.pro/_next/data/$token/anime/$id.json?id=$id") - .parsedSafe()?.pageProps - ?: throw ErrorLoadingException("Invalid Json reponse") - val isMovie = res.anime.format == "MOVIE" - return newAnimeLoadResponse( - res.anime.title?.english ?: res.anime.title?.romaji - ?: throw ErrorLoadingException("Invalid title reponse"), - url, if (isMovie) TvType.AnimeMovie else TvType.Anime - ) { - recommendations = res.recommended.mapNotNull { it.toSearchResponse() } - tags = res.anime.genres - posterUrl = res.anime.coverImage?.large ?: res.anime.coverImage?.medium - plot = res.anime.description - showStatus = when (res.anime.status) { - "FINISHED" -> ShowStatus.Completed - "RELEASING" -> ShowStatus.Ongoing - else -> null - } - addAniListId(id.toIntOrNull()) - - // subbed because they are both subbed and dubbed - if (isMovie) - addEpisodes( - DubStatus.Subbed, - listOf(newEpisode("$mainUrl/api/anime/?id=$id&episode=1")) - ) - else - addEpisodes(DubStatus.Subbed, res.episodes.episodes?.nodes?.mapIndexed { index, node -> - val episodeIndex = node?.number ?: (index + 1) - //"$mainUrl/_next/data/$token/watch/$id.json?episode=${node.number ?: return@mapNotNull null}&id=$id" - newEpisode("$mainUrl/api/anime?id=$id&episode=${episodeIndex}") { - episode = episodeIndex - posterUrl = node?.thumbnail?.original?.url - name = node?.titles?.canonical - } - }) - } - } - - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - return app.get(data).parsed().let { res -> - val dubReferer = res.dub?.Referer ?: "" - res.dub?.sources?.forEach { source -> - callback( - ExtractorLink( - name, - "${source.label ?: name} (DUB)", - source.file ?: return@forEach, - dubReferer, - getQualityFromName(source.label), - source.type == "hls" - ) - ) - } - - val subReferer = res.dub?.Referer ?: "" - res.sub?.sources?.forEach { source -> - callback( - ExtractorLink( - name, - "${source.label ?: name} (SUB)", - source.file ?: return@forEach, - subReferer, - getQualityFromName(source.label), - source.type == "hls" - ) - ) - } - - !res.dub?.sources.isNullOrEmpty() && !res.sub?.sources.isNullOrEmpty() - } - } - - data class AniLoadResponse( - @JsonProperty("sub") val sub: DubSubSource?, - @JsonProperty("dub") val dub: DubSubSource?, - @JsonProperty("episodes") val episodes: Int? - ) - - data class Sources( - @JsonProperty("file") val file: String?, - @JsonProperty("label") val label: String?, - @JsonProperty("type") val type: String? - ) - - data class DubSubSource( - @JsonProperty("Referer") var Referer: String?, - @JsonProperty("sources") var sources: ArrayList = arrayListOf() - ) - - data class PageProps( - @JsonProperty("searchResults") val searchResults: SearchResults? - ) - - data class SearchResults( - @JsonProperty("Page") val Page: Page? - ) - - data class Page( - @JsonProperty("media") val media: ArrayList = arrayListOf() - ) - - data class CoverImage( - @JsonProperty("color") val color: String?, - @JsonProperty("medium") val medium: String?, - @JsonProperty("large") val large: String?, - ) - - data class Title( - @JsonProperty("english") val english: String?, - @JsonProperty("romaji") val romaji: String?, - ) - - data class Search( - @JsonProperty("pageProps") val pageProps: PageProps?, - @JsonProperty("__N_SSP") val _NSSP: Boolean? - ) - - data class Anime( - @JsonProperty("status") val status: String?, - @JsonProperty("id") val id: Int?, - @JsonProperty("title") val title: Title?, - @JsonProperty("coverImage") val coverImage: CoverImage?, - @JsonProperty("format") val format: String?, - @JsonProperty("duration") val duration: Int?, - @JsonProperty("meanScore") val meanScore: Int?, - @JsonProperty("nextAiringEpisode") val nextAiringEpisode: String?, - @JsonProperty("bannerImage") val bannerImage: String?, - @JsonProperty("description") val description: String?, - @JsonProperty("genres") val genres: ArrayList? = null, - @JsonProperty("season") val season: String?, - @JsonProperty("startDate") val startDate: StartDate?, - ) - - data class StartDate( - @JsonProperty("year") val year: Int? - ) - - data class AnimeResponsePage( - @JsonProperty("pageProps") val pageProps: AnimeResponse?, - ) - - data class AnimeResponse( - @JsonProperty("anime") val anime: Anime, - @JsonProperty("recommended") val recommended: ArrayList, - @JsonProperty("episodes") val episodes: EpisodesParent, - ) - - data class EpisodesParent( - @JsonProperty("id") val id: String?, - @JsonProperty("season") val season: String?, - @JsonProperty("startDate") val startDate: String?, - @JsonProperty("episodeCount") val episodeCount: Int?, - @JsonProperty("episodes") val episodes: Episodes?, - ) - - data class Episodes( - @JsonProperty("nodes") val nodes: ArrayList = arrayListOf() - ) - - data class Nodes( - @JsonProperty("number") val number: Int? = null, - @JsonProperty("titles") val titles: Titles?, - @JsonProperty("thumbnail") val thumbnail: Thumbnail?, - ) - - data class Titles( - @JsonProperty("canonical") val canonical: String?, - ) - - data class Original( - @JsonProperty("url") val url: String?, - ) - - data class Thumbnail( - @JsonProperty("original") val original: Original?, - ) -} \ No newline at end of file diff --git a/AniflixProvider/src/main/kotlin/com/lagradost/AniflixProviderPlugin.kt b/AniflixProvider/src/main/kotlin/com/lagradost/AniflixProviderPlugin.kt deleted file mode 100644 index 54d3391..0000000 --- a/AniflixProvider/src/main/kotlin/com/lagradost/AniflixProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class AniflixProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(AniflixProvider()) - } -} \ No newline at end of file diff --git a/AnimeFlickProvider/build.gradle.kts b/AnimeFlickProvider/build.gradle.kts deleted file mode 100644 index 25b3e27..0000000 --- a/AnimeFlickProvider/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=animeflick.net&sz=24" -} \ No newline at end of file diff --git a/AnimeFlickProvider/src/main/AndroidManifest.xml b/AnimeFlickProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/AnimeFlickProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/AnimeFlickProvider/src/main/kotlin/com/lagradost/AnimeFlickProvider.kt b/AnimeFlickProvider/src/main/kotlin/com/lagradost/AnimeFlickProvider.kt deleted file mode 100644 index 4c76bfa..0000000 --- a/AnimeFlickProvider/src/main/kotlin/com/lagradost/AnimeFlickProvider.kt +++ /dev/null @@ -1,119 +0,0 @@ -package com.lagradost - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.cloudstream3.utils.extractorApis -import org.jsoup.Jsoup -import java.util.* - -class AnimeFlickProvider : MainAPI() { - 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 - } - } - - override var mainUrl = "https://animeflick.net" - override var name = "AnimeFlick" - override val hasQuickSearch = false - override val hasMainPage = false - - override val supportedTypes = setOf( - TvType.AnimeMovie, - TvType.Anime, - TvType.OVA - ) - - override suspend fun search(query: String): List { - val link = "https://animeflick.net/search.php?search=$query" - val html = app.get(link).text - val doc = Jsoup.parse(html) - - return doc.select(".row.mt-2").mapNotNull { - val href = mainUrl + it.selectFirst("a")?.attr("href") - val title = it.selectFirst("h5 > a")?.text() ?: return@mapNotNull null - val poster = mainUrl + it.selectFirst("img")?.attr("src")?.replace("70x110", "225x320") - AnimeSearchResponse( - title, - href, - this.name, - getType(title), - poster, - null, - EnumSet.of(DubStatus.Subbed), - ) - } - } - - override suspend fun load(url: String): LoadResponse { - val html = app.get(url).text - val doc = Jsoup.parse(html) - - val poster = mainUrl + doc.selectFirst("img.rounded")?.attr("src") - val title = doc.selectFirst("h2.title")!!.text() - - val yearText = doc.selectFirst(".trending-year")?.text() - val year = - if (yearText != null) Regex("""(\d{4})""").find(yearText)?.destructured?.component1() - ?.toIntOrNull() else null - val description = doc.selectFirst("p")?.text() - - val genres = doc.select("a[href*=\"genre-\"]").map { it.text() } - - val episodes = doc.select("#collapseOne .block-space > .row > div:nth-child(2)").map { - val name = it.selectFirst("a")?.text() - val link = mainUrl + it.selectFirst("a")?.attr("href") - Episode(link, name) - }.reversed() - - return newAnimeLoadResponse(title, url, getType(title)) { - posterUrl = poster - this.year = year - - addEpisodes(DubStatus.Subbed, episodes) - - plot = description - tags = genres - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val html = app.get(data).text - - val episodeRegex = Regex("""(https://.*?\.mp4)""") - val links = episodeRegex.findAll(html).map { - it.value - }.toList() - for (link in links) { - var alreadyAdded = false - for (extractor in extractorApis) { - if (link.startsWith(extractor.mainUrl)) { - extractor.getSafeUrl(link, data, subtitleCallback, callback) - alreadyAdded = true - break - } - } - if (!alreadyAdded) { - callback( - ExtractorLink( - this.name, - "${this.name} - Auto", - link, - "", - Qualities.P1080.value - ) - ) - } - } - - return true - } -} diff --git a/AnimeFlickProvider/src/main/kotlin/com/lagradost/AnimeFlickProviderPlugin.kt b/AnimeFlickProvider/src/main/kotlin/com/lagradost/AnimeFlickProviderPlugin.kt deleted file mode 100644 index 0e9d53c..0000000 --- a/AnimeFlickProvider/src/main/kotlin/com/lagradost/AnimeFlickProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class AnimeFlickProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(AnimeFlickProvider()) - } -} \ No newline at end of file diff --git a/AnimePaheProvider/build.gradle.kts b/AnimePaheProvider/build.gradle.kts deleted file mode 100644 index c6905e5..0000000 --- a/AnimePaheProvider/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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 = 0 // will be 3 if unspecified - tvTypes = listOf( - "AnimeMovie", - "Anime", - "OVA", - ) - -} \ No newline at end of file diff --git a/AnimePaheProvider/src/main/AndroidManifest.xml b/AnimePaheProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/AnimePaheProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/AnimePaheProvider/src/main/kotlin/com/lagradost/AnimePaheProvider.kt b/AnimePaheProvider/src/main/kotlin/com/lagradost/AnimePaheProvider.kt deleted file mode 100644 index 56c0e4a..0000000 --- a/AnimePaheProvider/src/main/kotlin/com/lagradost/AnimePaheProvider.kt +++ /dev/null @@ -1,562 +0,0 @@ -package com.lagradost - -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.module.kotlin.readValue -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId -import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.JsUnpacker -import com.lagradost.cloudstream3.utils.getQualityFromName -import com.lagradost.nicehttp.NiceResponse -import org.jsoup.Jsoup -import kotlin.math.pow - -class AnimePaheProvider : MainAPI() { - // credit to https://github.com/justfoolingaround/animdl/tree/master/animdl/core/codebase/providers/animepahe - companion object { - const val MAIN_URL = "https://animepahe.com" - - var cookies: Map = mapOf() - private 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 - } - - suspend fun generateSession(): Boolean { - if (cookies.isNotEmpty()) return true - return try { - val response = app.get("$MAIN_URL/") - cookies = response.cookies - true - } catch (e: Exception) { - false - } - } - - val YTSM = Regex("ysmm = '([^']+)") - - val KWIK_PARAMS_RE = Regex("""\("(\w+)",\d+,"(\w+)",(\d+),(\d+),\d+\)""") - val KWIK_D_URL = Regex("action=\"([^\"]+)\"") - val KWIK_D_TOKEN = Regex("value=\"([^\"]+)\"") - val YOUTUBE_VIDEO_LINK = - Regex("""(^(?:https?:)?(?://)?(?:www\.)?(?:youtu\.be/|youtube(?:-nocookie)?\.(?:[A-Za-z]{2,4}|[A-Za-z]{2,3}\.[A-Za-z]{2})/)(?:watch|embed/|vi?/)*(?:\?[\w=&]*vi?=)?[^#&?/]{11}.*${'$'})""") - } - - override var mainUrl = MAIN_URL - override var name = "AnimePahe" - override val hasQuickSearch = false - override val hasMainPage = true - - override val supportedTypes = setOf( - TvType.AnimeMovie, - TvType.Anime, - TvType.OVA - ) - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - data class Data( - @JsonProperty("id") val id: Int, - @JsonProperty("anime_id") val animeId: Int, - @JsonProperty("anime_title") val animeTitle: String, - @JsonProperty("anime_slug") val animeSlug: String, - @JsonProperty("episode") val episode: Int, - @JsonProperty("snapshot") val snapshot: String, - @JsonProperty("created_at") val createdAt: String, - @JsonProperty("anime_session") val animeSession: String, - ) - - data class AnimePaheLatestReleases( - @JsonProperty("total") val total: Int, - @JsonProperty("data") val data: List - ) - - val urls = listOf( - Pair("$mainUrl/api?m=airing&page=1", "Latest Releases"), - ) - - val items = ArrayList() - for (i in urls) { - try { - val response = app.get(i.first).text - val episodes = parseJson(response).data.map { - newAnimeSearchResponse( - it.animeTitle, - "https://pahe.win/a/${it.animeId}?slug=${it.animeTitle}", - fix = false - ) { - this.posterUrl = it.snapshot - addDubStatus(DubStatus.Subbed, it.episode) - } - } - - items.add(HomePageList(i.second, episodes)) - } catch (e: Exception) { - e.printStackTrace() - } - } - if (items.size <= 0) throw ErrorLoadingException() - return HomePageResponse(items) - } - - data class AnimePaheSearchData( - @JsonProperty("id") val id: Int, - @JsonProperty("slug") val slug: String, - @JsonProperty("title") val title: String, - @JsonProperty("type") val type: String, - @JsonProperty("episodes") val episodes: Int, - @JsonProperty("status") val status: String, - @JsonProperty("season") val season: String, - @JsonProperty("year") val year: Int, - @JsonProperty("score") val score: Double, - @JsonProperty("poster") val poster: String, - @JsonProperty("session") val session: String, - @JsonProperty("relevance") val relevance: String - ) - - data class AnimePaheSearch( - @JsonProperty("total") val total: Int, - @JsonProperty("data") val data: List - ) - - private suspend fun getAnimeByIdAndTitle(title: String, animeId: Int): String? { - val url = "$mainUrl/api?m=search&l=8&q=$title" - val headers = mapOf("referer" to "$mainUrl/") - - val req = app.get(url, headers = headers).text - val data = parseJson(req) - for (anime in data.data) { - if (anime.id == animeId) { - return "https://animepahe.com/anime/${anime.session}" - } - } - return null - } - - - override suspend fun search(query: String): List { - val url = "$mainUrl/api?m=search&l=8&q=$query" - val headers = mapOf("referer" to "$mainUrl/") - - val req = app.get(url, headers = headers).text - val data = parseJson(req) - - return data.data.map { - newAnimeSearchResponse( - it.title, - "https://pahe.win/a/${it.id}?slug=${it.title}", - fix = false - ) { - this.posterUrl = it.poster - addDubStatus(DubStatus.Subbed, it.episodes) - } - } - } - - private data class AnimeData( - @JsonProperty("id") val id: Int, - @JsonProperty("anime_id") val animeId: Int, - @JsonProperty("episode") val episode: Int, - @JsonProperty("title") val title: String, - @JsonProperty("snapshot") val snapshot: String, - @JsonProperty("session") val session: String, - @JsonProperty("filler") val filler: Int, - @JsonProperty("created_at") val createdAt: String - ) - - private data class AnimePaheAnimeData( - @JsonProperty("total") val total: Int, - @JsonProperty("per_page") val perPage: Int, - @JsonProperty("current_page") val currentPage: Int, - @JsonProperty("last_page") val lastPage: Int, - @JsonProperty("next_page_url") val nextPageUrl: String?, - @JsonProperty("prev_page_url") val prevPageUrl: String?, - @JsonProperty("from") val from: Int, - @JsonProperty("to") val to: Int, - @JsonProperty("data") val data: List - ) - - private suspend fun generateListOfEpisodes(link: String): ArrayList { - try { - val attrs = link.split('/') - val id = attrs[attrs.size - 1].split("?")[0] - - val uri = "$mainUrl/api?m=release&id=$id&sort=episode_asc&page=1" - val headers = mapOf("referer" to "$mainUrl/") - - val req = app.get(uri, headers = headers).text - val data = parseJson(req) - - val lastPage = data.lastPage - val perPage = data.perPage - val total = data.total - var ep = 1 - val episodes = ArrayList() - - fun getEpisodeTitle(k: AnimeData): String { - return k.title.ifEmpty { - "Episode ${k.episode}" - } - } - - if (lastPage == 1 && perPage > total) { - data.data.forEach { - episodes.add( - newEpisode("$mainUrl/api?m=links&id=${it.animeId}&session=${it.session}&p=kwik!!TRUE!!") { - addDate(it.createdAt) - this.name = getEpisodeTitle(it) - this.posterUrl = it.snapshot - } - ) - } - } else { - for (page in 0 until lastPage) { - for (i in 0 until perPage) { - if (ep <= total) { - episodes.add( - Episode( - "$mainUrl/api?m=release&id=${id}&sort=episode_asc&page=${page + 1}&ep=${ep}!!FALSE!!" - ) - ) - ++ep - } - } - } - } - return episodes - } catch (e: Exception) { - return ArrayList() - } - } - - override suspend fun load(url: String): LoadResponse? { - return suspendSafeApiCall { - val regex = Regex("""a/(\d+)\?slug=(.+)""") - val (animeId, animeTitle) = regex.find(url)!!.destructured - val link = getAnimeByIdAndTitle(animeTitle, animeId.toInt())!! - - val html = app.get(link).text - val doc = Jsoup.parse(html) - - val japTitle = doc.selectFirst("h2.japanese")?.text() - val poster = doc.selectFirst(".anime-poster a")?.attr("href") - - val tvType = doc.selectFirst("""a[href*="/anime/type/"]""")?.text() - - val trailer: String? = if (html.contains("https://www.youtube.com/watch")) { - YOUTUBE_VIDEO_LINK.find(html)?.destructured?.component1() - } else { - null - } - - val episodes = generateListOfEpisodes(url) - val year = Regex("""Aired:[^,]*, (\d+)""") - .find(html)!!.destructured.component1() - .toIntOrNull() - val status = - when (Regex("""Status:[^a]*a href=["']/anime/(.*?)["']""") - .find(html)!!.destructured.component1()) { - "airing" -> ShowStatus.Ongoing - "completed" -> ShowStatus.Completed - else -> null - } - val synopsis = doc.selectFirst(".anime-synopsis")?.text() - - var anilistId: Int? = null - var malId: Int? = null - - doc.select(".external-links > a").forEach { aTag -> - val split = aTag.attr("href").split("/") - - if (aTag.attr("href").contains("anilist.co")) { - anilistId = split[split.size - 1].toIntOrNull() - } else if (aTag.attr("href").contains("myanimelist.net")) { - malId = split[split.size - 1].toIntOrNull() - } - } - - newAnimeLoadResponse(animeTitle, url, getType(tvType.toString())) { - engName = animeTitle - japName = japTitle - - this.posterUrl = poster - this.year = year - - addEpisodes(DubStatus.Subbed, episodes) - this.showStatus = status - plot = synopsis - tags = if (!doc.select(".anime-genre > ul a").isEmpty()) { - ArrayList(doc.select(".anime-genre > ul a").map { it.text().toString() }) - } else { - null - } - - addMalId(malId) - addAniListId(anilistId) - addTrailer(trailer) - } - } - } - - - private fun isNumber(s: String?): Boolean { - return s?.toIntOrNull() != null - } - - private fun cookieStrToMap(cookie: String): Map { - val cookies = mutableMapOf() - for (string in cookie.split("; ")) { - val split = string.split("=").toMutableList() - val name = split.removeFirst().trim() - val value = if (split.size == 0) { - "true" - } else { - split.joinToString("=") - } - cookies[name] = value - } - return cookies.toMap() - } - - private fun getString(content: String, s1: Int, s2: Int): String { - val characterMap = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/" - - val slice2 = characterMap.slice(0 until s2) - var acc: Long = 0 - - for ((n, i) in content.reversed().withIndex()) { - acc += (when (isNumber("$i")) { - true -> "$i".toLong() - false -> "0".toLong() - }) * s1.toDouble().pow(n.toDouble()).toInt() - } - - var k = "" - - while (acc > 0) { - k = slice2[(acc % s2).toInt()] + k - acc = (acc - (acc % s2)) / s2 - } - - return when (k != "") { - true -> k - false -> "0" - } - } - - private fun decrypt(fullString: String, key: String, v1: Int, v2: Int): String { - var r = "" - var i = 0 - - while (i < fullString.length) { - var s = "" - - while (fullString[i] != key[v2]) { - s += fullString[i] - ++i - } - var j = 0 - - while (j < key.length) { - s = s.replace(key[j].toString(), j.toString()) - ++j - } - r += (getString(s, v2, 10).toInt() - v1).toChar() - ++i - } - return r - } - - private fun zipGen(gen: Sequence>): ArrayList, Pair>> { - val allItems = gen.toList().toMutableList() - val newList = ArrayList, Pair>>() - - while (allItems.size > 1) { - newList.add(Pair(allItems[0], allItems[1])) - allItems.removeAt(0) - allItems.removeAt(0) - } - return newList - } - - private fun decodeAdfly(codedKey: String): String { - var r = "" - var j = "" - - for ((n, l) in codedKey.withIndex()) { - if (n % 2 != 0) { - j = l + j - } else { - r += l - } - } - - val encodedUri = ((r + j).toCharArray().map { it.toString() }).toMutableList() - val numbers = sequence { - for ((i, n) in encodedUri.withIndex()) { - if (isNumber(n)) { - yield(Pair(i, n.toInt())) - } - } - } - - for ((first, second) in zipGen(numbers)) { - val xor = first.second.xor(second.second) - if (xor < 10) { - encodedUri[first.first] = xor.toString() - } - } - var returnValue = String(encodedUri.joinToString("").toByteArray(), Charsets.UTF_8) - returnValue = base64Decode(returnValue) - return returnValue.slice(16..returnValue.length - 17) - } - - private data class VideoQuality( - @JsonProperty("id") val id: Int?, - @JsonProperty("audio") val audio: String?, - @JsonProperty("kwik") val kwik: String?, - @JsonProperty("kwik_pahewin") val kwikPahewin: String - ) - - private data class AnimePaheEpisodeLoadLinks( - @JsonProperty("data") val data: List> - ) - - private suspend fun bypassAdfly(adflyUri: String): String { - if (!generateSession()) { - return bypassAdfly(adflyUri) - } - - var responseCode = 302 - var adflyContent: NiceResponse? = null - var tries = 0 - - while (responseCode != 200 && tries < 20) { - adflyContent = app.get( - app.get(adflyUri, cookies = cookies, allowRedirects = false).url, - cookies = cookies, - allowRedirects = false - ) - cookies = cookies + adflyContent.cookies - responseCode = adflyContent.code - ++tries - } - if (tries > 19) { - throw Exception("Failed to bypass adfly.") - } - return decodeAdfly(YTSM.find(adflyContent?.text.toString())!!.destructured.component1()) - } - - private suspend fun getStreamUrlFromKwik(url: String?): String? { - if (url == null) return null - val response = - app.get( - url, - headers = mapOf("referer" to mainUrl), - cookies = cookies - ).text - Regex("eval((.|\\n)*?)").find(response)?.groupValues?.get(1)?.let { jsEval -> - JsUnpacker("eval$jsEval").unpack()?.let { unPacked -> - Regex("source=\'(.*?)\'").find(unPacked)?.groupValues?.get(1)?.let { link -> - return link - } - } - } - return null - } - - private suspend fun getStreamUrlFromKwikAdfly(adflyUri: String): String { - val fContent = - app.get( - bypassAdfly(adflyUri), - headers = mapOf("referer" to "https://kwik.cx/"), - cookies = cookies - ) - cookies = cookies + fContent.cookies - - val (fullString, key, v1, v2) = KWIK_PARAMS_RE.find(fContent.text)!!.destructured - val decrypted = decrypt(fullString, key, v1.toInt(), v2.toInt()) - val uri = KWIK_D_URL.find(decrypted)!!.destructured.component1() - val tok = KWIK_D_TOKEN.find(decrypted)!!.destructured.component1() - var content: NiceResponse? = null - - var code = 419 - var tries = 0 - - while (code != 302 && tries < 20) { - content = app.post( - uri, - allowRedirects = false, - data = mapOf("_token" to tok), - headers = mapOf("referer" to fContent.url), - cookies = fContent.cookies - ) - code = content.code - ++tries - } - if (tries > 19) { - throw Exception("Failed to extract the stream uri from kwik.") - } - return content?.headers?.values("location").toString() - } - - private suspend fun extractVideoLinks( - episodeLink: String, - callback: (ExtractorLink) -> Unit - ) { - var link = episodeLink - val headers = mapOf("referer" to "$mainUrl/") - - if (link.contains("!!TRUE!!")) { - link = link.replace("!!TRUE!!", "") - } else { - val regex = """&ep=(\d+)!!FALSE!!""".toRegex() - val episodeNum = regex.find(link)?.destructured?.component1()?.toIntOrNull() - link = link.replace(regex, "") - - val req = app.get(link, headers = headers).text - val jsonResponse = parseJson(req) - val ep = ((jsonResponse.data.map { - if (it.episode == episodeNum) { - it - } else { - null - } - }).filterNotNull())[0] - link = "$mainUrl/api?m=links&id=${ep.animeId}&session=${ep.session}&p=kwik" - } - val req = app.get(link, headers = headers).text - val data = mapper.readValue(req) - - data.data.forEach { - it.entries.toList().apmap { quality -> - getStreamUrlFromKwik(quality.value.kwik)?.let { link -> - callback( - ExtractorLink( - "KWIK", - "KWIK - ${quality.key} [${quality.value.audio ?: "jpn"}]", - link, - "https://kwik.cx/", - getQualityFromName(quality.key), - link.contains(".m3u8") - ) - ) - } - } - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - extractVideoLinks(data, callback) - return true - } -} diff --git a/AnimePaheProvider/src/main/kotlin/com/lagradost/AnimePaheProviderPlugin.kt b/AnimePaheProvider/src/main/kotlin/com/lagradost/AnimePaheProviderPlugin.kt deleted file mode 100644 index e0ae1f3..0000000 --- a/AnimePaheProvider/src/main/kotlin/com/lagradost/AnimePaheProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class AnimePaheProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(AnimePaheProvider()) - } -} \ No newline at end of file diff --git a/AnimekisaProvider/build.gradle.kts b/AnimekisaProvider/build.gradle.kts deleted file mode 100644 index 5d62c10..0000000 --- a/AnimekisaProvider/build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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 = 0 // will be 3 if unspecified - tvTypes = listOf( - "AnimeMovie", - "Anime", - "OVA", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=animekisa.in&sz=24" -} \ No newline at end of file diff --git a/AnimekisaProvider/src/main/AndroidManifest.xml b/AnimekisaProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/AnimekisaProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/AnimekisaProvider/src/main/kotlin/com/lagradost/AnimekisaProvider.kt b/AnimekisaProvider/src/main/kotlin/com/lagradost/AnimekisaProvider.kt deleted file mode 100644 index 0a5ca2f..0000000 --- a/AnimekisaProvider/src/main/kotlin/com/lagradost/AnimekisaProvider.kt +++ /dev/null @@ -1,131 +0,0 @@ -package com.lagradost - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall -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.* - - -class AnimekisaProvider : MainAPI() { - override var mainUrl = "https://animekisa.in" - override var 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(page: Int, request : MainPageRequest): 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 = urls.mapNotNull { - suspendSafeApiCall { - val home = Jsoup.parse( - parseJson( - app.get( - it.first - ).text - ).html - ).select("div.flw-item").mapNotNull secondMap@ { - val title = it.selectFirst("h3.title a")?.text() ?: return@secondMap null - val link = it.selectFirst("a")?.attr("href") ?: return@secondMap null - 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), - ) - } - HomePageList(name, home) - } - } - - if (items.isEmpty()) throw ErrorLoadingException() - return HomePageResponse(items) - } - - override suspend fun search(query: String): List { - return app.get("$mainUrl/search/?keyword=$query").document.select("div.flw-item") - .mapNotNull { - 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-.*-.(/|))"), - "" - ) ?: return@mapNotNull null - 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").mapNotNull { - val link = it.selectFirst("a")?.attr("href") ?: return@mapNotNull null - Episode(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, subtitleCallback, callback) - } - return true - } -} \ No newline at end of file diff --git a/AnimekisaProvider/src/main/kotlin/com/lagradost/AnimekisaProviderPlugin.kt b/AnimekisaProvider/src/main/kotlin/com/lagradost/AnimekisaProviderPlugin.kt deleted file mode 100644 index 058cee7..0000000 --- a/AnimekisaProvider/src/main/kotlin/com/lagradost/AnimekisaProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class AnimekisaProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(AnimekisaProvider()) - } -} \ No newline at end of file diff --git a/AsiaFlixProvider/build.gradle.kts b/AsiaFlixProvider/build.gradle.kts deleted file mode 100644 index 0280ad9..0000000 --- a/AsiaFlixProvider/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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", - "OVA", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=asiaflix.app&sz=24" -} \ No newline at end of file diff --git a/AsiaFlixProvider/src/main/AndroidManifest.xml b/AsiaFlixProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/AsiaFlixProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/AsiaFlixProvider/src/main/kotlin/com/lagradost/AsiaFlixProvider.kt b/AsiaFlixProvider/src/main/kotlin/com/lagradost/AsiaFlixProvider.kt deleted file mode 100644 index cc8d3ad..0000000 --- a/AsiaFlixProvider/src/main/kotlin/com/lagradost/AsiaFlixProvider.kt +++ /dev/null @@ -1,198 +0,0 @@ -package com.lagradost - -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.module.kotlin.readValue -import com.lagradost.cloudstream3.* -//import com.lagradost.cloudstream3.animeproviders.GogoanimeProvider.Companion.getStatus -import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.getQualityFromName -import java.net.URI - -class AsiaFlixProvider : MainAPI() { - 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 - } - } - } - - override var mainUrl = "https://asiaflix.app" - override var name = "AsiaFlix" - override val hasQuickSearch = false - override val hasMainPage = true - override val hasChromecastSupport = false - override val supportedTypes = setOf(TvType.AsianDrama) - - private val apiUrl = "https://api.asiaflix.app/api/v2" - - data class DashBoardObject( - @JsonProperty("sectionName") val sectionName: String, - @JsonProperty("type") val type: String?, - @JsonProperty("data") val data: List? - ) - - data class Episodes( - @JsonProperty("_id") val _id: String, - @JsonProperty("epUrl") val epUrl: String?, - @JsonProperty("number") val number: Int?, - @JsonProperty("type") val type: String?, - @JsonProperty("extracted") val extracted: String?, - @JsonProperty("videoUrl") val videoUrl: String? - ) - - - data class Data( - @JsonProperty("_id") val _id: String, - @JsonProperty("name") val name: String, - @JsonProperty("altNames") val altNames: String?, - @JsonProperty("image") val image: String?, - @JsonProperty("tvStatus") val tvStatus: String?, - @JsonProperty("genre") val genre: String?, - @JsonProperty("releaseYear") val releaseYear: Int?, - @JsonProperty("createdAt") val createdAt: Long?, - @JsonProperty("episodes") val episodes: List?, - @JsonProperty("views") val views: Int? - ) - - - data class DramaPage( - @JsonProperty("_id") val _id: String, - @JsonProperty("name") val name: String, - @JsonProperty("altNames") val altNames: String?, - @JsonProperty("synopsis") val synopsis: String?, - @JsonProperty("image") val image: String?, - @JsonProperty("language") val language: String?, - @JsonProperty("dramaUrl") val dramaUrl: String?, - @JsonProperty("published") val published: Boolean?, - @JsonProperty("tvStatus") val tvStatus: String?, - @JsonProperty("firstAirDate") val firstAirDate: String?, - @JsonProperty("genre") val genre: String?, - @JsonProperty("releaseYear") val releaseYear: Int?, - @JsonProperty("createdAt") val createdAt: Long?, - @JsonProperty("modifiedAt") val modifiedAt: Long?, - @JsonProperty("episodes") val episodes: List, - @JsonProperty("__v") val __v: Int?, - @JsonProperty("cdnImage") val cdnImage: String?, - @JsonProperty("views") val views: Int? - ) - - private fun Data.toSearchResponse(): TvSeriesSearchResponse { - return TvSeriesSearchResponse( - name, - _id, - this@AsiaFlixProvider.name, - TvType.AsianDrama, - image, - releaseYear, - episodes?.size, - ) - } - - private fun Episodes.toEpisode(): Episode? { - if (videoUrl != null && videoUrl.contains("watch/null") || number == null) return null - return videoUrl?.let { - Episode( - it, - null, - number, - ) - } - } - - private fun DramaPage.toLoadResponse(): TvSeriesLoadResponse { - return TvSeriesLoadResponse( - name, - "$mainUrl$dramaUrl/$_id".replace("drama-detail", "show-details"), - this@AsiaFlixProvider.name, - TvType.AsianDrama, - episodes.mapNotNull { it.toEpisode() }.sortedBy { it.episode }, - image, - releaseYear, - synopsis, - getStatus(tvStatus ?: ""), - null, - genre?.split(",")?.map { it.trim() } - ) - } - - override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { - val headers = mapOf("X-Requested-By" to "asiaflix-web") - val response = app.get("$apiUrl/dashboard", headers = headers).text - - val customMapper = - mapper.copy().configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true) - // Hack, because it can either be object or a list - val cleanedResponse = Regex(""""data":(\{.*?),\{"sectionName"""").replace(response) { - """"data":null},{"sectionName"""" - } - - val dashBoard = customMapper.readValue?>(cleanedResponse) - - val listItems = dashBoard?.mapNotNull { - it.data?.map { data -> - data.toSearchResponse() - }?.let { searchResponse -> - HomePageList(it.sectionName, searchResponse) - } - } - return HomePageResponse(listItems ?: listOf()) - } - - data class Link( - @JsonProperty("url") val url: String?, - ) - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - if (isCasting) return false - val headers = mapOf("X-Requested-By" to "asiaflix-web") - app.get( - "$apiUrl/utility/get-stream-links?url=$data", - headers = headers - ).text.toKotlinObject().url?.let { -// val fixedUrl = "https://api.asiaflix.app/api/v2/utility/cors-proxy/playlist/${URLEncoder.encode(it, StandardCharsets.UTF_8.toString())}" - callback.invoke( - ExtractorLink( - name, - name, - it, - "https://asianload1.com/", - /** <------ This provider should be added instead */ - getQualityFromName(it), - URI(it).path.endsWith(".m3u8") - ) - ) - } - return true - } - - override suspend fun search(query: String): List? { - val headers = mapOf("X-Requested-By" to "asiaflix-web") - val url = "$apiUrl/drama/search?q=$query" - val response = app.get(url, headers = headers).text - return mapper.readValue?>(response)?.map { it.toSearchResponse() } - } - - override suspend fun load(url: String): LoadResponse { - val headers = mapOf("X-Requested-By" to "asiaflix-web") - val requestUrl = "$apiUrl/drama?id=${url.split("/").lastOrNull()}" - val response = app.get(requestUrl, headers = headers).text - val dramaPage = response.toKotlinObject() - return dramaPage.toLoadResponse() - } -} diff --git a/AsiaFlixProvider/src/main/kotlin/com/lagradost/AsiaFlixProviderPlugin.kt b/AsiaFlixProvider/src/main/kotlin/com/lagradost/AsiaFlixProviderPlugin.kt deleted file mode 100644 index f6656e4..0000000 --- a/AsiaFlixProvider/src/main/kotlin/com/lagradost/AsiaFlixProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class AsiaFlixProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(AsiaFlixProvider()) - } -} \ No newline at end of file diff --git a/BflixProvider/build.gradle.kts b/BflixProvider/build.gradle.kts deleted file mode 100644 index 69c717d..0000000 --- a/BflixProvider/build.gradle.kts +++ /dev/null @@ -1,22 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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 - - - iconUrl = "https://www.google.com/s2/favicons?domain=bflix.ru&sz=24" -} \ No newline at end of file diff --git a/BflixProvider/src/main/AndroidManifest.xml b/BflixProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/BflixProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/BflixProvider/src/main/kotlin/com/lagradost/BflixProvider.kt b/BflixProvider/src/main/kotlin/com/lagradost/BflixProvider.kt deleted file mode 100644 index 98dc3bb..0000000 --- a/BflixProvider/src/main/kotlin/com/lagradost/BflixProvider.kt +++ /dev/null @@ -1,300 +0,0 @@ -package com.lagradost - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.NineAnimeApi.decodeVrf -import com.lagradost.NineAnimeApi.encode -import com.lagradost.NineAnimeApi.encodeVrf -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.Jsoup - -open class BflixProvider : MainAPI() { - override var mainUrl = "https://bflix.ru" - override var name = "Bflix" - override val hasMainPage = true - override val hasChromecastSupport = true - override val hasDownloadSupport = true - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - ) - - //override val uniqueId: Int by lazy { "BflixProvider".hashCode() } - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val items = ArrayList() - val soup = app.get("$mainUrl/home").document - val testa = listOf( - Pair("Movies", "div.tab-content[data-name=movies] div.filmlist div.item"), - Pair("Shows", "div.tab-content[data-name=shows] div.filmlist div.item"), - Pair("Trending", "div.tab-content[data-name=trending] div.filmlist div.item"), - Pair( - "Latest Movies", - "div.container section.bl:contains(Latest Movies) div.filmlist div.item" - ), - Pair( - "Latest TV-Series", - "div.container section.bl:contains(Latest TV-Series) div.filmlist div.item" - ), - ) - for ((name, element) in testa) try { - val test = soup.select(element).map { - val title = it.selectFirst("h3 a")!!.text() - val link = fixUrl(it.selectFirst("a")!!.attr("href")) - val qualityInfo = it.selectFirst("div.quality")!!.text() - val quality = getQualityFromString(qualityInfo) - TvSeriesSearchResponse( - title, - link, - this.name, - if (link.contains("/movie/")) TvType.Movie else TvType.TvSeries, - it.selectFirst("a.poster img")!!.attr("src"), - null, - null, - quality = quality - ) - } - items.add(HomePageList(name, test)) - } catch (e: Exception) { - e.printStackTrace() - } - - if (items.size <= 0) throw ErrorLoadingException() - return HomePageResponse(items) - } - - override suspend fun search(query: String): List? { - val encodedquery = encodeVrf(query, mainKey) - val url = "$mainUrl/search?keyword=$query&vrf=$encodedquery" - val html = app.get(url).text - val document = Jsoup.parse(html) - - return document.select(".filmlist div.item").map { - val title = it.selectFirst("h3 a")!!.text() - val href = fixUrl(it.selectFirst("a")!!.attr("href")) - val image = it.selectFirst("a.poster img")!!.attr("src") - val isMovie = href.contains("/movie/") - val qualityInfo = it.selectFirst("div.quality")!!.text() - val quality = getQualityFromString(qualityInfo) - - if (isMovie) { - MovieSearchResponse( - title, - href, - this.name, - TvType.Movie, - image, - null, - quality = quality - ) - } else { - TvSeriesSearchResponse( - title, - href, - this.name, - TvType.TvSeries, - image, - null, - null, - quality = quality - ) - } - } - } - - data class Response( - @JsonProperty("html") val html: String - ) - - companion object { - val mainKey = "OrAimkpzm6phmN3j" - } - - override suspend fun load(url: String): LoadResponse? { - val soup = app.get(url).document - val movieid = soup.selectFirst("div#watch")!!.attr("data-id") - val movieidencoded = encodeVrf(movieid, mainKey) - val title = soup.selectFirst("div.info h1")!!.text() - val description = soup.selectFirst(".info .desc")?.text()?.trim() - val poster: String? = try { - soup.selectFirst("img.poster")!!.attr("src") - } catch (e: Exception) { - soup.selectFirst(".info .poster img")!!.attr("src") - } - - val tags = soup.select("div.info .meta div:contains(Genre) a").map { it.text() } - val vrfUrl = "$mainUrl/ajax/film/servers?id=$movieid&vrf=$movieidencoded" - println("VRF___ $vrfUrl") - val episodes = Jsoup.parse( - app.get( - vrfUrl - ).parsed().html - ).select("div.episode").map { - val a = it.selectFirst("a") - val href = fixUrl(a!!.attr("href")) - val extraData = a.attr("data-kname").let { str -> - str.split("-").mapNotNull { subStr -> subStr.toIntOrNull() } - } - val isValid = extraData.size == 2 - val episode = if (isValid) extraData.getOrNull(1) else null - val season = if (isValid) extraData.getOrNull(0) else null - - val eptitle = it.selectFirst(".episode a span.name")!!.text() - val secondtitle = it.selectFirst(".episode a span")!!.text() - .replace(Regex("(Episode (\\d+):|Episode (\\d+)-|Episode (\\d+))"), "") ?: "" - Episode( - href, - secondtitle + eptitle, - season, - episode, - ) - } - val tvType = - if (url.contains("/movie/") && episodes.size == 1) TvType.Movie else TvType.TvSeries - val recommendations = - soup.select("div.bl-2 section.bl div.content div.filmlist div.item") - .mapNotNull { element -> - val recTitle = element.select("h3 a").text() ?: return@mapNotNull null - val image = element.select("a.poster img")?.attr("src") - val recUrl = fixUrl(element.select("a").attr("href")) - MovieSearchResponse( - recTitle, - recUrl, - this.name, - if (recUrl.contains("/movie/")) TvType.Movie else TvType.TvSeries, - image, - year = null - ) - } - val rating = soup.selectFirst(".info span.imdb")?.text()?.toRatingInt() - val durationdoc = soup.selectFirst("div.info div.meta").toString() - val durationregex = Regex("((\\d+) min)") - val yearegex = Regex("(\\d+)") - val duration = if (durationdoc.contains("na min")) null - else durationregex.find(durationdoc)?.destructured?.component1()?.replace(" min", "") - ?.toIntOrNull() - val year = if (mainUrl == "https://bflix.ru") { - yearegex.find(durationdoc)?.destructured?.component1() - ?.replace(Regex("|"), "") - } else null - return when (tvType) { - TvType.TvSeries -> { - TvSeriesLoadResponse( - title, - url, - this.name, - tvType, - episodes, - poster, - year?.toIntOrNull(), - description, - null, - rating, - tags, - recommendations = recommendations, - duration = duration, - ) - } - TvType.Movie -> { - MovieLoadResponse( - title, - url, - this.name, - tvType, - url, - poster, - year?.toIntOrNull(), - description, - rating, - tags, - recommendations = recommendations, - duration = duration - ) - } - else -> null - } - } - - - data class Subtitles( - @JsonProperty("file") val file: String, - @JsonProperty("label") val label: String, - @JsonProperty("kind") val kind: String - ) - - data class Links( - @JsonProperty("url") val url: String - ) - - data class Servers( - @JsonProperty("28") val mcloud: String?, - @JsonProperty("35") val mp4upload: String?, - @JsonProperty("40") val streamtape: String?, - @JsonProperty("41") val vidstream: String?, - @JsonProperty("43") val videovard: String? - ) - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val soup = app.get(data).document - - val movieid = encode(soup.selectFirst("div#watch")?.attr("data-id") ?: return false) - val movieidencoded = encodeVrf(movieid, mainKey) - Jsoup.parse( - parseJson( - app.get( - "$mainUrl/ajax/film/servers?id=$movieid&vrf=$movieidencoded" - ).text - ).html - ) - .select("html body #episodes").map { - val cleandata = data.replace(mainUrl, "") - val a = it.select("a").map { - it.attr("data-kname") - } - val tvType = - if (data.contains("movie/") && a.size == 1) TvType.Movie else TvType.TvSeries - val servers = if (tvType == TvType.Movie) it.select(".episode a").attr("data-ep") - else - it.select(".episode a[href=$cleandata]").attr("data-ep") - ?: it.select(".episode a[href=${cleandata.replace("/1-full", "")}]") - .attr("data-ep") - val jsonservers = parseJson(servers) ?: return@map - listOfNotNull( - jsonservers.vidstream, - jsonservers.mcloud, - jsonservers.mp4upload, - jsonservers.streamtape, - jsonservers.videovard, - ).mapNotNull { - val epserver = app.get("$mainUrl/ajax/episode/info?id=$it").text - (if (epserver.contains("url")) { - parseJson(epserver) - } else null)?.url?.let { - decodeVrf(it, mainKey) - } - }.apmap { url -> - loadExtractor( - url, data, subtitleCallback, callback - ) - } - //Apparently any server works, I haven't found any diference - val sublink = - app.get("$mainUrl/ajax/episode/subtitles/${jsonservers.mcloud}").text - val jsonsub = parseJson>(sublink) - jsonsub.forEach { subtitle -> - subtitleCallback( - SubtitleFile(subtitle.label, subtitle.file) - ) - } - } - - return true - } -} diff --git a/BflixProvider/src/main/kotlin/com/lagradost/BflixProviderPlugin.kt b/BflixProvider/src/main/kotlin/com/lagradost/BflixProviderPlugin.kt deleted file mode 100644 index 8379bea..0000000 --- a/BflixProvider/src/main/kotlin/com/lagradost/BflixProviderPlugin.kt +++ /dev/null @@ -1,16 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class BflixProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(BflixProvider()) - registerMainAPI(FmoviesToProvider()) - registerMainAPI(SflixProProvider()) - } -} \ No newline at end of file diff --git a/BflixProvider/src/main/kotlin/com/lagradost/FmoviesToProvider.kt b/BflixProvider/src/main/kotlin/com/lagradost/FmoviesToProvider.kt deleted file mode 100644 index 75eda7f..0000000 --- a/BflixProvider/src/main/kotlin/com/lagradost/FmoviesToProvider.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.lagradost - -class FmoviesToProvider : BflixProvider() { - override var mainUrl = "https://fmovies.to" - override var name = "Fmovies.to" -} \ No newline at end of file diff --git a/BflixProvider/src/main/kotlin/com/lagradost/NineAnimeApi.kt b/BflixProvider/src/main/kotlin/com/lagradost/NineAnimeApi.kt deleted file mode 100644 index c40dd67..0000000 --- a/BflixProvider/src/main/kotlin/com/lagradost/NineAnimeApi.kt +++ /dev/null @@ -1,112 +0,0 @@ -package com.lagradost - -// taken from https://github.com/saikou-app/saikou/blob/b35364c8c2a00364178a472fccf1ab72f09815b4/app/src/main/java/ani/saikou/parsers/anime/NineAnime.kt -// GNU General Public License v3.0 https://github.com/saikou-app/saikou/blob/main/LICENSE.md -object NineAnimeApi { - private const val nineAnimeKey = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" - private const val cipherKey = "kMXzgyNzT3k5dYab" - - fun encodeVrf(text: String, mainKey: String): String { - return encode( - encrypt( - cipher(mainKey, encode(text)), - nineAnimeKey - )//.replace("""=+$""".toRegex(), "") - ) - } - - fun decodeVrf(text: String, mainKey: String): String { - return decode(cipher(mainKey, decrypt(text, nineAnimeKey))) - } - - fun encrypt(input: String, key: String): String { - if (input.any { it.code > 255 }) throw Exception("illegal characters!") - var output = "" - for (i in input.indices step 3) { - val a = intArrayOf(-1, -1, -1, -1) - a[0] = input[i].code shr 2 - a[1] = (3 and input[i].code) shl 4 - if (input.length > i + 1) { - a[1] = a[1] or (input[i + 1].code shr 4) - a[2] = (15 and input[i + 1].code) shl 2 - } - if (input.length > i + 2) { - a[2] = a[2] or (input[i + 2].code shr 6) - a[3] = 63 and input[i + 2].code - } - for (n in a) { - if (n == -1) output += "=" - else { - if (n in 0..63) output += key[n] - } - } - } - return output - } - - fun cipher(key: String, text: String): String { - val arr = IntArray(256) { it } - - var u = 0 - var r: Int - arr.indices.forEach { - u = (u + arr[it] + key[it % key.length].code) % 256 - r = arr[it] - arr[it] = arr[u] - arr[u] = r - } - u = 0 - var c = 0 - - return text.indices.map { j -> - c = (c + 1) % 256 - u = (u + arr[c]) % 256 - r = arr[c] - arr[c] = arr[u] - arr[u] = r - (text[j].code xor arr[(arr[c] + arr[u]) % 256]).toChar() - }.joinToString("") - } - - @Suppress("SameParameterValue") - private fun decrypt(input: String, key: String): String { - val t = if (input.replace("""[\t\n\f\r]""".toRegex(), "").length % 4 == 0) { - input.replace("""==?$""".toRegex(), "") - } else input - if (t.length % 4 == 1 || t.contains("""[^+/0-9A-Za-z]""".toRegex())) throw Exception("bad input") - var i: Int - var r = "" - var e = 0 - var u = 0 - for (o in t.indices) { - e = e shl 6 - i = key.indexOf(t[o]) - e = e or i - u += 6 - if (24 == u) { - r += ((16711680 and e) shr 16).toChar() - r += ((65280 and e) shr 8).toChar() - r += (255 and e).toChar() - e = 0 - u = 0 - } - } - return if (12 == u) { - e = e shr 4 - r + e.toChar() - } else { - if (18 == u) { - e = e shr 2 - r += ((65280 and e) shr 8).toChar() - r += (255 and e).toChar() - } - r - } - } - - fun encode(input: String): String = - java.net.URLEncoder.encode(input, "utf-8").replace("+", "%20") - - private fun decode(input: String): String = java.net.URLDecoder.decode(input, "utf-8") -} \ No newline at end of file diff --git a/BflixProvider/src/main/kotlin/com/lagradost/SflixProProvider.kt b/BflixProvider/src/main/kotlin/com/lagradost/SflixProProvider.kt deleted file mode 100644 index 3ae7f81..0000000 --- a/BflixProvider/src/main/kotlin/com/lagradost/SflixProProvider.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.lagradost - -class SflixProProvider : BflixProvider() { - override var mainUrl = "https://sflix.pro" - override var name = "Sflix.pro" -} \ No newline at end of file diff --git a/DubbedAnimeProvider/build.gradle.kts b/DubbedAnimeProvider/build.gradle.kts deleted file mode 100644 index 0644ac5..0000000 --- a/DubbedAnimeProvider/build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=bestdubbedanime.com&sz=24" -} \ No newline at end of file diff --git a/DubbedAnimeProvider/src/main/AndroidManifest.xml b/DubbedAnimeProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/DubbedAnimeProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/DubbedAnimeProvider/src/main/kotlin/com/lagradost/DubbedAnimeProvider.kt b/DubbedAnimeProvider/src/main/kotlin/com/lagradost/DubbedAnimeProvider.kt deleted file mode 100644 index bb59f69..0000000 --- a/DubbedAnimeProvider/src/main/kotlin/com/lagradost/DubbedAnimeProvider.kt +++ /dev/null @@ -1,270 +0,0 @@ -package com.lagradost - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.APIHolder.unixTime -import com.lagradost.cloudstream3.APIHolder.unixTimeMS -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.getQualityFromName -import org.jsoup.Jsoup -import java.util.* - -class DubbedAnimeProvider : MainAPI() { - override var mainUrl = "https://bestdubbedanime.com" - override var name = "DubbedAnime" - override val hasQuickSearch = true - override val hasMainPage = true - - override val supportedTypes = setOf( - TvType.AnimeMovie, - TvType.Anime, - ) - - data class QueryEpisodeResultRoot( - @JsonProperty("result") - val result: QueryEpisodeResult, - ) - - data class QueryEpisodeResult( - @JsonProperty("anime") val anime: List, - @JsonProperty("error") val error: Boolean, - @JsonProperty("errorMSG") val errorMSG: String?, - ) - - data class EpisodeInfo( - @JsonProperty("serversHTML") val serversHTML: String, - @JsonProperty("title") val title: String, - @JsonProperty("preview_img") val previewImg: String?, - @JsonProperty("wideImg") val wideImg: String?, - @JsonProperty("year") val year: String?, - @JsonProperty("desc") val desc: String?, - - /* - @JsonProperty("rowid") val rowid: String, - @JsonProperty("status") val status: String, - @JsonProperty("skips") val skips: String, - @JsonProperty("totalEp") val totalEp: Long, - @JsonProperty("ep") val ep: String, - @JsonProperty("NextEp") val nextEp: Long, - @JsonProperty("slug") val slug: String, - @JsonProperty("showid") val showid: String, - @JsonProperty("Epviews") val epviews: String, - @JsonProperty("TotalViews") val totalViews: String, - @JsonProperty("tags") val tags: String,*/ - ) - - private suspend fun parseDocumentTrending(url: String): List { - val response = app.get(url).text - val document = Jsoup.parse(response) - return document.select("li > a").mapNotNull { - val href = fixUrl(it.attr("href")) - val title = it.selectFirst("> div > div.cittx")?.text() ?: return@mapNotNull null - val poster = fixUrlNull(it.selectFirst("> div > div.imghddde > img")?.attr("src")) - AnimeSearchResponse( - title, - href, - this.name, - TvType.Anime, - poster, - null, - EnumSet.of(DubStatus.Dubbed), - ) - } - } - - private suspend fun parseDocument( - url: String, - trimEpisode: Boolean = false - ): List { - val response = app.get(url).text - val document = Jsoup.parse(response) - return document.select("a.grid__link").mapNotNull { - val href = fixUrl(it.attr("href")) - val title = it.selectFirst("> div.gridtitlek")?.text() ?: return@mapNotNull null - val poster = - fixUrl(it.selectFirst("> img.grid__img")?.attr("src") ?: return@mapNotNull null) - AnimeSearchResponse( - title, - if (trimEpisode) href.removeRange(href.lastIndexOf('/'), href.length) else href, - this.name, - TvType.Anime, - poster, - null, - EnumSet.of(DubStatus.Dubbed), - ) - } - } - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val trendingUrl = "$mainUrl/xz/trending.php?_=$unixTimeMS" - val lastEpisodeUrl = "$mainUrl/xz/epgrid.php?p=1&_=$unixTimeMS" - val recentlyAddedUrl = "$mainUrl/xz/gridgrabrecent.php?p=1&_=$unixTimeMS" - //val allUrl = "$mainUrl/xz/gridgrab.php?p=1&limit=12&_=$unixTimeMS" - - val listItems = listOf( - HomePageList("Trending", parseDocumentTrending(trendingUrl)), - HomePageList("Recently Added", parseDocument(recentlyAddedUrl)), - HomePageList("Recent Releases", parseDocument(lastEpisodeUrl, true)), - // HomePageList("All", parseDocument(allUrl)) - ) - - return HomePageResponse(listItems) - } - - - private suspend fun getEpisode(slug: String, isMovie: Boolean): EpisodeInfo { - val url = - mainUrl + (if (isMovie) "/movies/jsonMovie" else "/xz/v3/jsonEpi") + ".php?slug=$slug&_=$unixTime" - val response = app.get(url).text - val mapped = parseJson(response) - return mapped.result.anime.first() - } - - - private fun getIsMovie(href: String): Boolean { - return href.contains("movies/") - } - - private fun getSlug(href: String): String { - return href.replace("$mainUrl/", "") - } - - override suspend fun quickSearch(query: String): List { - val url = "$mainUrl/xz/searchgrid.php?p=1&limit=12&s=$query&_=$unixTime" - val response = app.get(url).text - val document = Jsoup.parse(response) - val items = document.select("div.grid__item > a") - if (items.isEmpty()) return emptyList() - return items.mapNotNull { i -> - val href = fixUrl(i.attr("href")) - val title = i.selectFirst("div.gridtitlek")?.text() ?: return@mapNotNull null - val img = fixUrlNull(i.selectFirst("img.grid__img")?.attr("src")) - - if (getIsMovie(href)) { - MovieSearchResponse( - title, href, this.name, TvType.AnimeMovie, img, null - ) - } else { - AnimeSearchResponse( - title, - href, - this.name, - TvType.Anime, - img, - null, - EnumSet.of(DubStatus.Dubbed), - ) - } - } - } - - override suspend fun search(query: String): List { - val url = "$mainUrl/search/$query" - val response = app.get(url).text - val document = Jsoup.parse(response) - val items = document.select("div.resultinner > a.resulta") - if (items.isEmpty()) return ArrayList() - return items.mapNotNull { i -> - val innerDiv = i.selectFirst("> div.result") - val href = fixUrl(i.attr("href")) - val img = fixUrl(innerDiv?.selectFirst("> div.imgkz > img")?.attr("src") ?: return@mapNotNull null) - val title = innerDiv.selectFirst("> div.titleresults")?.text() ?: return@mapNotNull null - - if (getIsMovie(href)) { - MovieSearchResponse( - title, href, this.name, TvType.AnimeMovie, img, null - ) - } else { - AnimeSearchResponse( - title, - href, - this.name, - TvType.Anime, - img, - null, - EnumSet.of(DubStatus.Dubbed), - ) - } - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val serversHTML = (if (data.startsWith(mainUrl)) { // CLASSIC EPISODE - val slug = getSlug(data) - getEpisode(slug, false).serversHTML - } else data).replace("\\", "") - - val hls = ArrayList("hl=\"(.*?)\"".toRegex().findAll(serversHTML).map { - it.groupValues[1] - }.toList()) - for (hl in hls) { - try { - val sources = app.get("$mainUrl/xz/api/playeri.php?url=$hl&_=$unixTime").text - val find = "src=\"(.*?)\".*?label=\"(.*?)\"".toRegex().find(sources) - if (find != null) { - val quality = find.groupValues[2] - callback.invoke( - ExtractorLink( - this.name, - this.name + " " + quality + if (quality.endsWith('p')) "" else 'p', - fixUrl(find.groupValues[1]), - this.mainUrl, - getQualityFromName(quality) - ) - ) - } - } catch (e: Exception) { - //IDK - } - } - return true - } - - override suspend fun load(url: String): LoadResponse { - if (getIsMovie(url)) { - val realSlug = url.replace("movies/", "") - val episode = getEpisode(realSlug, true) - val poster = episode.previewImg ?: episode.wideImg - return MovieLoadResponse( - episode.title, - realSlug, - this.name, - TvType.AnimeMovie, - episode.serversHTML, - if (poster == null) null else fixUrl(poster), - episode.year?.toIntOrNull(), - episode.desc, - null - ) - } else { - val response = app.get(url).text - val document = Jsoup.parse(response) - val title = document.selectFirst("h4")!!.text() - val descriptHeader = document.selectFirst("div.animeDescript") - val descript = descriptHeader?.selectFirst("> p")?.text() - val year = descriptHeader?.selectFirst("> div.distatsx > div.sroverd") - ?.text() - ?.replace("Released: ", "") - ?.toIntOrNull() - - val episodes = document.select("a.epibloks").map { - val epTitle = it.selectFirst("> div.inwel > span.isgrxx")?.text() - Episode(fixUrl(it.attr("href")), epTitle) - } - - val img = fixUrl(document.select("div.fkimgs > img").attr("src")) - return newAnimeLoadResponse(title, url, TvType.Anime) { - posterUrl = img - this.year = year - addEpisodes(DubStatus.Dubbed, episodes) - plot = descript - } - } - } -} \ No newline at end of file diff --git a/DubbedAnimeProvider/src/main/kotlin/com/lagradost/DubbedAnimeProviderPlugin.kt b/DubbedAnimeProvider/src/main/kotlin/com/lagradost/DubbedAnimeProviderPlugin.kt deleted file mode 100644 index 939fe9f..0000000 --- a/DubbedAnimeProvider/src/main/kotlin/com/lagradost/DubbedAnimeProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class DubbedAnimeProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(DubbedAnimeProvider()) - } -} \ No newline at end of file diff --git a/EjaTv/build.gradle.kts b/EjaTv/build.gradle.kts deleted file mode 100644 index f5651cf..0000000 --- a/EjaTv/build.gradle.kts +++ /dev/null @@ -1,20 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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") -} \ No newline at end of file diff --git a/EjaTv/src/main/AndroidManifest.xml b/EjaTv/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/EjaTv/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/EjaTv/src/main/kotlin/com/lagradost/EjaTv.kt b/EjaTv/src/main/kotlin/com/lagradost/EjaTv.kt deleted file mode 100644 index d017ef5..0000000 --- a/EjaTv/src/main/kotlin/com/lagradost/EjaTv.kt +++ /dev/null @@ -1,120 +0,0 @@ -package com.lagradost - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.AppUtils.toJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import org.jsoup.nodes.Element - -class EjaTv : MainAPI() { - override var mainUrl = "https://eja.tv" - override var name = "Eja.tv" - - // Universal language? - override var lang = "en" - override val hasDownloadSupport = false - - override val hasMainPage = true - override val supportedTypes = setOf( - TvType.Live - ) - - private fun Element.toSearchResponse(): LiveSearchResponse? { - val link = this.select("div.alternative a").last() ?: return null - val href = fixUrl(link.attr("href")) - val img = this.selectFirst("div.thumb img") - val lang = this.selectFirst(".card-title > a")?.attr("href")?.removePrefix("?country=") - ?.replace("int", "eu") //international -> European Union 🇪🇺 - return LiveSearchResponse( - // Kinda hack way to get the title - img?.attr("alt")?.replaceFirst("Watch ", "") ?: return null, - href, - this@EjaTv.name, - TvType.Live, - fixUrl(img.attr("src")), - lang = lang - ) - } - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - // Maybe this based on app language or as setting? - val language = "English" - val dataMap = mapOf( - "News" to mapOf("language" to language, "category" to "News"), - "Sports" to mapOf("language" to language, "category" to "Sports"), - "Entertainment" to mapOf("language" to language, "category" to "Entertainment") - ) - return HomePageResponse(dataMap.apmap { (title, data) -> - val document = app.post(mainUrl, data = data).document - val shows = document.select("div.card-body").mapNotNull { - it.toSearchResponse() - } - HomePageList( - title, - shows, - isHorizontalImages = true - ) - }) - } - - override suspend fun search(query: String): List { - return app.post( - mainUrl, data = mapOf("search" to query) - ).document.select("div.card-body").mapNotNull { - it.toSearchResponse() - } - } - - override suspend fun load(url: String): LoadResponse { - val doc = app.get(url).document - val sections = - doc.select("li.list-group-item.d-flex.justify-content-between.align-items-center") - - val link = fixUrl(sections.last()!!.select("a").attr("href")) - - val title = doc.select("h5.text-center").text() - val poster = fixUrl(doc.select("p.text-center img").attr("src")) - - val summary = sections.subList(0, 3).joinToString("
") { - val innerText = it.ownText().trim() - val outerText = it.select("a").text().trim() - "$innerText: $outerText" - } - - return LiveStreamLoadResponse( - title, - url, - this.name, - LoadData(link, title).toJson(), - poster, - plot = summary - ) - } - - data class LoadData( - val url: String, - val title: String - ) - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val loadData = parseJson(data) - - callback.invoke( - ExtractorLink( - this.name, - loadData.title, - loadData.url, - "", - Qualities.Unknown.value, - isM3u8 = true - ) - ) - return true - } -} \ No newline at end of file diff --git a/EjaTv/src/main/kotlin/com/lagradost/EjaTvPlugin.kt b/EjaTv/src/main/kotlin/com/lagradost/EjaTvPlugin.kt deleted file mode 100644 index 644a44d..0000000 --- a/EjaTv/src/main/kotlin/com/lagradost/EjaTvPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class EjaTvPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(EjaTv()) - } -} \ No newline at end of file diff --git a/GogoanimeProvider/build.gradle.kts b/GogoanimeProvider/build.gradle.kts deleted file mode 100644 index 2ee6852..0000000 --- a/GogoanimeProvider/build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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=gogoanime.lu&sz=24" -} \ No newline at end of file diff --git a/GogoanimeProvider/src/main/AndroidManifest.xml b/GogoanimeProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/GogoanimeProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/GogoanimeProvider/src/main/kotlin/com/lagradost/GogoanimeProvider.kt b/GogoanimeProvider/src/main/kotlin/com/lagradost/GogoanimeProvider.kt deleted file mode 100644 index f8037ff..0000000 --- a/GogoanimeProvider/src/main/kotlin/com/lagradost/GogoanimeProvider.kt +++ /dev/null @@ -1,412 +0,0 @@ -package com.lagradost - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.mvvm.normalSafeApiCall -import com.lagradost.cloudstream3.mvvm.safeApiCall -import com.lagradost.cloudstream3.utils.AppUtils -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.Document -import java.net.URI -import java.util.* -import javax.crypto.Cipher -import javax.crypto.spec.IvParameterSpec -import javax.crypto.spec.SecretKeySpec - -class GogoanimeProvider : MainAPI() { - 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 - } - } - - /** - * @param id base64Decode(show_id) + IV - * @return the encryption key - * */ - private fun getKey(id: String): String? { - return normalSafeApiCall { - id.map { - it.code.toString(16) - }.joinToString("").substring(0, 32) - } - } - - val qualityRegex = Regex("(\\d+)P") - - // https://github.com/saikou-app/saikou/blob/3e756bd8e876ad7a9318b17110526880525a5cd3/app/src/main/java/ani/saikou/anime/source/extractors/GogoCDN.kt#L60 - // No Licence on the function - private fun cryptoHandler( - string: String, - iv: String, - secretKeyString: String, - encrypt: Boolean = true - ): String { - //println("IV: $iv, Key: $secretKeyString, encrypt: $encrypt, Message: $string") - val ivParameterSpec = IvParameterSpec(iv.toByteArray()) - val secretKey = SecretKeySpec(secretKeyString.toByteArray(), "AES") - val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") - return if (!encrypt) { - cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec) - String(cipher.doFinal(base64DecodeArray(string))) - } else { - cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec) - base64Encode(cipher.doFinal(string.toByteArray())) - } - } - - private fun String.decodeHex(): ByteArray { - check(length % 2 == 0) { "Must have an even length" } - return chunked(2) - .map { it.toInt(16).toByte() } - .toByteArray() - } - - /** - * @param iframeUrl something like https://gogoplay4.com/streaming.php?id=XXXXXX - * @param mainApiName used for ExtractorLink names and source - * @param iv secret iv from site, required non-null if isUsingAdaptiveKeys is off - * @param secretKey secret key for decryption from site, required non-null if isUsingAdaptiveKeys is off - * @param secretDecryptKey secret key to decrypt the response json, required non-null if isUsingAdaptiveKeys is off - * @param isUsingAdaptiveKeys generates keys from IV and ID, see getKey() - * @param isUsingAdaptiveData generate encrypt-ajax data based on $("script[data-name='episode']")[0].dataset.value - * */ - suspend fun extractVidstream( - iframeUrl: String, - mainApiName: String, - callback: (ExtractorLink) -> Unit, - iv: String?, - secretKey: String?, - secretDecryptKey: String?, - // This could be removed, but i prefer it verbose - isUsingAdaptiveKeys: Boolean, - isUsingAdaptiveData: Boolean, - // If you don't want to re-fetch the document - iframeDocument: Document? = null - ) = safeApiCall { - // https://github.com/saikou-app/saikou/blob/3e756bd8e876ad7a9318b17110526880525a5cd3/app/src/main/java/ani/saikou/anime/source/extractors/GogoCDN.kt - // No Licence on the following code - // Also modified of https://github.com/jmir1/aniyomi-extensions/blob/master/src/en/gogoanime/src/eu/kanade/tachiyomi/animeextension/en/gogoanime/extractors/GogoCdnExtractor.kt - // License on the code above https://github.com/jmir1/aniyomi-extensions/blob/master/LICENSE - - if ((iv == null || secretKey == null || secretDecryptKey == null) && !isUsingAdaptiveKeys) - return@safeApiCall - - val id = Regex("id=([^&]+)").find(iframeUrl)!!.value.removePrefix("id=") - - var document: Document? = iframeDocument - val foundIv = - iv ?: (document ?: app.get(iframeUrl).document.also { document = it }) - .select("""div.wrapper[class*=container]""") - .attr("class").split("-").lastOrNull() ?: return@safeApiCall - val foundKey = secretKey ?: getKey(base64Decode(id) + foundIv) ?: return@safeApiCall - val foundDecryptKey = secretDecryptKey ?: foundKey - - val uri = URI(iframeUrl) - val mainUrl = "https://" + uri.host - - val encryptedId = cryptoHandler(id, foundIv, foundKey) - val encryptRequestData = if (isUsingAdaptiveData) { - // Only fetch the document if necessary - val realDocument = document ?: app.get(iframeUrl).document - val dataEncrypted = - realDocument.select("script[data-name='episode']").attr("data-value") - val headers = cryptoHandler(dataEncrypted, foundIv, foundKey, false) - "id=$encryptedId&alias=$id&" + headers.substringAfter("&") - } else { - "id=$encryptedId&alias=$id" - } - - val jsonResponse = - app.get( - "$mainUrl/encrypt-ajax.php?$encryptRequestData", - headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ) - val dataencrypted = - jsonResponse.text.substringAfter("{\"data\":\"").substringBefore("\"}") - val datadecrypted = cryptoHandler(dataencrypted, foundIv, foundDecryptKey, false) - val sources = AppUtils.parseJson(datadecrypted) - - fun invokeGogoSource( - source: GogoSource, - sourceCallback: (ExtractorLink) -> Unit - ) { - sourceCallback.invoke( - ExtractorLink( - mainApiName, - mainApiName, - source.file, - mainUrl, - getQualityFromName(source.label), - isM3u8 = source.type == "hls" || source.label?.contains( - "auto", - ignoreCase = true - ) == true - ) - ) - } - - sources.source?.forEach { - invokeGogoSource(it, callback) - } - sources.sourceBk?.forEach { - invokeGogoSource(it, callback) - } - } - } - - override var mainUrl = "https://gogoanime.lu" - override var name = "GogoAnime" - override val hasQuickSearch = false - override val hasMainPage = true - - override val supportedTypes = setOf( - TvType.AnimeMovie, - TvType.Anime, - TvType.OVA - ) - - val headers = mapOf( - "authority" to "ajax.gogo-load.com", - "sec-ch-ua" to "\"Google Chrome\";v=\"89\", \"Chromium\";v=\"89\", \";Not A Brand\";v=\"99\"", - "accept" to "text/html, */*; q=0.01", - "dnt" to "1", - "sec-ch-ua-mobile" to "?0", - "user-agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36", - "origin" to mainUrl, - "sec-fetch-site" to "cross-site", - "sec-fetch-mode" to "cors", - "sec-fetch-dest" to "empty", - "referer" to "$mainUrl/" - ) - val parseRegex = - Regex("""
  • \s*\n.*\n.*\n.*?img src="(.*?)"""") - - override val mainPage = mainPageOf( - Pair("1", "Recent Release - Sub"), - Pair("2", "Recent Release - Dub"), - Pair("3", "Recent Release - Chinese"), - ) - - override suspend fun getMainPage( - page: Int, - request : MainPageRequest - ): HomePageResponse { - val params = mapOf("page" to page.toString(), "type" to request.data) - val html = app.get( - "https://ajax.gogo-load.com/ajax/page-recent-release.html", - headers = headers, - params = params - ) - val isSub = listOf(1, 3).contains(request.data.toInt()) - - val home = parseRegex.findAll(html.text).map { - val (link, epNum, title, poster) = it.destructured - newAnimeSearchResponse(title, link) { - this.posterUrl = poster - addDubStatus(!isSub, epNum.toIntOrNull()) - } - }.toList() - - return newHomePageResponse(request.name, home) - } - - override suspend fun search(query: String): ArrayList { - val link = "$mainUrl/search.html?keyword=$query" - val html = app.get(link).text - val doc = Jsoup.parse(html) - - val episodes = doc.select(""".last_episodes li""").mapNotNull { - AnimeSearchResponse( - it.selectFirst(".name")?.text()?.replace(" (Dub)", "") ?: return@mapNotNull null, - fixUrl(it.selectFirst(".name > a")?.attr("href") ?: return@mapNotNull null), - this.name, - TvType.Anime, - it.selectFirst("img")?.attr("src"), - it.selectFirst(".released")?.text()?.split(":")?.getOrNull(1)?.trim() - ?.toIntOrNull(), - if (it.selectFirst(".name")?.text() - ?.contains("Dub") == true - ) EnumSet.of(DubStatus.Dubbed) else EnumSet.of( - DubStatus.Subbed - ), - ) - } - - return ArrayList(episodes) - } - - private fun getProperAnimeLink(uri: String): String { - if (uri.contains("-episode")) { - val split = uri.split("/") - val slug = split[split.size - 1].split("-episode")[0] - return "$mainUrl/category/$slug" - } - return uri - } - - override suspend fun load(url: String): LoadResponse { - val link = getProperAnimeLink(url) - val episodeloadApi = "https://ajax.gogo-load.com/ajax/load-list-episode" - val doc = app.get(link).document - - val animeBody = doc.selectFirst(".anime_info_body_bg") - val title = animeBody?.selectFirst("h1")!!.text() - val poster = animeBody.selectFirst("img")?.attr("src") - var description: String? = null - val genre = ArrayList() - var year: Int? = null - var status: String? = null - var nativeName: String? = null - var type: String? = null - - animeBody.select("p.type").forEach { pType -> - when (pType.selectFirst("span")?.text()?.trim()) { - "Plot Summary:" -> { - description = pType.text().replace("Plot Summary:", "").trim() - } - "Genre:" -> { - genre.addAll(pType.select("a").map { - it.attr("title") - }) - } - "Released:" -> { - year = pType.text().replace("Released:", "").trim().toIntOrNull() - } - "Status:" -> { - status = pType.text().replace("Status:", "").trim() - } - "Other name:" -> { - nativeName = pType.text().replace("Other name:", "").trim() - } - "Type:" -> { - type = pType.text().replace("type:", "").trim() - } - } - } - - val animeId = doc.selectFirst("#movie_id")!!.attr("value") - val params = mapOf("ep_start" to "0", "ep_end" to "2000", "id" to animeId) - - val episodes = app.get(episodeloadApi, params = params).document.select("a").map { - Episode( - fixUrl(it.attr("href").trim()), - "Episode " + it.selectFirst(".name")?.text()?.replace("EP", "")?.trim() - ) - }.reversed() - - return newAnimeLoadResponse(title, link, getType(type.toString())) { - japName = nativeName - engName = title - posterUrl = poster - this.year = year - addEpisodes(DubStatus.Subbed, episodes) // TODO CHECK - plot = description - tags = genre - - showStatus = getStatus(status.toString()) - } - } - - data class GogoSources( - @JsonProperty("source") val source: List?, - @JsonProperty("sourceBk") val sourceBk: List?, - //val track: List, - //val advertising: List, - //val linkiframe: String - ) - - data class GogoSource( - @JsonProperty("file") val file: String, - @JsonProperty("label") val label: String?, - @JsonProperty("type") val type: String?, - @JsonProperty("default") val default: String? = null - ) - - private suspend fun extractVideos( - uri: String, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ) { - val doc = app.get(uri).document - - val iframe = fixUrlNull(doc.selectFirst("div.play-video > iframe")?.attr("src")) ?: return - - argamap( - { - val link = iframe.replace("streaming.php", "download") - val page = app.get(link, headers = mapOf("Referer" to iframe)) - - page.document.select(".dowload > a").apmap { - if (it.hasAttr("download")) { - val qual = if (it.text() - .contains("HDP") - ) "1080" else qualityRegex.find(it.text())?.destructured?.component1() - .toString() - callback( - ExtractorLink( - "Gogoanime", - "Gogoanime", - it.attr("href"), - page.url, - getQualityFromName(qual), - it.attr("href").contains(".m3u8") - ) - ) - } else { - val url = it.attr("href") - loadExtractor(url, null, subtitleCallback, callback) - } - } - }, { - val streamingResponse = app.get(iframe, headers = mapOf("Referer" to iframe)) - val streamingDocument = streamingResponse.document - argamap({ - streamingDocument.select(".list-server-items > .linkserver") - .forEach { element -> - val status = element.attr("data-status") ?: return@forEach - if (status != "1") return@forEach - val data = element.attr("data-video") ?: return@forEach - loadExtractor(data, streamingResponse.url, subtitleCallback, callback) - } - }, { - val iv = "3134003223491201" - val secretKey = "37911490979715163134003223491201" - val secretDecryptKey = "54674138327930866480207815084989" - extractVidstream( - iframe, - this.name, - callback, - iv, - secretKey, - secretDecryptKey, - isUsingAdaptiveKeys = false, - isUsingAdaptiveData = true - ) - }) - } - ) - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - extractVideos(data, subtitleCallback, callback) - return true - } -} diff --git a/GogoanimeProvider/src/main/kotlin/com/lagradost/GogoanimeProviderPlugin.kt b/GogoanimeProvider/src/main/kotlin/com/lagradost/GogoanimeProviderPlugin.kt deleted file mode 100644 index 34e0fb1..0000000 --- a/GogoanimeProvider/src/main/kotlin/com/lagradost/GogoanimeProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class GogoanimeProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(GogoanimeProvider()) - } -} \ No newline at end of file diff --git a/HDMProvider/build.gradle.kts b/HDMProvider/build.gradle.kts deleted file mode 100644 index 1e957a0..0000000 --- a/HDMProvider/build.gradle.kts +++ /dev/null @@ -1,23 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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", - ) - - } \ No newline at end of file diff --git a/HDMProvider/src/main/AndroidManifest.xml b/HDMProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/HDMProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/HDMProvider/src/main/kotlin/com/lagradost/HDMProvider.kt b/HDMProvider/src/main/kotlin/com/lagradost/HDMProvider.kt deleted file mode 100644 index f9378ca..0000000 --- a/HDMProvider/src/main/kotlin/com/lagradost/HDMProvider.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.lagradost - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import org.jsoup.Jsoup - -class HDMProvider : MainAPI() { - override var name = "HD Movies" - override var mainUrl = "https://hdm.to" - override val hasMainPage = true - - override val supportedTypes = setOf( - TvType.Movie, - ) - - override suspend fun search(query: String): List { - val url = "$mainUrl/search/$query" - val response = app.get(url).text - val document = Jsoup.parse(response) - val items = document.select("div.col-md-2 > article > a") - if (items.isEmpty()) return emptyList() - - return items.map { i -> - val href = i.attr("href") - val data = i.selectFirst("> div.item")!! - val img = data.selectFirst("> img")!!.attr("src") - val name = data.selectFirst("> div.movie-details")!!.text() - MovieSearchResponse(name, href, this.name, TvType.Movie, img, null) - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - if (data == "") return false - val slug = Regex(".*/(.*?)\\.mp4").find(data)?.groupValues?.get(1) ?: return false - val response = app.get(data).text - val key = Regex("playlist\\.m3u8(.*?)\"").find(response)?.groupValues?.get(1) ?: return false - callback.invoke( - ExtractorLink( - this.name, - this.name, - "https://hls.1o.to/vod/$slug/playlist.m3u8$key", - "", - Qualities.P720.value, - true - ) - ) - return true - } - - override suspend fun load(url: String): LoadResponse? { - val response = app.get(url).text - val document = Jsoup.parse(response) - val title = document.selectFirst("h2.movieTitle")?.text() ?: throw ErrorLoadingException("No Data Found") - val poster = document.selectFirst("div.post-thumbnail > img")!!.attr("src") - val descript = document.selectFirst("div.synopsis > p")!!.text() - val year = document.select("div.movieInfoAll > div.row > div.col-md-6").getOrNull(1)?.selectFirst("> p > a")?.text() - ?.toIntOrNull() - val data = "src/player/\\?v=(.*?)\"".toRegex().find(response)?.groupValues?.get(1) ?: return null - - return MovieLoadResponse( - title, url, this.name, TvType.Movie, - "$mainUrl/src/player/?v=$data", poster, year, descript, null - ) - } - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val html = app.get(mainUrl, timeout = 25).text - val document = Jsoup.parse(html) - val all = ArrayList() - - val mainbody = document.getElementsByTag("body") - ?.select("div.homeContentOuter > section > div.container > div") - // Fetch row title - val inner = mainbody?.select("div.col-md-2.col-sm-2.mrgb") - val title = mainbody?.select("div > div")?.firstOrNull()?.select("div.title.titleBar")?.text() ?: "Unnamed Row" - // Fetch list of items and map - if (inner != null) { - val elements: List = inner.map { - - val aa = it.select("a").firstOrNull() - val item = aa?.select("div.item") - val href = aa?.attr("href") - val link = when (href != null) { - true -> fixUrl(href) - false -> "" - } - val name = item?.select("div.movie-details")?.text() ?: "" - var image = item?.select("img")?.get(1)?.attr("src") ?: "" - val year = null - - MovieSearchResponse( - name, - link, - this.name, - TvType.Movie, - image, - year, - null, - ) - } - - all.add( - HomePageList( - title, elements - ) - ) - } - return HomePageResponse(all) - } -} \ No newline at end of file diff --git a/HDMProvider/src/main/kotlin/com/lagradost/HDMProviderPlugin.kt b/HDMProvider/src/main/kotlin/com/lagradost/HDMProviderPlugin.kt deleted file mode 100644 index f2eaf2f..0000000 --- a/HDMProvider/src/main/kotlin/com/lagradost/HDMProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class HDMProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(HDMProvider()) - } -} \ No newline at end of file diff --git a/IHaveNoTvProvider/build.gradle.kts b/IHaveNoTvProvider/build.gradle.kts deleted file mode 100644 index e161bca..0000000 --- a/IHaveNoTvProvider/build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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", - "Documentary", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=ihavenotv.com&sz=24" -} \ No newline at end of file diff --git a/IHaveNoTvProvider/src/main/AndroidManifest.xml b/IHaveNoTvProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/IHaveNoTvProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/IHaveNoTvProvider/src/main/kotlin/com/lagradost/IHaveNoTvProvider.kt b/IHaveNoTvProvider/src/main/kotlin/com/lagradost/IHaveNoTvProvider.kt deleted file mode 100644 index 8f73130..0000000 --- a/IHaveNoTvProvider/src/main/kotlin/com/lagradost/IHaveNoTvProvider.kt +++ /dev/null @@ -1,222 +0,0 @@ -package com.lagradost - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.Jsoup -import java.net.URLEncoder - -class IHaveNoTvProvider : MainAPI() { - override var mainUrl = "https://ihavenotv.com" - override var name = "I Have No TV" - override val hasQuickSearch = false - override val hasMainPage = true - - override val supportedTypes = setOf(TvType.Documentary) - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - // Uhh, I am too lazy to scrape the "latest documentaries" and "recommended documentaries", - // so I am just scraping 3 random categories - val allCategories = listOf( - "astronomy", - "brain", - "creativity", - "design", - "economics", - "environment", - "health", - "history", - "lifehack", - "math", - "music", - "nature", - "people", - "physics", - "science", - "technology", - "travel" - ) - - val categories = allCategories.asSequence().shuffled().take(3) - .toList() // randomly get 3 categories, because there are too many - - val items = ArrayList() - - categories.forEach { cat -> - val link = "$mainUrl/category/$cat" - val html = app.get(link).text - val soup = Jsoup.parse(html) - - val searchResults: MutableMap = mutableMapOf() - soup.select(".episodesDiv .episode").forEach { res -> - val poster = res.selectFirst("img")?.attr("src") - val aTag = if (res.html().contains("/series/")) { - res.selectFirst(".episodeMeta > a") - } else { - res.selectFirst("a[href][title]") - } - val year = Regex("""•?\s+(\d{4})\s+•""").find( - res.selectFirst(".episodeMeta")!!.text() - )?.destructured?.component1()?.toIntOrNull() - - val title = aTag!!.attr("title") - val href = fixUrl(aTag.attr("href")) - searchResults[href] = TvSeriesSearchResponse( - title, - href, - this.name, - TvType.Documentary,//if (href.contains("/series/")) TvType.TvSeries else TvType.Movie, - poster, - year, - null - ) - } - items.add( - HomePageList( - capitalizeString(cat), - ArrayList(searchResults.values).subList(0, 5) - ) - ) // just 5 results per category, app crashes when they are too many - } - - return HomePageResponse(items) - } - - override suspend fun search(query: String): ArrayList { - val url = """$mainUrl/search/${URLEncoder.encode(query, "UTF-8")}""" - val response = app.get(url).text - val soup = Jsoup.parse(response) - - val searchResults: MutableMap = mutableMapOf() - - soup.select(".episodesDiv .episode").forEach { res -> - val poster = res.selectFirst("img")?.attr("src") - val aTag = if (res.html().contains("/series/")) { - res.selectFirst(".episodeMeta > a") - } else { - res.selectFirst("a[href][title]") - } - val year = - Regex("""•?\s+(\d{4})\s+•""").find( - res.selectFirst(".episodeMeta")!!.text() - )?.destructured?.component1() - ?.toIntOrNull() - - val title = aTag!!.attr("title") - val href = fixUrl(aTag.attr("href")) - searchResults[href] = TvSeriesSearchResponse( - title, - href, - this.name, - TvType.Documentary, //if (href.contains("/series/")) TvType.TvSeries else TvType.Movie, - poster, - year, - null - ) - } - - return ArrayList(searchResults.values) - } - - override suspend fun load(url: String): LoadResponse { - val isSeries = url.contains("/series/") - val html = app.get(url).text - val soup = Jsoup.parse(html) - - val container = soup.selectFirst(".container-fluid h1")?.parent() - val title = if (isSeries) { - container?.selectFirst("h1")?.text()?.split("•")?.firstOrNull().toString() - } else soup.selectFirst(".videoDetails")!!.selectFirst("strong")?.text().toString() - val description = if (isSeries) { - container?.selectFirst("p")?.text() - } else { - soup.selectFirst(".videoDetails > p")?.text() - } - - var year: Int? = null - val categories: MutableSet = mutableSetOf() - - val episodes = if (isSeries) { - container?.select(".episode")?.map { ep -> - val thumb = ep.selectFirst("img")!!.attr("src") - - val epLink = fixUrl(ep.selectFirst("a[title]")!!.attr("href")) - val (season, epNum) = if (ep.selectFirst(".episodeMeta > strong") != null && - ep.selectFirst(".episodeMeta > strong")!!.html().contains("S") - ) { - val split = ep.selectFirst(".episodeMeta > strong")?.text()?.split("E") - Pair( - split?.firstOrNull()?.replace("S", "")?.toIntOrNull(), - split?.get(1)?.toIntOrNull() - ) - } else Pair(null, null) - - year = Regex("""•?\s+(\d{4})\s+•""").find( - ep.selectFirst(".episodeMeta")!!.text() - )?.destructured?.component1()?.toIntOrNull() - - categories.addAll( - ep.select(".episodeMeta > a[href*=\"/category/\"]").map { it.text().trim() }) - - newEpisode(epLink) { - this.name = ep.selectFirst("a[title]")!!.attr("title") - this.season = season - this.episode = epNum - this.posterUrl = thumb - this.description = ep.selectFirst(".episodeSynopsis")?.text() - } - } - } else { - listOf(MovieLoadResponse( - title, - url, - this.name, - TvType.Movie, - url, - soup.selectFirst("[rel=\"image_src\"]")!!.attr("href"), - Regex("""•?\s+(\d{4})\s+•""").find( - soup.selectFirst(".videoDetails")!!.text() - )?.destructured?.component1()?.toIntOrNull(), - description, - null, - soup.selectFirst(".videoDetails")!!.select("a[href*=\"/category/\"]") - .map { it.text().trim() } - )) - } - - val poster = episodes?.firstOrNull().let { - if (isSeries && it != null) (it as Episode).posterUrl - else null - } - - return if (isSeries) TvSeriesLoadResponse( - title, - url, - this.name, - TvType.TvSeries, - episodes!!.map { it as Episode }, - poster, - year, - description, - null, - null, - categories.toList() - ) else (episodes?.first() as MovieLoadResponse) - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val html = app.get(data).text - val soup = Jsoup.parse(html) - - val iframe = soup.selectFirst("#videoWrap iframe") - if (iframe != null) { - loadExtractor(iframe.attr("src"), null, subtitleCallback, callback) - } - return true - } -} diff --git a/IHaveNoTvProvider/src/main/kotlin/com/lagradost/IHaveNoTvProviderPlugin.kt b/IHaveNoTvProvider/src/main/kotlin/com/lagradost/IHaveNoTvProviderPlugin.kt deleted file mode 100644 index b888b7e..0000000 --- a/IHaveNoTvProvider/src/main/kotlin/com/lagradost/IHaveNoTvProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class IHaveNoTvProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(IHaveNoTvProvider()) - } -} \ No newline at end of file diff --git a/KawaiifuProvider/build.gradle.kts b/KawaiifuProvider/build.gradle.kts deleted file mode 100644 index d2e3e37..0000000 --- a/KawaiifuProvider/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=kawaiifu.com&sz=24" -} \ No newline at end of file diff --git a/KawaiifuProvider/src/main/AndroidManifest.xml b/KawaiifuProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/KawaiifuProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/KawaiifuProvider/src/main/kotlin/com/lagradost/KawaiifuProvider.kt b/KawaiifuProvider/src/main/kotlin/com/lagradost/KawaiifuProvider.kt deleted file mode 100644 index 84567f2..0000000 --- a/KawaiifuProvider/src/main/kotlin/com/lagradost/KawaiifuProvider.kt +++ /dev/null @@ -1,174 +0,0 @@ -package com.lagradost - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.getQualityFromName -import org.jsoup.Jsoup -import java.util.* - -class KawaiifuProvider : MainAPI() { - override var mainUrl = "https://kawaiifu.com" - override var name = "Kawaiifu" - override val hasQuickSearch = false - override val hasMainPage = true - - override val supportedTypes = setOf(TvType.Anime, TvType.AnimeMovie) - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val items = ArrayList() - val resp = app.get(mainUrl).text - - val soup = Jsoup.parse(resp) - - items.add(HomePageList("Latest Updates", soup.select(".today-update .item").mapNotNull { - val title = it.selectFirst("img")?.attr("alt") - AnimeSearchResponse( - title ?: return@mapNotNull null, - it.selectFirst("a")?.attr("href") ?: return@mapNotNull null, - this.name, - TvType.Anime, - it.selectFirst("img")?.attr("src"), - it.selectFirst("h4 > a")?.attr("href")?.split("-")?.last()?.toIntOrNull(), - if (title.contains("(DUB)")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of( - DubStatus.Subbed - ), - ) - })) - for (section in soup.select(".section")) { - try { - val title = section.selectFirst(".title")!!.text() - val anime = section.select(".list-film > .item").mapNotNull { ani -> - val animTitle = ani.selectFirst("img")?.attr("alt") - AnimeSearchResponse( - animTitle ?: return@mapNotNull null, - ani.selectFirst("a")?.attr("href") ?: return@mapNotNull null, - this.name, - TvType.Anime, - ani.selectFirst("img")?.attr("src"), - ani.selectFirst(".vl-chil-date")?.text()?.toIntOrNull(), - if (animTitle.contains("(DUB)")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of( - DubStatus.Subbed - ), - ) - } - items.add(HomePageList(title, anime)) - - } catch (e: Exception) { - e.printStackTrace() - } - } - if (items.size <= 0) throw ErrorLoadingException() - return HomePageResponse(items) - } - - - override suspend fun search(query: String): ArrayList { - val link = "$mainUrl/search-movie?keyword=${query}" - val html = app.get(link).text - val soup = Jsoup.parse(html) - - return ArrayList(soup.select(".item").mapNotNull { - val year = it.selectFirst("h4 > a")?.attr("href")?.split("-")?.last()?.toIntOrNull() - val title = it.selectFirst("img")?.attr("alt") ?: return@mapNotNull null - val poster = it.selectFirst("img")?.attr("src") - val uri = it.selectFirst("a")?.attr("href") ?: return@mapNotNull null - AnimeSearchResponse( - title, - uri, - this.name, - TvType.Anime, - poster, - year, - if (title.contains("(DUB)")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of(DubStatus.Subbed), - ) - }) - } - - override suspend fun load(url: String): LoadResponse { - val html = app.get(url).text - val soup = Jsoup.parse(html) - - val title = soup.selectFirst(".title")!!.text() - val tags = soup.select(".table a[href*=\"/tag/\"]").map { tag -> tag.text() } - val description = soup.select(".sub-desc p") - .filter { it -> it.select("strong").isEmpty() && it.select("iframe").isEmpty() } - .joinToString("\n") { it.text() } - val year = url.split("/").filter { it.contains("-") }[0].split("-")[1].toIntOrNull() - - val episodesLink = soup.selectFirst("a[href*=\".html-episode\"]")?.attr("href") - ?: throw ErrorLoadingException("Error getting episode list") - val episodes = Jsoup.parse( - app.get(episodesLink).text - ).selectFirst(".list-ep")?.select("li")?.map { - Episode( - it.selectFirst("a")!!.attr("href"), - if (it.text().trim().toIntOrNull() != null) "Episode ${ - it.text().trim() - }" else it.text().trim() - ) - } - val poster = soup.selectFirst("a.thumb > img")?.attr("src") - - return newAnimeLoadResponse(title, url, TvType.Anime) { - this.year = year - posterUrl = poster - addEpisodes(DubStatus.Subbed, episodes) - plot = description - this.tags = tags - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val htmlSource = app.get(data).text - val soupa = Jsoup.parse(htmlSource) - - val episodeNum = - if (data.contains("ep=")) data.split("ep=")[1].split("&")[0].toIntOrNull() else null - - val servers = soupa.select(".list-server").map { - val serverName = it.selectFirst(".server-name")!!.text() - val episodes = it.select(".list-ep > li > a") - .map { episode -> Pair(episode.attr("href"), episode.text()) } - val episode = if (episodeNum == null) episodes[0] else episodes.mapNotNull { ep -> - if ((if (ep.first.contains("ep=")) ep.first.split("ep=")[1].split("&")[0].toIntOrNull() else null) == episodeNum) { - ep - } else null - }[0] - Pair(serverName, episode) - }.map { - if (it.second.first == data) { - val sources = soupa.select("video > source") - .map { source -> Pair(source.attr("src"), source.attr("data-quality")) } - Triple(it.first, sources, it.second.second) - } else { - val html = app.get(it.second.first).text - val soup = Jsoup.parse(html) - - val sources = soup.select("video > source") - .map { source -> Pair(source.attr("src"), source.attr("data-quality")) } - Triple(it.first, sources, it.second.second) - } - } - - servers.forEach { - it.second.forEach { source -> - callback( - ExtractorLink( - "Kawaiifu", - it.first, - source.first, - "", - getQualityFromName(source.second), - source.first.contains(".m3u") - ) - ) - } - } - return true - } -} diff --git a/KawaiifuProvider/src/main/kotlin/com/lagradost/KawaiifuProviderPlugin.kt b/KawaiifuProvider/src/main/kotlin/com/lagradost/KawaiifuProviderPlugin.kt deleted file mode 100644 index a04c63d..0000000 --- a/KawaiifuProvider/src/main/kotlin/com/lagradost/KawaiifuProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class KawaiifuProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(KawaiifuProvider()) - } -} \ No newline at end of file diff --git a/KimCartoonProvider/build.gradle.kts b/KimCartoonProvider/build.gradle.kts deleted file mode 100644 index 4e8de17..0000000 --- a/KimCartoonProvider/build.gradle.kts +++ /dev/null @@ -1,24 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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( - "Cartoon", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=kimcartoon.li&sz=24" -} \ No newline at end of file diff --git a/KimCartoonProvider/src/main/AndroidManifest.xml b/KimCartoonProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/KimCartoonProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/KimCartoonProvider/src/main/kotlin/com/lagradost/KimCartoonProvider.kt b/KimCartoonProvider/src/main/kotlin/com/lagradost/KimCartoonProvider.kt deleted file mode 100644 index fcc2c5e..0000000 --- a/KimCartoonProvider/src/main/kotlin/com/lagradost/KimCartoonProvider.kt +++ /dev/null @@ -1,152 +0,0 @@ -package com.lagradost - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor - -class KimCartoonProvider : MainAPI() { - - override var mainUrl = "https://kimcartoon.li" - override var name = "Kim Cartoon" - override val hasQuickSearch = true - override val hasMainPage = true - - override val supportedTypes = setOf(TvType.Cartoon) - - private fun fixUrl(url: String): String { - return if (url.startsWith("/")) mainUrl + url else url - } - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val doc = app.get(mainUrl).document.select("#container") - val response = mutableListOf( - HomePageList( - "Latest Update", - doc.select("div.bigBarContainer div.items > div > a").map { - AnimeSearchResponse( - it.select(".item-title").let { div -> - //Because it doesn't contain Title separately - div.text().replace(div.select("span").text(), "") - }, - mainUrl + it.attr("href"), - mainUrl, - TvType.Cartoon, - fixUrl(it.select("img").let { img -> - img.attr("src").let { src -> - src.ifEmpty { img.attr("srctemp") } - } - }) - ) - } - ) - ) - val list = mapOf( - "Top Day" to "tab-top-day", - "Top Week" to "tab-top-week", - "Top Month" to "tab-top-month", - "New Cartoons" to "tab-newest-series" - ) - response.addAll(list.map { item -> - HomePageList( - item.key, - doc.select("#${item.value} > div").map { - AnimeSearchResponse( - it.select("span.title").text(), - mainUrl + it.select("a")[0].attr("href"), - mainUrl, - TvType.Cartoon, - fixUrl(it.select("a > img").attr("src")) - ) - } - ) - }) - return HomePageResponse(response) - } - - override suspend fun search(query: String): List { - return app.post( - "$mainUrl/Search/Cartoon", - data = mapOf("keyword" to query) - ).document - .select("#leftside > div.bigBarContainer div.list-cartoon > div.item > a") - .map { - AnimeSearchResponse( - it.select("span").text(), - mainUrl + it.attr("href"), - mainUrl, - TvType.Cartoon, - fixUrl(it.select("img").attr("src")) - ) - } - } - - override suspend fun quickSearch(query: String): List { - return app.post( - "$mainUrl/Ajax/SearchSuggest", - data = mapOf("keyword" to query) - ).document.select("a").map { - AnimeSearchResponse( - it.text(), - it.attr("href"), - mainUrl, - TvType.Cartoon, - ) - } - } - - - private fun getStatus(from: String?): ShowStatus? { - return when { - from?.contains("Completed") == true -> ShowStatus.Completed - from?.contains("Ongoing") == true -> ShowStatus.Ongoing - else -> null - } - } - - override suspend fun load(url: String): LoadResponse { - val doc = app.get(url).document.select("#leftside") - val info = doc.select("div.barContent") - val name = info.select("a.bigChar").text() - val eps = doc.select("table.listing > tbody > tr a").reversed().map { - Episode( - fixUrl(it.attr("href")), - it.text().replace(name, "").trim() - ) - } - val infoText = info.text() - fun getData(after: String, before: String): String? { - return if (infoText.contains(after)) - infoText - .substringAfter("$after:") - .substringBefore(before) - .trim() - else null - } - - return newTvSeriesLoadResponse(name, url, TvType.Cartoon, eps) { - posterUrl = fixUrl(info.select("div > img").attr("src")) - showStatus = getStatus(getData("Status", "Views")) - plot = getData("Summary", "Tags:") - tags = getData("Genres", "Date aired")?.split(",") - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val servers = - app.get(data).document.select("#selectServer > option").map { fixUrl(it.attr("value")) } - servers.apmap { - app.get(it).document.select("#my_video_1").attr("src").let { iframe -> - if (iframe.isNotEmpty()) { - loadExtractor(iframe, "$mainUrl/", subtitleCallback, callback) - } - //There are other servers, but they require some work to do - } - } - return true - } -} \ No newline at end of file diff --git a/KimCartoonProvider/src/main/kotlin/com/lagradost/KimCartoonProviderPlugin.kt b/KimCartoonProvider/src/main/kotlin/com/lagradost/KimCartoonProviderPlugin.kt deleted file mode 100644 index 9cbee8d..0000000 --- a/KimCartoonProvider/src/main/kotlin/com/lagradost/KimCartoonProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class KimCartoonProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(KimCartoonProvider()) - } -} \ No newline at end of file diff --git a/KisskhProvider/build.gradle.kts b/KisskhProvider/build.gradle.kts deleted file mode 100644 index 3456beb..0000000 --- a/KisskhProvider/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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", - "Anime", - "Movie", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=kisskh.me&sz=24" -} \ No newline at end of file diff --git a/KisskhProvider/src/main/AndroidManifest.xml b/KisskhProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/KisskhProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/KisskhProvider/src/main/kotlin/com/lagradost/KisskhProvider.kt b/KisskhProvider/src/main/kotlin/com/lagradost/KisskhProvider.kt deleted file mode 100644 index 940bb19..0000000 --- a/KisskhProvider/src/main/kotlin/com/lagradost/KisskhProvider.kt +++ /dev/null @@ -1,208 +0,0 @@ -package com.lagradost - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.mvvm.safeApiCall -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.AppUtils.toJson -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.M3u8Helper -import com.lagradost.cloudstream3.utils.loadExtractor -import java.util.ArrayList - -class KisskhProvider : MainAPI() { - override var mainUrl = "https://kisskh.me" - override var name = "Kisskh" - override val hasMainPage = true - override val hasDownloadSupport = true - override val supportedTypes = setOf( - TvType.AsianDrama, - TvType.Anime - ) - - override val mainPage = mainPageOf( - "&type=2&sub=0&country=2&status=0&order=1" to "Movie Popular", - "&type=2&sub=0&country=2&status=0&order=2" to "Movie Last Update", - "&type=1&sub=0&country=2&status=0&order=1" to "TVSeries Popular", - "&type=1&sub=0&country=2&status=0&order=2" to "TVSeries Last Update", - "&type=3&sub=0&country=0&status=0&order=1" to "Anime Popular", - "&type=3&sub=0&country=0&status=0&order=2" to "Anime Last Update", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val home = app.get("$mainUrl/api/DramaList/List?page=$page${request.data}") - .parsedSafe()?.data - ?.mapNotNull { media -> - media.toSearchResponse() - } ?: throw ErrorLoadingException("Invalid Json reponse") - return newHomePageResponse(request.name, home) - } - - private fun Media.toSearchResponse(): SearchResponse? { - - return newAnimeSearchResponse( - title ?: return null, - "$title/$id", - TvType.TvSeries, - ) { - this.posterUrl = thumbnail - addSub(episodesCount) - } - } - - override suspend fun search(query: String): List { - val searchResponse = - app.get("$mainUrl/api/DramaList/Search?q=$query&type=0", referer = "$mainUrl/").text - return tryParseJson>(searchResponse)?.mapNotNull { media -> - media.toSearchResponse() - } ?: throw ErrorLoadingException("Invalid Json reponse") - } - - private fun getTitle(str: String): String { - return str.replace(Regex("[^a-zA-Z0-9]"), "-") - } - - override suspend fun load(url: String): LoadResponse? { - val id = url.split("/") - val res = app.get( - "$mainUrl/api/DramaList/Drama/${id.last()}?isq=false", - referer = "$mainUrl/Drama/${ - getTitle(id.first()) - }?id=${id.last()}" - ).parsedSafe() - ?: throw ErrorLoadingException("Invalid Json reponse") - - val episodes = res.episodes?.map { eps -> - Episode( - data = Data(res.title, eps.number, res.id, eps.id).toJson(), - episode = eps.number - ) - } ?: throw ErrorLoadingException("No Episode") - - return newTvSeriesLoadResponse( - res.title ?: return null, - url, - if (res.type == "Movie" || episodes.size == 1) TvType.Movie else TvType.TvSeries, - episodes - ) { - this.posterUrl = res.thumbnail - this.year = res.releaseDate?.split("-")?.first()?.toIntOrNull() - this.plot = res.description - this.tags = listOf("${res.country}", "${res.status}", "${res.type}") - this.showStatus = when (res.status) { - "Completed" -> ShowStatus.Completed - "Ongoing" -> ShowStatus.Ongoing - else -> null - } - } - - } - - private fun getLanguage(str: String): String { - return when (str) { - "Indonesia" -> "Indonesian" - else -> str - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val loadData = parseJson(data) - - app.get( - "$mainUrl/api/DramaList/Episode/${loadData.epsId}.png?err=false&ts=&time=", - referer = "$mainUrl/Drama/${getTitle("${loadData.title}")}/Episode-${loadData.eps}?id=${loadData.id}&ep=${loadData.epsId}&page=0&pageSize=100" - ).parsedSafe()?.let { source -> - listOf(source.video, source.thirdParty).apmap { link -> - safeApiCall { - if (link?.contains(".m3u8") == true) { - M3u8Helper.generateM3u8( - this.name, - link, - referer = "$mainUrl/", - headers = mapOf("Origin" to mainUrl) - ).forEach(callback) - } else { - loadExtractor( - link?.substringBefore("=http") ?: return@safeApiCall, - "$mainUrl/", - subtitleCallback, - callback - ) - } - } - } - } - - // parsedSafe doesn't work in > - app.get("$mainUrl/api/Sub/${loadData.epsId}").text.let { res -> - tryParseJson>(res)?.map { sub -> - subtitleCallback.invoke( - SubtitleFile( - getLanguage(sub.label ?: return@map), - sub.src ?: return@map - ) - ) - } - } - - return true - - } - - data class Data( - val title: String?, - val eps: Int?, - val id: Int?, - val epsId: Int?, - ) - - data class Sources( - @JsonProperty("Video") val video: String?, - @JsonProperty("ThirdParty") val thirdParty: String?, - ) - - data class Subtitle( - @JsonProperty("src") val src: String?, - @JsonProperty("label") val label: String?, - ) - - data class Responses( - @JsonProperty("data") val data: ArrayList? = arrayListOf(), - ) - - data class Media( - @JsonProperty("episodesCount") val episodesCount: Int?, - @JsonProperty("thumbnail") val thumbnail: String?, - @JsonProperty("id") val id: Int?, - @JsonProperty("title") val title: String?, - ) - - data class Episodes( - @JsonProperty("id") val id: Int?, - @JsonProperty("number") val number: Int?, - @JsonProperty("sub") val sub: Int?, - ) - - data class MediaDetail( - @JsonProperty("description") val description: String?, - @JsonProperty("releaseDate") val releaseDate: String?, - @JsonProperty("status") val status: String?, - @JsonProperty("type") val type: String?, - @JsonProperty("country") val country: String?, - @JsonProperty("episodes") val episodes: ArrayList? = arrayListOf(), - @JsonProperty("thumbnail") val thumbnail: String?, - @JsonProperty("id") val id: Int?, - @JsonProperty("title") val title: String?, - ) - -} diff --git a/KisskhProvider/src/main/kotlin/com/lagradost/KisskhProviderPlugin.kt b/KisskhProvider/src/main/kotlin/com/lagradost/KisskhProviderPlugin.kt deleted file mode 100644 index 4d0b0af..0000000 --- a/KisskhProvider/src/main/kotlin/com/lagradost/KisskhProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class KisskhProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(KisskhProvider()) - } -} \ No newline at end of file diff --git a/MeloMovieProvider/build.gradle.kts b/MeloMovieProvider/build.gradle.kts deleted file mode 100644 index 9d4c472..0000000 --- a/MeloMovieProvider/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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=melomovie.com&sz=24" -} \ No newline at end of file diff --git a/MeloMovieProvider/src/main/AndroidManifest.xml b/MeloMovieProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/MeloMovieProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/MeloMovieProvider/src/main/kotlin/com/lagradost/MeloMovieProvider.kt b/MeloMovieProvider/src/main/kotlin/com/lagradost/MeloMovieProvider.kt deleted file mode 100644 index 5149a93..0000000 --- a/MeloMovieProvider/src/main/kotlin/com/lagradost/MeloMovieProvider.kt +++ /dev/null @@ -1,195 +0,0 @@ -package com.lagradost - -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.module.kotlin.readValue -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addImdbUrl -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.getQualityFromName -import org.jsoup.Jsoup -import org.jsoup.nodes.Element - -class MeloMovieProvider : MainAPI() { - override var name = "MeloMovie" - override var mainUrl = "https://melomovie.com" - override val instantLinkLoading = true - override val hasQuickSearch = true - override val hasChromecastSupport = false // MKV FILES CANT BE PLAYED ON A CHROMECAST - - data class MeloMovieSearchResult( - @JsonProperty("id") val id: Int, - @JsonProperty("imdb_code") val imdbId: String, - @JsonProperty("title") val title: String, - @JsonProperty("type") val type: Int, // 1 = MOVIE, 2 = TV-SERIES - @JsonProperty("year") val year: Int?, // 1 = MOVIE, 2 = TV-SERIES - //"mppa" for tags - ) - - data class MeloMovieLink( - @JsonProperty("name") val name: String, - @JsonProperty("link") val link: String - ) - - override suspend fun quickSearch(query: String): List { - return search(query) - } - - override suspend fun search(query: String): List { - val url = "$mainUrl/movie/search/?name=$query" - val returnValue: ArrayList = ArrayList() - val response = app.get(url).text - val mapped = response.let { mapper.readValue>(it) } - if (mapped.isEmpty()) return returnValue - - for (i in mapped) { - val currentUrl = "$mainUrl/movie/${i.id}" - val currentPoster = "$mainUrl/assets/images/poster/${i.imdbId}.jpg" - if (i.type == 2) { // TV-SERIES - returnValue.add( - TvSeriesSearchResponse( - i.title, - currentUrl, - this.name, - TvType.TvSeries, - currentPoster, - i.year, - null - ) - ) - } else if (i.type == 1) { // MOVIE - returnValue.add( - MovieSearchResponse( - i.title, - currentUrl, - this.name, - TvType.Movie, - currentUrl, - i.year - ) - ) - } - } - return returnValue - } - - // http not https, the links are not https! - private fun fixUrl(url: String): String { - if (url.isEmpty()) return "" - - if (url.startsWith("//")) { - return "http:$url" - } - if (!url.startsWith("http")) { - return "http://$url" - } - return url - } - - private fun serializeData(element: Element): List { - val eps = element.select("> tbody > tr") - val parsed = eps.mapNotNull { - try { - val tds = it.select("> td") - val name = tds[if (tds.size == 5) 1 else 0].text() - val url = fixUrl(tds.last()!!.selectFirst("> a")!!.attr("data-lnk").replace(" ", "%20")) - MeloMovieLink(name, url) - } catch (e: Exception) { - MeloMovieLink("", "") - } - }.filter { it.link != "" && it.name != "" } - return parsed - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val links = parseJson>(data) - for (link in links) { - callback.invoke( - ExtractorLink( - this.name, - link.name, - link.link, - "", - getQualityFromName(link.name), - false - ) - ) - } - return true - } - - override suspend fun load(url: String): LoadResponse? { - val response = app.get(url).text - - //backdrop = imgurl - fun findUsingRegex(src: String): String? { - return src.toRegex().find(response)?.groups?.get(1)?.value ?: return null - } - - val imdbUrl = findUsingRegex("var imdb = \"(.*?)\"") - val document = Jsoup.parse(response) - val poster = document.selectFirst("img.img-fluid")!!.attr("src") - val type = findUsingRegex("var posttype = ([0-9]*)")?.toInt() ?: return null - val titleInfo = document.selectFirst("div.movie_detail_title > div > div > h1") - val title = titleInfo!!.ownText() - val year = - titleInfo.selectFirst("> a")?.text()?.replace("(", "")?.replace(")", "")?.toIntOrNull() - val plot = document.selectFirst("div.col-lg-12 > p")!!.text() - - if (type == 1) { // MOVIE - val serialize = document.selectFirst("table.accordion__list") - ?: throw ErrorLoadingException("No links found") - return newMovieLoadResponse( - title, - url, - TvType.Movie, - serializeData(serialize) - ) { - this.posterUrl = poster - this.year = year - this.plot = plot - addImdbUrl(imdbUrl) - } - } else if (type == 2) { - val episodes = ArrayList() - val seasons = document.select("div.accordion__card") - ?: throw ErrorLoadingException("No episodes found") - for (s in seasons) { - val season = - s.selectFirst("> div.card-header > button > span")!!.text() - .replace("Season: ", "").toIntOrNull() - val localEpisodes = s.select("> div.collapse > div > div > div.accordion__card") - for (e in localEpisodes) { - val episode = - e.selectFirst("> div.card-header > button > span")!!.text() - .replace("Episode: ", "").toIntOrNull() - val links = - e.selectFirst("> div.collapse > div > table.accordion__list") ?: continue - val data = serializeData(links) - episodes.add(newEpisode(data) { - this.season = season - this.episode = episode - }) - } - } - episodes.reverse() - return newTvSeriesLoadResponse( - title, - url, - TvType.TvSeries, - episodes - ) { - this.posterUrl = poster - this.year = year - this.plot = plot - addImdbUrl(imdbUrl) - } - } - return null - } -} \ No newline at end of file diff --git a/MeloMovieProvider/src/main/kotlin/com/lagradost/MeloMovieProviderPlugin.kt b/MeloMovieProvider/src/main/kotlin/com/lagradost/MeloMovieProviderPlugin.kt deleted file mode 100644 index 56f0bb4..0000000 --- a/MeloMovieProvider/src/main/kotlin/com/lagradost/MeloMovieProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class MeloMovieProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(MeloMovieProvider()) - } -} \ No newline at end of file diff --git a/NginxProvider/build.gradle.kts b/NginxProvider/build.gradle.kts deleted file mode 100644 index 6b459e0..0000000 --- a/NginxProvider/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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", - "TvSeries", - "Movie", - ) - - } \ No newline at end of file diff --git a/NginxProvider/src/main/AndroidManifest.xml b/NginxProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/NginxProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/NginxProvider/src/main/kotlin/com/lagradost/NginxProvider.kt b/NginxProvider/src/main/kotlin/com/lagradost/NginxProvider.kt deleted file mode 100644 index 0f99e19..0000000 --- a/NginxProvider/src/main/kotlin/com/lagradost/NginxProvider.kt +++ /dev/null @@ -1,286 +0,0 @@ -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.Qualities - -class NginxProvider : MainAPI() { - override var name = "Nginx" - override val hasQuickSearch = false - override val hasMainPage = true - override val supportedTypes = setOf(TvType.AnimeMovie, TvType.TvSeries, TvType.Movie) - - companion object { - var loginCredentials: String? = null - var overrideUrl: String? = null - const val ERROR_STRING = "No nginx url specified in the settings" - } - - private fun getAuthHeader(): Map { - val url = overrideUrl ?: throw ErrorLoadingException(ERROR_STRING) - mainUrl = url - println("OVERRIDING URL TO $overrideUrl") - if (mainUrl == "NONE" || mainUrl.isBlank()) { - throw ErrorLoadingException(ERROR_STRING) - } - - val localCredentials = loginCredentials - if (localCredentials == null || localCredentials.trim() == ":") { - return mapOf("Authorization" to "Basic ") // no Authorization headers - } - - val basicAuthToken = - base64Encode(localCredentials.toByteArray()) // will this be loaded when not using the provider ??? can increase load - - return mapOf("Authorization" to "Basic $basicAuthToken") - } - - override suspend fun load(url: String): LoadResponse { - val authHeader = - getAuthHeader() // call again because it isn't reloaded if in main class and storedCredentials loads after - // url can be tvshow.nfo for series or mediaRootUrl for movies - - val mainRootDocument = app.get(url, authHeader).document - - val nfoUrl = url + mainRootDocument.getElementsByAttributeValueContaining("href", ".nfo") - .attr("href") // metadata url file - - val metadataDocument = app.get(nfoUrl, authHeader).document // get the metadata nfo file - - val isMovie = !nfoUrl.contains("tvshow.nfo") - - val title = metadataDocument.selectFirst("title")!!.text() - - val description = metadataDocument.selectFirst("plot")!!.text() - - if (isMovie) { - val poster = metadataDocument.selectFirst("thumb")!!.text() - val trailer = metadataDocument.select("trailer").mapNotNull { - it?.text()?.replace( - "plugin://plugin.video.youtube/play/?video_id=", - "https://www.youtube.com/watch?v=" - ) - } - val partialUrl = - mainRootDocument.getElementsByAttributeValueContaining("href", ".nfo").attr("href") - .replace(".nfo", ".") - val date = metadataDocument.selectFirst("year")?.text()?.toIntOrNull() - val ratingAverage = metadataDocument.selectFirst("value")?.text()?.toIntOrNull() - val tagsList = metadataDocument.select("genre") - .mapNotNull { // all the tags like action, thriller ... - it?.text() - - } - - - val dataList = - mainRootDocument.getElementsByAttributeValueContaining( // list of all urls of the webpage - "href", - partialUrl - ) - - val data = url + dataList.firstNotNullOf { item -> - item.takeIf { - (!it.attr("href").contains(".nfo") && !it.attr("href").contains(".jpg")) - } - }.attr("href").toString() // exclude poster and nfo (metadata) file - - return newMovieLoadResponse( - title, - url, - TvType.Movie, - data - ) { - this.year = date - this.plot = description - this.rating = ratingAverage - this.tags = tagsList - addTrailer(trailer) - addPoster(poster, authHeader) - } - } else // a tv serie - { - val list = ArrayList>() - val mediaRootUrl = url.replace("tvshow.nfo", "") - val posterUrl = mediaRootUrl + "poster.jpg" - val mediaRootDocument = app.get(mediaRootUrl, authHeader).document - val seasons = - mediaRootDocument.getElementsByAttributeValueContaining("href", "Season%20") - - - val tagsList = metadataDocument.select("genre") - .mapNotNull { // all the tags like action, thriller ...; unused variable - it?.text() - } - - //val actorsList = document.select("actor") - // ?.mapNotNull { // all the tags like action, thriller ...; unused variable - // it?.text() - // } - - seasons.forEach { element -> - val season = - element.attr("href").replace("Season%20", "").replace("/", "").toIntOrNull() - val href = mediaRootUrl + element.attr("href") - if (season != null && season > 0 && href.isNotBlank()) { - list.add(Pair(season, href)) - } - } - - if (list.isEmpty()) throw ErrorLoadingException("No Seasons Found") - - val episodeList = ArrayList() - - - list.apmap { (seasonInt, seasonString) -> - val seasonDocument = app.get(seasonString, authHeader).document - val episodes = seasonDocument.getElementsByAttributeValueContaining( - "href", - ".nfo" - ) // get metadata - episodes.forEach { episode -> - val nfoDocument = app.get( - seasonString + episode.attr("href"), - authHeader - ).document // get episode metadata file - val epNum = nfoDocument.selectFirst("episode")?.text()?.toIntOrNull() - val poster = - seasonString + episode.attr("href").replace(".nfo", "-thumb.jpg") - val name = nfoDocument.selectFirst("title")!!.text() - // val seasonInt = nfoDocument.selectFirst("season").text().toIntOrNull() - val date = nfoDocument.selectFirst("aired")?.text() - val plot = nfoDocument.selectFirst("plot")?.text() - - val dataList = seasonDocument.getElementsByAttributeValueContaining( - "href", - episode.attr("href").replace(".nfo", "") - ) - val data = seasonString + dataList.firstNotNullOf { item -> - item.takeIf { - (!it.attr("href").contains(".nfo") && !it.attr("href").contains(".jpg")) - } - }.attr("href").toString() // exclude poster and nfo (metadata) file - - episodeList.add( - newEpisode(data) { - this.name = name - this.season = seasonInt - this.episode = epNum - this.posterUrl = poster // will require headers too - this.description = plot - addDate(date) - } - ) - } - } - return newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodeList) { - this.name = title - this.url = url - this.episodes = episodeList - this.plot = description - this.tags = tagsList - addPoster(posterUrl, authHeader) - } - } - - } - - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - // loadExtractor(data, null) { callback(it.copy(headers=authHeader)) } - val authHeader = - getAuthHeader() // call again because it isn't reloaded if in main class and storedCredentials loads after - callback.invoke( - ExtractorLink( - name, - name, - data, - data, // referer not needed - Qualities.Unknown.value, - false, - authHeader, - ) - ) - - return true - } - - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val authHeader = - getAuthHeader() // call again because it isn't reloaded if in main class and storedCredentials loads after - - val document = app.get(mainUrl, authHeader).document - val categories = document.select("a") - val returnList = categories.mapNotNull { - val categoryTitle = it.text() // get the category title like Movies or Series - if (categoryTitle != "../" && categoryTitle != "Music/") { // exclude parent dir and Music dir - val href = it?.attr("href") - val categoryPath = fixUrlNull(href?.trim()) - ?: return@mapNotNull null // get the url of the category; like http://192.168.1.10/media/Movies/ - - val categoryDocument = app.get( - categoryPath, - authHeader - ).document // queries the page http://192.168.1.10/media/Movies/ - val contentLinks = categoryDocument.select("a") - val currentList = contentLinks.mapNotNull { head -> - if (head.attr("href") != "../") { - try { - val mediaRootUrl = - categoryPath + head.attr("href")// like http://192.168.1.10/media/Series/Chernobyl/ - val mediaDocument = app.get(mediaRootUrl, authHeader).document - val nfoFilename = mediaDocument.getElementsByAttributeValueContaining( - "href", - ".nfo" - )[0].attr("href") - val isMovieType = nfoFilename != "tvshow.nfo" - val nfoPath = - mediaRootUrl + nfoFilename // must exist or will raise errors, only the first one is taken - val nfoContent = - app.get(nfoPath, authHeader).document // all the metadata - - if (isMovieType) { - val movieName = nfoContent.select("title").text() - val posterUrl = mediaRootUrl + "poster.jpg" - return@mapNotNull newMovieSearchResponse( - movieName, - mediaRootUrl, - TvType.Movie, - ) { - addPoster(posterUrl, authHeader) - } - } else { // tv serie - val serieName = nfoContent.select("title").text() - - val posterUrl = mediaRootUrl + "poster.jpg" - - newTvSeriesSearchResponse( - serieName, - nfoPath, - TvType.TvSeries, - ) { - addPoster(posterUrl, authHeader) - } - } - } catch (e: Exception) { // can cause issues invisible errors - null - //logError(e) // not working because it changes the return type of currentList to Any - } - } else null - } - if (currentList.isNotEmpty() && categoryTitle != "../") { // exclude upper dir - HomePageList(categoryTitle, currentList) - } else null - } else null // the path is ../ which is parent directory - } - // if (returnList.isEmpty()) return null // maybe doing nothing idk - return HomePageResponse(returnList) - } -} diff --git a/NginxProvider/src/main/kotlin/com/lagradost/NginxProviderPlugin.kt b/NginxProvider/src/main/kotlin/com/lagradost/NginxProviderPlugin.kt deleted file mode 100644 index 0d8d099..0000000 --- a/NginxProvider/src/main/kotlin/com/lagradost/NginxProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class NginxProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(NginxProvider()) - } -} \ No newline at end of file diff --git a/NineAnimeProvider/build.gradle.kts b/NineAnimeProvider/build.gradle.kts deleted file mode 100644 index f97127a..0000000 --- a/NineAnimeProvider/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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=9anime.id&sz=24" -} \ No newline at end of file diff --git a/NineAnimeProvider/src/main/AndroidManifest.xml b/NineAnimeProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/NineAnimeProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/NineAnimeProvider/src/main/kotlin/com/lagradost/NineAnimeProvider.kt b/NineAnimeProvider/src/main/kotlin/com/lagradost/NineAnimeProvider.kt deleted file mode 100644 index b8dc644..0000000 --- a/NineAnimeProvider/src/main/kotlin/com/lagradost/NineAnimeProvider.kt +++ /dev/null @@ -1,357 +0,0 @@ -package com.lagradost - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.mvvm.logError -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.Jsoup - -class NineAnimeProvider : MainAPI() { - override var mainUrl = "https://9anime.id" - override var name = "9Anime" - override val hasMainPage = true - override val hasChromecastSupport = true - override val hasDownloadSupport = true - override val supportedTypes = setOf(TvType.Anime) - override val hasQuickSearch = true - - // taken from https://github.com/saikou-app/saikou/blob/b35364c8c2a00364178a472fccf1ab72f09815b4/app/src/main/java/ani/saikou/parsers/anime/NineAnime.kt - // GNU General Public License v3.0 https://github.com/saikou-app/saikou/blob/main/LICENSE.md - companion object { - private const val nineAnimeKey = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" - private const val cipherKey = "kMXzgyNzT3k5dYab" - - fun encodeVrf(text: String, mainKey: String): String { - return encode( - encrypt( - cipher(mainKey, encode(text)), - nineAnimeKey - )//.replace("""=+$""".toRegex(), "") - ) - } - - fun decodeVrf(text: String, mainKey: String): String { - return decode(cipher(mainKey, decrypt(text, nineAnimeKey))) - } - - fun encrypt(input: String, key: String): String { - if (input.any { it.code > 255 }) throw Exception("illegal characters!") - var output = "" - for (i in input.indices step 3) { - val a = intArrayOf(-1, -1, -1, -1) - a[0] = input[i].code shr 2 - a[1] = (3 and input[i].code) shl 4 - if (input.length > i + 1) { - a[1] = a[1] or (input[i + 1].code shr 4) - a[2] = (15 and input[i + 1].code) shl 2 - } - if (input.length > i + 2) { - a[2] = a[2] or (input[i + 2].code shr 6) - a[3] = 63 and input[i + 2].code - } - for (n in a) { - if (n == -1) output += "=" - else { - if (n in 0..63) output += key[n] - } - } - } - return output - } - - fun cipher(key: String, text: String): String { - val arr = IntArray(256) { it } - - var u = 0 - var r: Int - arr.indices.forEach { - u = (u + arr[it] + key[it % key.length].code) % 256 - r = arr[it] - arr[it] = arr[u] - arr[u] = r - } - u = 0 - var c = 0 - - return text.indices.map { j -> - c = (c + 1) % 256 - u = (u + arr[c]) % 256 - r = arr[c] - arr[c] = arr[u] - arr[u] = r - (text[j].code xor arr[(arr[c] + arr[u]) % 256]).toChar() - }.joinToString("") - } - - @Suppress("SameParameterValue") - private fun decrypt(input: String, key: String): String { - val t = if (input.replace("""[\t\n\f\r]""".toRegex(), "").length % 4 == 0) { - input.replace("""==?$""".toRegex(), "") - } else input - if (t.length % 4 == 1 || t.contains("""[^+/0-9A-Za-z]""".toRegex())) throw Exception("bad input") - var i: Int - var r = "" - var e = 0 - var u = 0 - for (o in t.indices) { - e = e shl 6 - i = key.indexOf(t[o]) - e = e or i - u += 6 - if (24 == u) { - r += ((16711680 and e) shr 16).toChar() - r += ((65280 and e) shr 8).toChar() - r += (255 and e).toChar() - e = 0 - u = 0 - } - } - return if (12 == u) { - e = e shr 4 - r + e.toChar() - } else { - if (18 == u) { - e = e shr 2 - r += ((65280 and e) shr 8).toChar() - r += (255 and e).toChar() - } - r - } - } - - fun encode(input: String): String = - java.net.URLEncoder.encode(input, "utf-8").replace("+", "%20") - - private fun decode(input: String): String = java.net.URLDecoder.decode(input, "utf-8") - } - - override val mainPage = mainPageOf( - "$mainUrl/ajax/home/widget/trending?page=" to "Trending", - "$mainUrl/ajax/home/widget/updated-all?page=" to "All", - "$mainUrl/ajax/home/widget/updated-sub?page=" to "Recently Updated (SUB)", - "$mainUrl/ajax/home/widget/updated-dub?page=" to "Recently Updated (DUB)", - "$mainUrl/ajax/home/widget/updated-china?page=" to "Recently Updated (Chinese)", - "$mainUrl/ajax/home/widget/random?page=" to "Random", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val url = request.data + page - val home = Jsoup.parse( - app.get( - url - ).parsed().html - ).select("div.item").mapNotNull { element -> - val title = element.selectFirst(".info > .name") ?: return@mapNotNull null - val link = title.attr("href") - val poster = element.selectFirst(".poster > a > img")?.attr("src") - val meta = element.selectFirst(".poster > a > .meta > .inner > .left") - val subbedEpisodes = meta?.selectFirst(".sub")?.text()?.toIntOrNull() - val dubbedEpisodes = meta?.selectFirst(".dub")?.text()?.toIntOrNull() - - newAnimeSearchResponse(title.text() ?: return@mapNotNull null, link) { - this.posterUrl = poster - addDubStatus( - dubbedEpisodes != null, - subbedEpisodes != null, - dubbedEpisodes, - subbedEpisodes - ) - } - } - - return newHomePageResponse(request.name, home) - } - - data class Response( - @JsonProperty("result") val html: String - ) - - data class QuickSearchResponse( - //@JsonProperty("status") val status: Int? = null, - @JsonProperty("result") val result: QuickSearchResult? = null, - //@JsonProperty("message") val message: String? = null, - //@JsonProperty("messages") val messages: ArrayList = arrayListOf() - ) - - data class QuickSearchResult( - @JsonProperty("html") val html: String? = null, - //@JsonProperty("linkMore") val linkMore: String? = null - ) - - override suspend fun quickSearch(query: String): List? { - val vrf = encodeVrf(query, cipherKey) - val url = - "$mainUrl/ajax/anime/search?keyword=$query&vrf=$vrf" - val response = app.get(url).parsedSafe() - val document = Jsoup.parse(response?.result?.html ?: return null) - return document.select(".items > a").mapNotNull { element -> - val link = fixUrl(element?.attr("href") ?: return@mapNotNull null) - val title = element.selectFirst(".info > .name")?.text() ?: return@mapNotNull null - newAnimeSearchResponse(title, link) { - posterUrl = element.selectFirst(".poster > span > img")?.attr("src") - } - } - } - - override suspend fun search(query: String): List { - val vrf = encodeVrf(query, cipherKey) - //?language%5B%5D=${if (selectDub) "dubbed" else "subbed"}& - val url = - "$mainUrl/filter?keyword=${encode(query)}&vrf=${vrf}&page=1" - return app.get(url).document.select("#list-items div.ani.poster.tip > a").mapNotNull { - val link = fixUrl(it.attr("href") ?: return@mapNotNull null) - val img = it.select("img") - val title = img.attr("alt") - newAnimeSearchResponse(title, link) { - posterUrl = img.attr("src") - } - } - } - - override suspend fun load(url: String): LoadResponse { - val validUrl = url.replace("https://9anime.to", mainUrl) - val doc = app.get(validUrl).document - - val meta = doc.selectFirst("#w-info") ?: throw ErrorLoadingException("Could not find info") - val ratingElement = meta.selectFirst(".brating > #w-rating") - val id = ratingElement?.attr("data-id") ?: throw ErrorLoadingException("Could not find id") - val binfo = - meta.selectFirst(".binfo") ?: throw ErrorLoadingException("Could not find binfo") - val info = binfo.selectFirst(".info") ?: throw ErrorLoadingException("Could not find info") - - val title = (info.selectFirst(".title") ?: info.selectFirst(".d-title"))?.text() - ?: throw ErrorLoadingException("Could not find title") - - val vrf = encodeVrf(id, cipherKey) - val episodeListUrl = "$mainUrl/ajax/episode/list/$id?vrf=$vrf" - val body = - app.get(episodeListUrl).parsedSafe()?.html - ?: throw ErrorLoadingException("Could not parse json with cipherKey=$cipherKey id=$id url=\n$episodeListUrl") - - val subEpisodes = ArrayList() - val dubEpisodes = ArrayList() - - //TODO RECOMMENDATIONS - - Jsoup.parse(body).body().select(".episodes > ul > li > a").mapNotNull { element -> - val ids = element.attr("data-ids").split(",", limit = 2) - - val epNum = element.attr("data-num") - .toIntOrNull() // might fuck up on 7.5 ect might use data-slug instead - val epTitle = element.selectFirst("span.d-title")?.text() - //val filler = element.hasClass("filler") - ids.getOrNull(1)?.let { dub -> - dubEpisodes.add( - Episode( - "$mainUrl/ajax/server/list/$dub?vrf=${encodeVrf(dub, cipherKey)}", - epTitle, - episode = epNum - ) - ) - } - ids.getOrNull(0)?.let { sub -> - subEpisodes.add( - Episode( - "$mainUrl/ajax/server/list/$sub?vrf=${encodeVrf(sub, cipherKey)}", - epTitle, - episode = epNum - ) - ) - } - } - - return newAnimeLoadResponse(title, url, TvType.Anime) { - addEpisodes(DubStatus.Dubbed, dubEpisodes) - addEpisodes(DubStatus.Subbed, subEpisodes) - - plot = info.selectFirst(".synopsis > .shorting > .content")?.text() - posterUrl = binfo.selectFirst(".poster > span > img")?.attr("src") - rating = ratingElement.attr("data-score").toFloat().times(1000f).toInt() - - info.select(".bmeta > .meta > div").forEach { element -> - when (element.ownText()) { - "Genre: " -> { - tags = element.select("span > a").mapNotNull { it?.text() } - } - "Duration: " -> { - duration = getDurationFromString(element.selectFirst("span")?.text()) - } - "Type: " -> { - type = when (element.selectFirst("span > a")?.text()) { - "ONA" -> TvType.OVA - else -> { - type - } - } - } - "Status: " -> { - showStatus = when (element.selectFirst("span")?.text()) { - "Releasing" -> ShowStatus.Ongoing - "Completed" -> ShowStatus.Completed - else -> { - showStatus - } - } - } - else -> {} - } - } - } - } - - data class Result( - @JsonProperty("url") - val url: String? = null - ) - - data class Links( - @JsonProperty("result") - val result: Result? = null - ) - - //TODO 9anime outro into {"status":200,"result":{"url":"","skip_data":{"intro_begin":67,"intro_end":154,"outro_begin":1337,"outro_end":1415,"count":3}},"message":"","messages":[]} - private suspend fun getEpisodeLinks(id: String): Links? { - return app.get("$mainUrl/ajax/server/$id?vrf=${encodeVrf(id, cipherKey)}").parsedSafe() - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val body = app.get(data).parsed().html - val document = Jsoup.parse(body) - - document.select("li").apmap { - try { - val name = it.text() - val encodedStreamUrl = - getEpisodeLinks(it.attr("data-link-id"))?.result?.url ?: return@apmap - val url = decodeVrf(encodedStreamUrl, cipherKey) - if (!loadExtractor(url, mainUrl, subtitleCallback, callback)) { - callback( - ExtractorLink( - this.name, - name, - url, - mainUrl, - Qualities.Unknown.value, - url.contains(".m3u8") - ) - ) - } - } catch (e: Exception) { - logError(e) - } - } - - return true - } -} diff --git a/NineAnimeProvider/src/main/kotlin/com/lagradost/NineAnimeProviderPlugin.kt b/NineAnimeProvider/src/main/kotlin/com/lagradost/NineAnimeProviderPlugin.kt deleted file mode 100644 index 3a21e3e..0000000 --- a/NineAnimeProvider/src/main/kotlin/com/lagradost/NineAnimeProviderPlugin.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class NineAnimeProviderPlugin : Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(NineAnimeProvider()) - registerMainAPI(WcoProvider()) - registerExtractorAPI(Mcloud()) - registerExtractorAPI(Vidstreamz()) - registerExtractorAPI(Vizcloud()) - registerExtractorAPI(Vizcloud2()) - registerExtractorAPI(VizcloudOnline()) - registerExtractorAPI(VizcloudXyz()) - registerExtractorAPI(VizcloudLive()) - registerExtractorAPI(VizcloudInfo()) - registerExtractorAPI(MwvnVizcloudInfo()) - registerExtractorAPI(VizcloudDigital()) - registerExtractorAPI(VizcloudCloud()) - registerExtractorAPI(VizcloudSite()) - registerExtractorAPI(WcoStream()) - } -} \ No newline at end of file diff --git a/NineAnimeProvider/src/main/kotlin/com/lagradost/WcoProvider.kt b/NineAnimeProvider/src/main/kotlin/com/lagradost/WcoProvider.kt deleted file mode 100644 index 9aeafb0..0000000 --- a/NineAnimeProvider/src/main/kotlin/com/lagradost/WcoProvider.kt +++ /dev/null @@ -1,238 +0,0 @@ -package com.lagradost - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.utils.ExtractorLink -import org.json.JSONObject -import org.jsoup.Jsoup -import org.jsoup.nodes.Document -import java.util.* - - -class WcoProvider : MainAPI() { - 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 - } - } - - override var mainUrl = "https://wcostream.cc" - override var name = "WCO Stream" - override val hasQuickSearch = true - override val hasMainPage = true - - override val supportedTypes = setOf( - TvType.AnimeMovie, - TvType.Anime, - TvType.OVA - ) - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val urls = listOf( - Pair("$mainUrl/ajax/list/recently_updated?type=tv", "Recently Updated Anime"), - Pair("$mainUrl/ajax/list/recently_updated?type=movie", "Recently Updated Movies"), - Pair("$mainUrl/ajax/list/recently_added?type=tv", "Recently Added Anime"), - Pair("$mainUrl/ajax/list/recently_added?type=movie", "Recently Added Movies"), - ) - - val items = ArrayList() - for (i in urls) { - try { - val response = JSONObject( - app.get( - i.first, - ).text - ).getString("html") // I won't make a dataclass for this shit - val document = Jsoup.parse(response) - val results = document.select("div.flw-item").map { - val filmPoster = it.selectFirst("> div.film-poster") - val filmDetail = it.selectFirst("> div.film-detail") - val nameHeader = filmDetail!!.selectFirst("> h3.film-name > a") - val title = nameHeader!!.text().replace(" (Dub)", "") - val href = - nameHeader.attr("href").replace("/watch/", "/anime/") - .replace(Regex("-episode-.*"), "/") - val isDub = - filmPoster!!.selectFirst("> div.film-poster-quality")?.text() - ?.contains("DUB") - ?: false - val poster = filmPoster.selectFirst("> img")!!.attr("data-src") - val set: EnumSet = - EnumSet.of(if (isDub) DubStatus.Dubbed else DubStatus.Subbed) - AnimeSearchResponse(title, href, this.name, TvType.Anime, poster, null, set) - } - items.add(HomePageList(i.second, results)) - } catch (e: Exception) { - e.printStackTrace() - } - } - if (items.size <= 0) throw ErrorLoadingException() - return HomePageResponse(items) - } - - - private fun fixAnimeLink(url: String): String { - val regex = "watch/([a-zA-Z\\-0-9]*)-episode".toRegex() - val (aniId) = regex.find(url)!!.destructured - return "$mainUrl/anime/$aniId" - } - - private fun parseSearchPage(soup: Document): List { - val items = soup.select(".film_list-wrap > .flw-item") - if (items.isEmpty()) return ArrayList() - return items.map { i -> - val href = fixAnimeLink(i.selectFirst("a")!!.attr("href")) - val img = fixUrl(i.selectFirst("img")!!.attr("data-src")) - val title = i.selectFirst("img")!!.attr("title") - val isDub = !i.select(".pick.film-poster-quality").isEmpty() - val year = - i.selectFirst(".film-detail.film-detail-fix > div > span:nth-child(1)")!!.text() - .toIntOrNull() - val type = - i.selectFirst(".film-detail.film-detail-fix > div > span:nth-child(3)")!!.text() - - if (getType(type) == TvType.AnimeMovie) { - MovieSearchResponse( - title, href, this.name, TvType.AnimeMovie, img, year - ) - } else { - AnimeSearchResponse( - title, - href, - this.name, - TvType.Anime, - img, - year, - EnumSet.of(if (isDub) DubStatus.Dubbed else DubStatus.Subbed), - ) - } - } - } - - override suspend fun search(query: String): List { - val url = "$mainUrl/search" - val response = - app.get(url, params = mapOf("keyword" to query)) - var document = Jsoup.parse(response.text) - val returnValue = parseSearchPage(document).toMutableList() - - while (!document.select(".pagination").isEmpty()) { - val link = document.select("a.page-link[rel=\"next\"]") - if (!link.isEmpty() && returnValue.size < 40) { - val extraResponse = app.get(fixUrl(link[0].attr("href"))).text - document = Jsoup.parse(extraResponse) - returnValue.addAll(parseSearchPage(document)) - } else { - break - } - } - return returnValue.distinctBy { it.url } - } - - override suspend fun quickSearch(query: String): List { - val response = JSONObject( - app.post( - "https://wcostream.cc/ajax/search", - data = mapOf("keyword" to query) - ).text - ).getString("html") // I won't make a dataclass for this shit - val document = Jsoup.parse(response) - - return document.select("a.nav-item").mapNotNull { - val title = it.selectFirst("img")?.attr("title") ?: return@mapNotNull null - val img = it?.selectFirst("img")?.attr("src") ?: return@mapNotNull null - val href = it?.attr("href") ?: return@mapNotNull null - val isDub = title.contains("(Dub)") - val filmInfo = it.selectFirst(".film-infor") - val year = filmInfo?.select("span")?.get(0)?.text()?.toIntOrNull() - val type = filmInfo?.select("span")?.get(1)?.text().toString() - if (getType(type) == TvType.AnimeMovie) { - MovieSearchResponse( - title, href, this.name, TvType.AnimeMovie, img, year - ) - } else { - AnimeSearchResponse( - title, - href, - this.name, - TvType.Anime, - img, - year, - EnumSet.of(if (isDub) DubStatus.Dubbed else DubStatus.Subbed), - ) - } - } - } - - override suspend fun load(url: String): LoadResponse { - val response = app.get(url, timeout = 120).text - val document = Jsoup.parse(response) - - val japaneseTitle = - document.selectFirst("div.elements div.row > div:nth-child(1) > div.row-line:nth-child(1)") - ?.text()?.trim()?.replace("Other names:", "")?.trim() - - val canonicalTitle = document.selectFirst("meta[name=\"title\"]") - ?.attr("content")?.split("| W")?.get(0).toString() - - val isDubbed = canonicalTitle.contains("Dub") - val episodeNodes = document.select(".tab-content .nav-item > a") - - val episodes = ArrayList(episodeNodes?.map { - Episode(it.attr("href")) - } ?: ArrayList()) - - val statusElem = - document.selectFirst("div.elements div.row > div:nth-child(1) > div.row-line:nth-child(2)") - val status = when (statusElem?.text()?.replace("Status:", "")?.trim()) { - "Ongoing" -> ShowStatus.Ongoing - "Completed" -> ShowStatus.Completed - else -> null - } - val yearText = - document.selectFirst("div.elements div.row > div:nth-child(2) > div.row-line:nth-child(4)") - ?.text() - val year = yearText?.replace("Date release:", "")?.trim()?.split("-")?.get(0)?.toIntOrNull() - - val poster = document.selectFirst(".film-poster-img")?.attr("src") - val type = document.selectFirst("span.item.mr-1 > a")?.text()?.trim() - - val synopsis = document.selectFirst(".description > p")?.text()?.trim() - val genre = - document.select("div.elements div.row > div:nth-child(1) > div.row-line:nth-child(5) > a") - .map { it?.text()?.trim().toString() } - - return newAnimeLoadResponse(canonicalTitle, url, getType(type ?: "")) { - japName = japaneseTitle - engName = canonicalTitle - posterUrl = poster - this.year = year - addEpisodes(if (isDubbed) DubStatus.Dubbed else DubStatus.Subbed, episodes) - showStatus = status - plot = synopsis - tags = genre - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val response = app.get(data).text - val servers = Jsoup.parse(response).select("#servers-list > ul > li").map { - mapOf( - "link" to it?.selectFirst("a")?.attr("data-embed"), - "title" to it?.selectFirst("span")?.text()?.trim() - ) - } - - for (server in servers) { - WcoStream().getSafeUrl(server["link"].toString(), null, subtitleCallback, callback) - Mcloud().getSafeUrl(server["link"].toString(), null, subtitleCallback, callback) - } - return true - } -} diff --git a/NineAnimeProvider/src/main/kotlin/com/lagradost/WcoStream.kt b/NineAnimeProvider/src/main/kotlin/com/lagradost/WcoStream.kt deleted file mode 100644 index c3cdfbf..0000000 --- a/NineAnimeProvider/src/main/kotlin/com/lagradost/WcoStream.kt +++ /dev/null @@ -1,133 +0,0 @@ -package com.lagradost - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.NineAnimeProvider.Companion.cipher -import com.lagradost.NineAnimeProvider.Companion.encrypt -import com.lagradost.cloudstream3.ErrorLoadingException -import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.utils.ExtractorApi -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities - -class Vidstreamz : WcoStream() { - override var mainUrl = "https://vidstreamz.online" -} - -open class Mcloud : WcoStream() { - override var name = "Mcloud" - override var mainUrl = "https://mcloud.to" - override val requiresReferer = true -} - -class Vizcloud : WcoStream() { - override var mainUrl = "https://vizcloud2.ru" -} - -class Vizcloud2 : WcoStream() { - override var mainUrl = "https://vizcloud2.online" -} - -class VizcloudOnline : WcoStream() { - override var mainUrl = "https://vizcloud.online" -} - -class VizcloudXyz : WcoStream() { - override var mainUrl = "https://vizcloud.xyz" -} - -class VizcloudLive : WcoStream() { - override var mainUrl = "https://vizcloud.live" -} - -class VizcloudInfo : WcoStream() { - override var mainUrl = "https://vizcloud.info" -} - -class MwvnVizcloudInfo : WcoStream() { - override var mainUrl = "https://mwvn.vizcloud.info" -} - -class VizcloudDigital : WcoStream() { - override var mainUrl = "https://vizcloud.digital" -} - -class VizcloudCloud : WcoStream() { - override var mainUrl = "https://vizcloud.cloud" -} - -class VizcloudSite : WcoStream() { - override var mainUrl = "https://vizcloud.site" -} - -open class WcoStream : ExtractorApi() { - override var name = "VidStream" // Cause works for animekisa and wco - override var mainUrl = "https://vidstream.pro" - override val requiresReferer = false - private val regex = Regex("(.+?/)e(?:mbed)?/([a-zA-Z0-9]+)") - - companion object { - // taken from https://github.com/saikou-app/saikou/blob/b35364c8c2a00364178a472fccf1ab72f09815b4/app/src/main/java/ani/saikou/parsers/anime/extractors/VizCloud.kt - // GNU General Public License v3.0 https://github.com/saikou-app/saikou/blob/main/LICENSE.md - private var lastChecked = 0L - private const val jsonLink = - "https://raw.githubusercontent.com/chenkaslowankiya/BruhFlow/main/keys.json" - private var cipherKey: VizCloudKey? = null - suspend fun getKey(): VizCloudKey { - cipherKey = - if (cipherKey != null && (lastChecked - System.currentTimeMillis()) < 1000 * 60 * 30) cipherKey!! - else { - lastChecked = System.currentTimeMillis() - app.get(jsonLink).parsed() - } - return cipherKey!! - } - - data class VizCloudKey( - @JsonProperty("cipherKey") val cipherKey: String, - @JsonProperty("mainKey") val mainKey: String, - @JsonProperty("encryptKey") val encryptKey: String, - @JsonProperty("dashTable") val dashTable: String - ) - - private const val baseTable = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+=/_" - - private fun dashify(id: String, dashTable: String): String { - val table = dashTable.split(" ") - return id.mapIndexedNotNull { i, c -> - table.getOrNull((baseTable.indexOf(c) * 16) + (i % 16)) - }.joinToString("-") - } - } - - //private val key = "LCbu3iYC7ln24K7P" // key credits @Modder4869 - override suspend fun getUrl(url: String, referer: String?): List { - val group = regex.find(url)?.groupValues!! - - val host = group[1] - val viz = getKey() - val id = encrypt( - cipher( - viz.cipherKey, - encrypt(group[2], viz.encryptKey).also { println(it) } - ).also { println(it) }, - viz.encryptKey - ).also { println(it) } - - val link = - "${host}mediainfo/${dashify(id, viz.dashTable)}?key=${viz.mainKey}" // - val response = app.get(link, referer = referer) - - data class Sources(@JsonProperty("file") val file: String) - data class Media(@JsonProperty("sources") val sources: List) - data class Data(@JsonProperty("media") val media: Media) - data class Response(@JsonProperty("data") val data: Data) - - - if (!response.text.startsWith("{")) throw ErrorLoadingException("Seems like 9Anime kiddies changed stuff again, Go touch some grass for bout an hour Or use a different Server") - return response.parsed().data.media.sources.map { - ExtractorLink(name, it.file,it.file,host,Qualities.Unknown.value,it.file.contains(".m3u8")) - } - - } -} diff --git a/OlgplyProvider/build.gradle.kts b/OlgplyProvider/build.gradle.kts deleted file mode 100644 index 846a41b..0000000 --- a/OlgplyProvider/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -// use an integer for version numbers -version = 3 - - -cloudstream { - // All of these properties are optional, you can safely remove them - - description = "Uses TMDB" - authors = listOf("Blatzar") - - /** - * 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=olgply.com&sz=24" -} \ No newline at end of file diff --git a/OlgplyProvider/src/main/AndroidManifest.xml b/OlgplyProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/OlgplyProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/OlgplyProvider/src/main/kotlin/com/lagradost/OlgplyProvider.kt b/OlgplyProvider/src/main/kotlin/com/lagradost/OlgplyProvider.kt deleted file mode 100644 index c2a0636..0000000 --- a/OlgplyProvider/src/main/kotlin/com/lagradost/OlgplyProvider.kt +++ /dev/null @@ -1,115 +0,0 @@ -package com.lagradost - -import com.lagradost.cloudstream3.SubtitleFile -import com.lagradost.cloudstream3.TvType -import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.metaproviders.TmdbLink -import com.lagradost.cloudstream3.metaproviders.TmdbProvider -import com.lagradost.cloudstream3.network.WebViewResolver -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.nicehttp.requestCreator -import org.mozilla.javascript.Context -import org.mozilla.javascript.Scriptable -import org.mozilla.javascript.ScriptableObject - -class OlgplyProvider : TmdbProvider() { - override var mainUrl = "https://olgply.com" - override val apiName = "Olgply" - override var name = "Olgply" - override val instantLinkLoading = true - override val useMetaLoadResponse = true - override val supportedTypes = setOf(TvType.TvSeries, TvType.Movie) - - private suspend fun loadLinksWithWebView( - url: String, -// subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ) { - val foundVideo = WebViewResolver( - Regex("""\.m3u8|i7njdjvszykaieynzsogaysdgb0hm8u1mzubmush4maopa4wde\.com""") - ).resolveUsingWebView( - requestCreator( - "GET", url, referer = "https://olgply.xyz/" - ) - ) - .first ?: return - - callback.invoke( - ExtractorLink( - this.name, - "Movies4Discord", - foundVideo.url.toString(), - "", - Qualities.Unknown.value, - true - ) - ) - } - - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val mappedData = parseJson(data) - val tmdbId = mappedData.tmdbID ?: return false - val jsRegex = Regex("""eval\(.*\);""") - - val apiUrl = - "https://olgply.xyz/${tmdbId}${mappedData.season?.let { "/$it" } ?: ""}${mappedData.episode?.let { "/$it" } ?: ""}" -// val html = -// app.get(apiUrl, referer = "https://olgply.xyz/").text -// val rhino = Context.enter() -// rhino.optimizationLevel = -1 -// val scope: Scriptable = rhino.initSafeStandardObjects() -// val documentJs = """ -// Plyr = function(){}; -// -// hlsPrototype = { -// loadSource(url) { -// this.url = url; -// } -// }; -// -// function Hls() {}; -// Hls.isSupported = function(){return true}; -// -// Hls.prototype = hlsPrototype; -// Hls.prototype.constructor = Hls; -// -// document = { -// "querySelector" : function() {} -// }; -// """.trimIndent() -// -// val foundJs = jsRegex.find(html)?.groupValues?.getOrNull(0) ?: return false -// try { -// rhino.evaluateString(scope, documentJs + foundJs, "JavaScript", 1, null) -// } catch (e: Exception) { -// } -// -// val hls = scope.get("hls", scope) as? ScriptableObject -// -// if (hls != null) { -// callback.invoke( -// ExtractorLink( -// this.name, -// this.name, -// hls["url"].toString(), -// this.mainUrl + "/", -// Qualities.Unknown.value, -// headers = mapOf("range" to "bytes=0-"), -// isM3u8 = true -// ) -// ) -// } else { - // Disgraceful fallback, but the js for Movies4Discord refuses to work correctly :( - loadLinksWithWebView(apiUrl, callback) -// } - return true - } -} \ No newline at end of file diff --git a/OlgplyProvider/src/main/kotlin/com/lagradost/OlgplyProviderPlugin.kt b/OlgplyProvider/src/main/kotlin/com/lagradost/OlgplyProviderPlugin.kt deleted file mode 100644 index 6ec160a..0000000 --- a/OlgplyProvider/src/main/kotlin/com/lagradost/OlgplyProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class OlgplyProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(OlgplyProvider()) - } -} \ No newline at end of file diff --git a/SflixProvider/build.gradle.kts b/SflixProvider/build.gradle.kts deleted file mode 100644 index a36cc5d..0000000 --- a/SflixProvider/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -// use an integer for version numbers -version = 2 - - -cloudstream { - // All of these properties are optional, you can safely remove them - - description = "Also includes Dopebox, Solarmovie, Zoro and 2embed" - // 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=www.2embed.to&sz=24" -} \ No newline at end of file diff --git a/SflixProvider/src/main/AndroidManifest.xml b/SflixProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/SflixProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/SflixProvider/src/main/kotlin/com/lagradost/DopeboxProvider.kt b/SflixProvider/src/main/kotlin/com/lagradost/DopeboxProvider.kt deleted file mode 100644 index fbcb65e..0000000 --- a/SflixProvider/src/main/kotlin/com/lagradost/DopeboxProvider.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.lagradost - -class DopeboxProvider : SflixProvider() { - override var mainUrl = "https://dopebox.to" - override var name = "Dopebox" -} \ No newline at end of file diff --git a/SflixProvider/src/main/kotlin/com/lagradost/HDTodayProvider.kt b/SflixProvider/src/main/kotlin/com/lagradost/HDTodayProvider.kt deleted file mode 100644 index 62b788e..0000000 --- a/SflixProvider/src/main/kotlin/com/lagradost/HDTodayProvider.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.lagradost - -class HDTodayProvider : SflixProvider() { - override var mainUrl = "https://hdtoday.cc" - override var name = "HDToday" -} diff --git a/SflixProvider/src/main/kotlin/com/lagradost/SflixProvider.kt b/SflixProvider/src/main/kotlin/com/lagradost/SflixProvider.kt deleted file mode 100644 index 3b9bcc7..0000000 --- a/SflixProvider/src/main/kotlin/com/lagradost/SflixProvider.kt +++ /dev/null @@ -1,747 +0,0 @@ -package com.lagradost - -import android.util.Log -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.APIHolder.getCaptchaToken -import com.lagradost.cloudstream3.APIHolder.unixTimeMS -import com.lagradost.cloudstream3.LoadResponse.Companion.addActors -import com.lagradost.cloudstream3.LoadResponse.Companion.addDuration -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -//import com.lagradost.cloudstream3.animeproviders.ZoroProvider -import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -import com.lagradost.cloudstream3.utils.Coroutines.ioSafe -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.M3u8Helper -import com.lagradost.cloudstream3.utils.getQualityFromName -import com.lagradost.cloudstream3.utils.loadExtractor -import com.lagradost.nicehttp.NiceResponse -import kotlinx.coroutines.delay -import okhttp3.RequestBody.Companion.toRequestBody -import org.jsoup.Jsoup -import org.jsoup.nodes.Element -import java.net.URI -import java.util.* -import kotlin.system.measureTimeMillis - -open class SflixProvider : MainAPI() { - override var mainUrl = "https://sflix.to" - override var name = "Sflix.to" - - override val hasQuickSearch = false - override val hasMainPage = true - override val hasChromecastSupport = true - override val hasDownloadSupport = true - override val usesWebView = true - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - ) - override val vpnStatus = VPNStatus.None - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val html = app.get("$mainUrl/home").text - val document = Jsoup.parse(html) - - val all = ArrayList() - - val map = mapOf( - "Trending Movies" to "div#trending-movies", - "Trending TV Shows" to "div#trending-tv", - ) - map.forEach { - all.add(HomePageList( - it.key, - document.select(it.value).select("div.flw-item").map { element -> - element.toSearchResult() - } - )) - } - - document.select("section.block_area.block_area_home.section-id-02").forEach { - val title = it.select("h2.cat-heading").text().trim() - val elements = it.select("div.flw-item").map { element -> - element.toSearchResult() - } - all.add(HomePageList(title, elements)) - } - - return HomePageResponse(all) - } - - override suspend fun search(query: String): List { - val url = "$mainUrl/search/${query.replace(" ", "-")}" - val html = app.get(url).text - val document = Jsoup.parse(html) - - return document.select("div.flw-item").map { - val title = it.select("h2.film-name").text() - val href = fixUrl(it.select("a").attr("href")) - val year = it.select("span.fdi-item").text().toIntOrNull() - val image = it.select("img").attr("data-src") - val isMovie = href.contains("/movie/") - - val metaInfo = it.select("div.fd-infor > span.fdi-item") - // val rating = metaInfo[0].text() - val quality = getQualityFromString(metaInfo.getOrNull(1)?.text()) - - if (isMovie) { - MovieSearchResponse( - title, - href, - this.name, - TvType.Movie, - image, - year, - quality = quality - ) - } else { - TvSeriesSearchResponse( - title, - href, - this.name, - TvType.TvSeries, - image, - year, - null, - quality = quality - ) - } - } - } - - override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document - - val details = document.select("div.detail_page-watch") - val img = details.select("img.film-poster-img") - val posterUrl = img.attr("src") - val title = img.attr("title") ?: throw ErrorLoadingException("No Title") - - /* - val year = Regex("""[Rr]eleased:\s*(\d{4})""").find( - document.select("div.elements").text() - )?.groupValues?.get(1)?.toIntOrNull() - val duration = Regex("""[Dd]uration:\s*(\d*)""").find( - document.select("div.elements").text() - )?.groupValues?.get(1)?.trim()?.plus(" min")*/ - var duration = document.selectFirst(".fs-item > .duration")?.text()?.trim() - var year: Int? = null - var tags: List? = null - var cast: List? = null - val youtubeTrailer = document.selectFirst("iframe#iframe-trailer")?.attr("data-src") - val rating = document.selectFirst(".fs-item > .imdb")?.text()?.trim() - ?.removePrefix("IMDB:")?.toRatingInt() - - document.select("div.elements > .row > div > .row-line").forEach { element -> - val type = element?.select(".type")?.text() ?: return@forEach - when { - type.contains("Released") -> { - year = Regex("\\d+").find( - element.ownText() ?: return@forEach - )?.groupValues?.firstOrNull()?.toIntOrNull() - } - type.contains("Genre") -> { - tags = element.select("a").mapNotNull { it.text() } - } - type.contains("Cast") -> { - cast = element.select("a").mapNotNull { it.text() } - } - type.contains("Duration") -> { - duration = duration ?: element.ownText().trim() - } - } - } - val plot = details.select("div.description").text().replace("Overview:", "").trim() - - val isMovie = url.contains("/movie/") - - // https://sflix.to/movie/free-never-say-never-again-hd-18317 -> 18317 - val idRegex = Regex(""".*-(\d+)""") - val dataId = details.attr("data-id") - val id = if (dataId.isNullOrEmpty()) - idRegex.find(url)?.groupValues?.get(1) - ?: throw ErrorLoadingException("Unable to get id from '$url'") - else dataId - - val recommendations = - document.select("div.film_list-wrap > div.flw-item").mapNotNull { element -> - val titleHeader = - element.select("div.film-detail > .film-name > a") ?: return@mapNotNull null - val recUrl = fixUrlNull(titleHeader.attr("href")) ?: return@mapNotNull null - val recTitle = titleHeader.text() ?: return@mapNotNull null - val poster = element.select("div.film-poster > img").attr("data-src") - MovieSearchResponse( - recTitle, - recUrl, - this.name, - if (recUrl.contains("/movie/")) TvType.Movie else TvType.TvSeries, - poster, - year = null - ) - } - - if (isMovie) { - // Movies - val episodesUrl = "$mainUrl/ajax/movie/episodes/$id" - val episodes = app.get(episodesUrl).text - - // Supported streams, they're identical - val sourceIds = Jsoup.parse(episodes).select("a").mapNotNull { element -> - var sourceId = element.attr("data-id") - if (sourceId.isNullOrEmpty()) - sourceId = element.attr("data-linkid") - - if (element.select("span").text().trim().isValidServer()) { - if (sourceId.isNullOrEmpty()) { - fixUrlNull(element.attr("href")) - } else { - "$url.$sourceId".replace("/movie/", "/watch-movie/") - } - } else { - null - } - } - - val comingSoon = sourceIds.isEmpty() - - return newMovieLoadResponse(title, url, TvType.Movie, sourceIds) { - this.year = year - this.posterUrl = posterUrl - this.plot = plot - addDuration(duration) - addActors(cast) - this.tags = tags - this.recommendations = recommendations - this.comingSoon = comingSoon - addTrailer(youtubeTrailer) - this.rating = rating - } - } else { - val seasonsDocument = app.get("$mainUrl/ajax/v2/tv/seasons/$id").document - val episodes = arrayListOf() - var seasonItems = seasonsDocument.select("div.dropdown-menu.dropdown-menu-model > a") - if (seasonItems.isNullOrEmpty()) - seasonItems = seasonsDocument.select("div.dropdown-menu > a.dropdown-item") - seasonItems.apmapIndexed { season, element -> - val seasonId = element.attr("data-id") - if (seasonId.isNullOrBlank()) return@apmapIndexed - - var episode = 0 - val seasonEpisodes = app.get("$mainUrl/ajax/v2/season/episodes/$seasonId").document - var seasonEpisodesItems = - seasonEpisodes.select("div.flw-item.film_single-item.episode-item.eps-item") - if (seasonEpisodesItems.isNullOrEmpty()) { - seasonEpisodesItems = - seasonEpisodes.select("ul > li > a") - } - seasonEpisodesItems.forEach { - val episodeImg = it?.select("img") - val episodeTitle = episodeImg?.attr("title") ?: it.ownText() - val episodePosterUrl = episodeImg?.attr("src") - val episodeData = it.attr("data-id") ?: return@forEach - - episode++ - - val episodeNum = - (it.select("div.episode-number").text() - ?: episodeTitle).let { str -> - Regex("""\d+""").find(str)?.groupValues?.firstOrNull() - ?.toIntOrNull() - } ?: episode - - episodes.add( - newEpisode(Pair(url, episodeData)) { - this.posterUrl = fixUrlNull(episodePosterUrl) - this.name = episodeTitle?.removePrefix("Episode $episodeNum: ") - this.season = season + 1 - this.episode = episodeNum - } - ) - } - } - - return newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) { - this.posterUrl = posterUrl - this.year = year - this.plot = plot - addDuration(duration) - addActors(cast) - this.tags = tags - this.recommendations = recommendations - addTrailer(youtubeTrailer) - this.rating = rating - } - } - } - - data class Tracks( - @JsonProperty("file") val file: String?, - @JsonProperty("label") val label: String?, - @JsonProperty("kind") val kind: String? - ) - - data class Sources( - @JsonProperty("file") val file: String?, - @JsonProperty("type") val type: String?, - @JsonProperty("label") val label: String? - ) - - data class SourceObject( - @JsonProperty("sources") val sources: List?, - @JsonProperty("sources_1") val sources1: List?, - @JsonProperty("sources_2") val sources2: List?, - @JsonProperty("sourcesBackup") val sourcesBackup: List?, - @JsonProperty("tracks") val tracks: List? - ) - - data class IframeJson( -// @JsonProperty("type") val type: String? = null, - @JsonProperty("link") val link: String? = null, -// @JsonProperty("sources") val sources: ArrayList = arrayListOf(), -// @JsonProperty("tracks") val tracks: ArrayList = arrayListOf(), -// @JsonProperty("title") val title: String? = null - ) - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val urls = (tryParseJson>(data)?.let { (prefix, server) -> - val episodesUrl = "$mainUrl/ajax/v2/episode/servers/$server" - - // Supported streams, they're identical - app.get(episodesUrl).document.select("a").mapNotNull { element -> - val id = element?.attr("data-id") ?: return@mapNotNull null - if (element.select("span").text().trim().isValidServer()) { - "$prefix.$id".replace("/tv/", "/watch-tv/") - } else { - null - } - } - } ?: tryParseJson>(data))?.distinct() - - urls?.apmap { url -> - suspendSafeApiCall { - // Possible without token - -// val response = app.get(url) -// val key = -// response.document.select("script[src*=https://www.google.com/recaptcha/api.js?render=]") -// .attr("src").substringAfter("render=") -// val token = getCaptchaToken(mainUrl, key) ?: return@suspendSafeApiCall - - val serverId = url.substringAfterLast(".") - val iframeLink = - app.get("${this.mainUrl}/ajax/get_link/$serverId").parsed().link - ?: return@suspendSafeApiCall - - // Some smarter ws11 or w10 selection might be required in the future. - val extractorData = - "https://ws11.rabbitstream.net/socket.io/?EIO=4&transport=polling" - - if (iframeLink.contains("streamlare", ignoreCase = true)) { - loadExtractor(iframeLink, null, subtitleCallback, callback) - } else { - extractRabbitStream(iframeLink, subtitleCallback, callback, false) { it } - } - } - } - - return !urls.isNullOrEmpty() - } - - override suspend fun extractorVerifierJob(extractorData: String?) { - runSflixExtractorVerifierJob(this, extractorData, "https://rabbitstream.net/") - } - - private fun Element.toSearchResult(): SearchResponse { - val inner = this.selectFirst("div.film-poster") - val img = inner!!.select("img") - val title = img.attr("title") - val posterUrl = img.attr("data-src") ?: img.attr("src") - val href = fixUrl(inner.select("a").attr("href")) - val isMovie = href.contains("/movie/") - val otherInfo = - this.selectFirst("div.film-detail > div.fd-infor")?.select("span")?.toList() ?: listOf() - //var rating: Int? = null - var year: Int? = null - var quality: SearchQuality? = null - when (otherInfo.size) { - 1 -> { - year = otherInfo[0]?.text()?.trim()?.toIntOrNull() - } - 2 -> { - year = otherInfo[0]?.text()?.trim()?.toIntOrNull() - } - 3 -> { - //rating = otherInfo[0]?.text()?.toRatingInt() - quality = getQualityFromString(otherInfo[1]?.text()) - year = otherInfo[2]?.text()?.trim()?.toIntOrNull() - } - } - - return if (isMovie) { - MovieSearchResponse( - title, - href, - this@SflixProvider.name, - TvType.Movie, - posterUrl = posterUrl, - year = year, - quality = quality, - ) - } else { - TvSeriesSearchResponse( - title, - href, - this@SflixProvider.name, - TvType.Movie, - posterUrl, - year = year, - episodes = null, - quality = quality, - ) - } - } - - companion object { - data class PollingData( - @JsonProperty("sid") val sid: String? = null, - @JsonProperty("upgrades") val upgrades: ArrayList = arrayListOf(), - @JsonProperty("pingInterval") val pingInterval: Int? = null, - @JsonProperty("pingTimeout") val pingTimeout: Int? = null - ) - - /* - # python code to figure out the time offset based on code if necessary - chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_" - code = "Nxa_-bM" - total = 0 - for i, char in enumerate(code[::-1]): - index = chars.index(char) - value = index * 64**i - total += value - print(f"total {total}") - */ - private fun generateTimeStamp(): String { - val chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_" - var code = "" - var time = unixTimeMS - while (time > 0) { - code += chars[(time % (chars.length)).toInt()] - time /= chars.length - } - return code.reversed() - } - - - /** - * Generates a session - * 1 Get request. - * */ - private suspend fun negotiateNewSid(baseUrl: String): PollingData? { - // Tries multiple times - for (i in 1..5) { - val jsonText = - app.get("$baseUrl&t=${generateTimeStamp()}").text.replaceBefore("{", "") -// println("Negotiated sid $jsonText") - parseJson(jsonText)?.let { return it } - delay(1000L * i) - } - return null - } - - /** - * Generates a new session if the request fails - * @return the data and if it is new. - * */ - private suspend fun getUpdatedData( - response: NiceResponse, - data: PollingData, - baseUrl: String - ): Pair { - if (!response.okhttpResponse.isSuccessful) { - return negotiateNewSid(baseUrl)?.let { - it to true - } ?: data to false - } - return data to false - } - - - private suspend fun initPolling( - extractorData: String, - referer: String - ): Pair { - val headers = mapOf( - "Referer" to referer // "https://rabbitstream.net/" - ) - - val data = negotiateNewSid(extractorData) ?: return null to null - app.post( - "$extractorData&t=${generateTimeStamp()}&sid=${data.sid}", - requestBody = "40".toRequestBody(), - headers = headers - ) - - // This makes the second get request work, and re-connect work. - val reconnectSid = - parseJson( - app.get( - "$extractorData&t=${generateTimeStamp()}&sid=${data.sid}", - headers = headers - ) -// .also { println("First get ${it.text}") } - .text.replaceBefore("{", "") - ).sid - - // This response is used in the post requests. Same contents in all it seems. - val authInt = - app.get( - "$extractorData&t=${generateTimeStamp()}&sid=${data.sid}", - timeout = 60, - headers = headers - ).text - //.also { println("Second get ${it}") } - // Dunno if it's actually generated like this, just guessing. - .toIntOrNull()?.plus(1) ?: 3 - - return data to reconnectSid - } - - suspend fun runSflixExtractorVerifierJob( - api: MainAPI, - extractorData: String?, - referer: String - ) { - if (extractorData == null) return - val headers = mapOf( - "Referer" to referer // "https://rabbitstream.net/" - ) - - lateinit var data: PollingData - var reconnectSid = "" - - initPolling(extractorData, referer) - .also { - data = it.first ?: throw RuntimeException("Data Null") - reconnectSid = it.second ?: throw RuntimeException("ReconnectSid Null") - } - - // Prevents them from fucking us over with doing a while(true){} loop - val interval = maxOf(data.pingInterval?.toLong()?.plus(2000) ?: return, 10000L) - var reconnect = false - var newAuth = false - - - while (true) { - val authData = - when { - newAuth -> "40" - reconnect -> """42["_reconnect", "$reconnectSid"]""" - else -> "3" - } - - val url = "${extractorData}&t=${generateTimeStamp()}&sid=${data.sid}" - - getUpdatedData( - app.post(url, json = authData, headers = headers), - data, - extractorData - ).also { - newAuth = it.second - data = it.first - } - - //.also { println("Sflix post job ${it.text}") } - Log.d(api.name, "Running ${api.name} job $url") - - val time = measureTimeMillis { - // This acts as a timeout - val getResponse = app.get( - url, - timeout = interval / 1000, - headers = headers - ) -// .also { println("Sflix get job ${it.text}") } - reconnect = getResponse.text.contains("sid") - } - // Always waits even if the get response is instant, to prevent a while true loop. - if (time < interval - 4000) - delay(4000) - } - } - - // Only scrape servers with these names - fun String?.isValidServer(): Boolean { - val list = listOf("upcloud", "vidcloud", "streamlare") - return list.contains(this?.lowercase(Locale.ROOT)) - } - - // For re-use in Zoro - private suspend fun Sources.toExtractorLink( - caller: MainAPI, - name: String, - extractorData: String? = null, - ): List? { - return this.file?.let { file -> - //println("FILE::: $file") - val isM3u8 = URI(this.file).path.endsWith(".m3u8") || this.type.equals( - "hls", - ignoreCase = true - ) - return if (isM3u8) { - suspendSafeApiCall { - M3u8Helper().m3u8Generation( - M3u8Helper.M3u8Stream( - this.file, - null, - mapOf("Referer" to "https://mzzcloud.life/") - ), false - ) - .map { stream -> - ExtractorLink( - caller.name, - "${caller.name} $name", - stream.streamUrl, - caller.mainUrl, - getQualityFromName(stream.quality?.toString()), - true, - extractorData = extractorData - ) - } - } ?: listOf( - // Fallback if m3u8 extractor fails - ExtractorLink( - caller.name, - "${caller.name} $name", - this.file, - caller.mainUrl, - getQualityFromName(this.label), - isM3u8, - extractorData = extractorData - ) - ) - } else { - listOf( - ExtractorLink( - caller.name, - caller.name, - file, - caller.mainUrl, - getQualityFromName(this.label), - false, - extractorData = extractorData - ) - ) - } - } - } - - private fun Tracks.toSubtitleFile(): SubtitleFile? { - return this.file?.let { - SubtitleFile( - this.label ?: "Unknown", - it - ) - } - } - - suspend fun MainAPI.extractRabbitStream( - url: String, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit, - useSidAuthentication: Boolean, - /** Used for extractorLink name, input: Source name */ - extractorData: String? = null, - nameTransformer: (String) -> String, - ) = suspendSafeApiCall { - // https://rapid-cloud.ru/embed-6/dcPOVRE57YOT?z= -> https://rapid-cloud.ru/embed-6 - val mainIframeUrl = - url.substringBeforeLast("/") - val mainIframeId = url.substringAfterLast("/") - .substringBefore("?") // https://rapid-cloud.ru/embed-6/dcPOVRE57YOT?z= -> dcPOVRE57YOT - val iframe = app.get(url, referer = mainUrl) - val iframeKey = - iframe.document.select("script[src*=https://www.google.com/recaptcha/api.js?render=]") - .attr("src").substringAfter("render=") - val iframeToken = getCaptchaToken(url, iframeKey) - val number = - Regex("""recaptchaNumber = '(.*?)'""").find(iframe.text)?.groupValues?.get(1) - - var sid: String? = null - if (useSidAuthentication && extractorData != null) { - negotiateNewSid(extractorData)?.also { pollingData -> - app.post( - "$extractorData&t=${generateTimeStamp()}&sid=${pollingData.sid}", - requestBody = "40".toRequestBody(), - timeout = 60 - ) - val text = app.get( - "$extractorData&t=${generateTimeStamp()}&sid=${pollingData.sid}", - timeout = 60 - ).text.replaceBefore("{", "") - - sid = parseJson(text).sid - ioSafe { app.get("$extractorData&t=${generateTimeStamp()}&sid=${pollingData.sid}") } - } - } - - val mapped = app.get( - "${ - mainIframeUrl.replace( - "/embed", - "/ajax/embed" - ) - }/getSources?id=$mainIframeId&_token=$iframeToken&_number=$number${sid?.let { "$&sId=$it" } ?: ""}", - referer = mainUrl, - headers = mapOf( - "X-Requested-With" to "XMLHttpRequest", - "Accept" to "*/*", - "Accept-Language" to "en-US,en;q=0.5", -// "Cache-Control" to "no-cache", - "Connection" to "keep-alive", -// "Sec-Fetch-Dest" to "empty", -// "Sec-Fetch-Mode" to "no-cors", -// "Sec-Fetch-Site" to "cross-site", -// "Pragma" to "no-cache", -// "Cache-Control" to "no-cache", - "TE" to "trailers" - ) - ).parsed() - - mapped.tracks?.forEach { track -> - track?.toSubtitleFile()?.let { subtitleFile -> - subtitleCallback.invoke(subtitleFile) - } - } - - val list = listOf( - mapped.sources to "source 1", - mapped.sources1 to "source 2", - mapped.sources2 to "source 3", - mapped.sourcesBackup to "source backup" - ) - list.forEach { subList -> - subList.first?.forEach { source -> - source?.toExtractorLink( - this, - nameTransformer(subList.second), - extractorData, - ) - ?.forEach { - // Sets Zoro SID used for video loading -// (this as? ZoroProvider)?.sid?.set(it.url.hashCode(), sid) - callback(it) - } - } - } - } - } -} - diff --git a/SflixProvider/src/main/kotlin/com/lagradost/SflixProviderPlugin.kt b/SflixProvider/src/main/kotlin/com/lagradost/SflixProviderPlugin.kt deleted file mode 100644 index 17c721a..0000000 --- a/SflixProvider/src/main/kotlin/com/lagradost/SflixProviderPlugin.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class SflixProviderPlugin : Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(SflixProvider()) - registerMainAPI(SolarmovieProvider()) - registerMainAPI(TwoEmbedProvider()) - registerMainAPI(DopeboxProvider()) - registerMainAPI(ZoroProvider()) - registerMainAPI(HDTodayProvider()) - } -} \ No newline at end of file diff --git a/SflixProvider/src/main/kotlin/com/lagradost/SolarmovieProvider.kt b/SflixProvider/src/main/kotlin/com/lagradost/SolarmovieProvider.kt deleted file mode 100644 index dbd827a..0000000 --- a/SflixProvider/src/main/kotlin/com/lagradost/SolarmovieProvider.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.lagradost - -class SolarmovieProvider : SflixProvider() { - override var mainUrl = "https://solarmovie.pe" - override var name = "Solarmovie" -} \ No newline at end of file diff --git a/SflixProvider/src/main/kotlin/com/lagradost/TwoEmbedProvider.kt b/SflixProvider/src/main/kotlin/com/lagradost/TwoEmbedProvider.kt deleted file mode 100644 index a97887d..0000000 --- a/SflixProvider/src/main/kotlin/com/lagradost/TwoEmbedProvider.kt +++ /dev/null @@ -1,79 +0,0 @@ -package com.lagradost - -import android.util.Log -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.SflixProvider.Companion.extractRabbitStream -import com.lagradost.SflixProvider.Companion.runSflixExtractorVerifierJob -import com.lagradost.cloudstream3.APIHolder.getCaptchaToken -import com.lagradost.cloudstream3.SubtitleFile -import com.lagradost.cloudstream3.TvType -import com.lagradost.cloudstream3.apmap -import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.metaproviders.TmdbLink -import com.lagradost.cloudstream3.metaproviders.TmdbProvider -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor - -class TwoEmbedProvider : TmdbProvider() { - override val apiName = "2Embed" - override var name = "2Embed" - override var mainUrl = "https://www.2embed.to" - override val useMetaLoadResponse = true - override val instantLinkLoading = false - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - ) - - data class EmbedJson ( - @JsonProperty("type") val type: String?, - @JsonProperty("link") val link: String, - @JsonProperty("sources") val sources: List, - @JsonProperty("tracks") val tracks: List? - ) - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val mappedData = parseJson(data) - val (id, site) = if (mappedData.imdbID != null) listOf( - mappedData.imdbID, - "imdb" - ) else listOf(mappedData.tmdbID.toString(), "tmdb") - val isMovie = mappedData.episode == null && mappedData.season == null - val embedUrl = if (isMovie) { - "$mainUrl/embed/$site/movie?id=$id" - } else { - val suffix = "$id&s=${mappedData.season ?: 1}&e=${mappedData.episode ?: 1}" - "$mainUrl/embed/$site/tv?id=$suffix" - } - - val document = app.get(embedUrl).document - val captchaKey = - document.select("script[src*=https://www.google.com/recaptcha/api.js?render=]") - .attr("src").substringAfter("render=") - - val servers = document.select(".dropdown-menu a[data-id]").map { it.attr("data-id") } - servers.apmap { serverID -> - val token = getCaptchaToken(embedUrl, captchaKey) - val ajax = app.get("$mainUrl/ajax/embed/play?id=$serverID&_token=$token", referer = embedUrl).text - val mappedservers = parseJson(ajax) - val iframeLink = mappedservers.link - if (iframeLink.contains("rabbitstream")) { - extractRabbitStream(iframeLink, subtitleCallback, callback, false) { it } - } else { - loadExtractor(iframeLink, embedUrl, subtitleCallback, callback) - } - } - return true - } - - override suspend fun extractorVerifierJob(extractorData: String?) { - Log.d(this.name, "Starting ${this.name} job!") - runSflixExtractorVerifierJob(this, extractorData, "https://rabbitstream.net/") - } -} diff --git a/SflixProvider/src/main/kotlin/com/lagradost/ZoroProvider.kt b/SflixProvider/src/main/kotlin/com/lagradost/ZoroProvider.kt deleted file mode 100644 index 7ff14a6..0000000 --- a/SflixProvider/src/main/kotlin/com/lagradost/ZoroProvider.kt +++ /dev/null @@ -1,371 +0,0 @@ -package com.lagradost - -import android.util.Log -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.SflixProvider.Companion.extractRabbitStream -import com.lagradost.SflixProvider.Companion.runSflixExtractorVerifierJob -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId -import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -import com.lagradost.cloudstream3.utils.Coroutines.ioSafe -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor -import com.lagradost.nicehttp.Requests.Companion.await -import okhttp3.Interceptor -import org.jsoup.Jsoup -import org.jsoup.nodes.Element -import java.net.URI - -private const val OPTIONS = "OPTIONS" - -class ZoroProvider : MainAPI() { - override var mainUrl = "https://zoro.to" - override var name = "Zoro" - override val hasQuickSearch = false - override val hasMainPage = true - override val hasChromecastSupport = true - override val hasDownloadSupport = true - override val usesWebView = 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 - } - } - } - - val epRegex = Regex("Ep (\\d+)/") - private fun Element.toSearchResult(): SearchResponse? { - val href = fixUrl(this.select("a").attr("href")) - val title = this.select("h3.film-name").text() - val dubSub = this.select(".film-poster > .tick.ltr").text() - //val episodes = this.selectFirst(".film-poster > .tick-eps")?.text()?.toIntOrNull() - - val dubExist = dubSub.contains("dub", ignoreCase = true) - val subExist = dubSub.contains("sub", ignoreCase = true) - val episodes = - this.selectFirst(".film-poster > .tick.rtl > .tick-eps")?.text()?.let { eps -> - //println("REGEX:::: $eps") - // current episode / max episode - //Regex("Ep (\\d+)/(\\d+)") - epRegex.find(eps)?.groupValues?.get(1)?.toIntOrNull() - } - if (href.contains("/news/") || title.trim().equals("News", ignoreCase = true)) return null - val posterUrl = fixUrl(this.select("img").attr("data-src")) - val type = getType(this.select("div.fd-infor > span.fdi-item").text()) - - return newAnimeSearchResponse(title, href, type) { - this.posterUrl = posterUrl - addDubStatus(dubExist, subExist, episodes, episodes) - } - } - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val html = app.get("$mainUrl/home").text - val document = Jsoup.parse(html) - - val homePageList = ArrayList() - - document.select("div.anif-block").forEach { block -> - val header = block.select("div.anif-block-header").text().trim() - val animes = block.select("li").mapNotNull { - it.toSearchResult() - } - if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes)) - } - - document.select("section.block_area.block_area_home").forEach { block -> - val header = block.select("h2.cat-heading").text().trim() - val animes = block.select("div.flw-item").mapNotNull { - it.toSearchResult() - } - if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes)) - } - - return HomePageResponse(homePageList) - } - - private data class Response( - @JsonProperty("status") val status: Boolean, - @JsonProperty("html") val html: String - ) - -// override suspend fun quickSearch(query: String): List { -// val url = "$mainUrl/ajax/search/suggest?keyword=${query}" -// val html = mapper.readValue(khttp.get(url).text).html -// val document = Jsoup.parse(html) -// -// return document.select("a.nav-item").map { -// val title = it.selectFirst(".film-name")?.text().toString() -// val href = fixUrl(it.attr("href")) -// val year = it.selectFirst(".film-infor > span")?.text()?.split(",")?.get(1)?.trim()?.toIntOrNull() -// val image = it.select("img").attr("data-src") -// -// AnimeSearchResponse( -// title, -// href, -// this.name, -// TvType.TvSeries, -// image, -// year, -// null, -// EnumSet.of(DubStatus.Subbed), -// null, -// null -// ) -// -// } -// } - - override suspend fun search(query: String): List { - val link = "$mainUrl/search?keyword=$query" - val html = app.get(link).text - val document = Jsoup.parse(html) - - return document.select(".flw-item").map { - val title = it.selectFirst(".film-detail > .film-name > a")?.attr("title").toString() - val filmPoster = it.selectFirst(".film-poster") - val poster = filmPoster!!.selectFirst("img")?.attr("data-src") - - val episodes = filmPoster.selectFirst("div.rtl > div.tick-eps")?.text()?.let { eps -> - // current episode / max episode - val epRegex = Regex("Ep (\\d+)/")//Regex("Ep (\\d+)/(\\d+)") - epRegex.find(eps)?.groupValues?.get(1)?.toIntOrNull() - } - val dubsub = filmPoster.selectFirst("div.ltr")?.text() - val dubExist = dubsub?.contains("DUB") ?: false - val subExist = dubsub?.contains("SUB") ?: false || dubsub?.contains("RAW") ?: false - - val tvType = - getType(it.selectFirst(".film-detail > .fd-infor > .fdi-item")?.text().toString()) - val href = fixUrl(it.selectFirst(".film-name a")!!.attr("href")) - - newAnimeSearchResponse(title, href, tvType) { - this.posterUrl = poster - addDubStatus(dubExist, subExist, episodes, episodes) - } - } - } - - private fun Element?.getActor(): Actor? { - val image = - fixUrlNull(this?.selectFirst(".pi-avatar > img")?.attr("data-src")) ?: return null - val name = this?.selectFirst(".pi-detail > .pi-name")?.text() ?: return null - return Actor(name = name, image = image) - } - - data class ZoroSyncData( - @JsonProperty("mal_id") val malId: String?, - @JsonProperty("anilist_id") val aniListId: String?, - ) - - override suspend fun load(url: String): LoadResponse { - val html = app.get(url).text - val document = Jsoup.parse(html) - - val syncData = tryParseJson(document.selectFirst("#syncData")?.data()) - - val title = document.selectFirst(".anisc-detail > .film-name")?.text().toString() - val poster = document.selectFirst(".anisc-poster img")?.attr("src") - val tags = document.select(".anisc-info a[href*=\"/genre/\"]").map { it.text() } - - var year: Int? = null - var japaneseTitle: String? = null - var status: ShowStatus? = null - - for (info in document.select(".anisc-info > .item.item-title")) { - val text = info?.text().toString() - when { - (year != null && japaneseTitle != null && status != null) -> break - text.contains("Premiered") && year == null -> - year = - info.selectFirst(".name")?.text().toString().split(" ").last().toIntOrNull() - - text.contains("Japanese") && japaneseTitle == null -> - japaneseTitle = info.selectFirst(".name")?.text().toString() - - text.contains("Status") && status == null -> - status = getStatus(info.selectFirst(".name")?.text().toString()) - } - } - - val description = document.selectFirst(".film-description.m-hide > .text")?.text() - val animeId = URI(url).path.split("-").last() - - val episodes = Jsoup.parse( - parseJson( - app.get( - "$mainUrl/ajax/v2/episode/list/$animeId" - ).text - ).html - ).select(".ss-list > a[href].ssl-item.ep-item").map { - newEpisode(it.attr("href")) { - this.name = it?.attr("title") - this.episode = it.selectFirst(".ssli-order")?.text()?.toIntOrNull() - } - } - - val actors = document.select("div.block-actors-content > div.bac-list-wrap > div.bac-item") - .mapNotNull { head -> - val subItems = head.select(".per-info") ?: return@mapNotNull null - if (subItems.isEmpty()) return@mapNotNull null - var role: ActorRole? = null - val mainActor = subItems.first()?.let { - role = when (it.selectFirst(".pi-detail > .pi-cast")?.text()?.trim()) { - "Supporting" -> ActorRole.Supporting - "Main" -> ActorRole.Main - else -> null - } - it.getActor() - } ?: return@mapNotNull null - val voiceActor = if (subItems.size >= 2) subItems[1]?.getActor() else null - ActorData(actor = mainActor, role = role, voiceActor = voiceActor) - } - - val recommendations = - document.select("#main-content > section > .tab-content > div > .film_list-wrap > .flw-item") - .mapNotNull { head -> - val filmPoster = head?.selectFirst(".film-poster") - val epPoster = filmPoster?.selectFirst("img")?.attr("data-src") - val a = head?.selectFirst(".film-detail > .film-name > a") - val epHref = a?.attr("href") - val epTitle = a?.attr("title") - if (epHref == null || epTitle == null || epPoster == null) { - null - } else { - AnimeSearchResponse( - epTitle, - fixUrl(epHref), - this.name, - TvType.Anime, - epPoster, - dubStatus = null - ) - } - } - - return newAnimeLoadResponse(title, url, TvType.Anime) { - japName = japaneseTitle - engName = title - posterUrl = poster - this.year = year - addEpisodes(DubStatus.Subbed, episodes) - showStatus = status - plot = description - this.tags = tags - this.recommendations = recommendations - this.actors = actors - addMalId(syncData?.malId?.toIntOrNull()) - addAniListId(syncData?.aniListId?.toIntOrNull()) - } - } - - private data class RapidCloudResponse( - @JsonProperty("link") val link: String - ) - - override suspend fun extractorVerifierJob(extractorData: String?) { - Log.d(this.name, "Starting ${this.name} job!") - runSflixExtractorVerifierJob(this, extractorData, "https://rapid-cloud.ru/") - } - - /** Url hashcode to sid */ - var sid: HashMap = hashMapOf() - - /** - * Makes an identical Options request before .ts request - * Adds an SID header to the .ts request. - * */ - override fun getVideoInterceptor(extractorLink: ExtractorLink): Interceptor { - // Needs to be object instead of lambda to make it compile correctly - return object : Interceptor { - override fun intercept(chain: Interceptor.Chain): okhttp3.Response { - val request = chain.request() - if (request.url.toString().endsWith(".ts") - && request.method != OPTIONS - // No option requests on VidCloud - && !request.url.toString().contains("betterstream") - ) { - val newRequest = - chain.request() - .newBuilder().apply { - sid[extractorLink.url.hashCode()]?.let { sid -> - addHeader("SID", sid) - } - } - .build() - val options = request.newBuilder().method(OPTIONS, request.body).build() - ioSafe { app.baseClient.newCall(options).await() } - - return chain.proceed(newRequest) - } else { - return chain.proceed(chain.request()) - } - } - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val servers: List> = Jsoup.parse( - app.get("$mainUrl/ajax/v2/episode/servers?episodeId=" + data.split("=")[1]) - .parsed().html - ).select(".server-item[data-type][data-id]").map { - Pair( - if (it.attr("data-type") == "sub") DubStatus.Subbed else DubStatus.Dubbed, - it.attr("data-id") - ) - } - - val extractorData = - "https://ws1.rapid-cloud.ru/socket.io/?EIO=4&transport=polling" - - // Prevent duplicates - servers.distinctBy { it.second }.apmap { - val link = - "$mainUrl/ajax/v2/episode/sources?id=${it.second}" - val extractorLink = app.get( - link, - ).parsed().link - val hasLoadedExtractorLink = - loadExtractor(extractorLink, "https://rapid-cloud.ru/", subtitleCallback, callback) - - if (!hasLoadedExtractorLink) { - extractRabbitStream( - extractorLink, - subtitleCallback, - // Blacklist VidCloud for now - { videoLink -> if (!videoLink.url.contains("betterstream")) callback(videoLink) }, - true, - extractorData - ) { sourceName -> - sourceName + " - ${it.first}" - } - } - } - - return true - } -} diff --git a/SoaptwoDayProvider/build.gradle.kts b/SoaptwoDayProvider/build.gradle.kts deleted file mode 100644 index abcbcfe..0000000 --- a/SoaptwoDayProvider/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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=secretlink.xyz&sz=24" -} \ No newline at end of file diff --git a/SoaptwoDayProvider/src/main/AndroidManifest.xml b/SoaptwoDayProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/SoaptwoDayProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/SoaptwoDayProvider/src/main/kotlin/com/lagradost/SoaptwoDayProvider.kt b/SoaptwoDayProvider/src/main/kotlin/com/lagradost/SoaptwoDayProvider.kt deleted file mode 100644 index 8b90e0b..0000000 --- a/SoaptwoDayProvider/src/main/kotlin/com/lagradost/SoaptwoDayProvider.kt +++ /dev/null @@ -1,263 +0,0 @@ -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 org.jsoup.Jsoup - -class SoaptwoDayProvider : MainAPI() { - override var mainUrl = "https://secretlink.xyz" //Probably a rip off, but it has no captcha - override var name = "Soap2Day" - override val hasMainPage = true - override val hasChromecastSupport = true - override val hasDownloadSupport = true - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - ) - - override val mainPage = mainPageOf( - Pair("$mainUrl/movielist?page=", "Movies"), - Pair("$mainUrl/tvlist?page=", "TV Series"), - ) - - 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.container div.row div.col-sm-12.col-lg-12 div.row div.col-sm-12.col-lg-12 .col-xs-6") - .map { - val title = it.selectFirst("h5 a")!!.text() - val link = it.selectFirst("a")!!.attr("href") - TvSeriesSearchResponse( - title, - link, - this.name, - TvType.TvSeries, - fixUrl(it.selectFirst("img")!!.attr("src")), - null, - null, - ) - } - return newHomePageResponse(request.name, home) - } - - override suspend fun search(query: String): List { - val doc = app.get("$mainUrl/search/keyword/$query").document - return doc.select("div.container div.row div.col-sm-12.col-lg-12 div.row div.col-sm-12.col-lg-12 .col-xs-6") - .map { - val title = it.selectFirst("h5 a")!!.text() - val image = fixUrl(it.selectFirst("img")!!.attr("src")) - val href = fixUrl(it.selectFirst("a")!!.attr("href")) - TvSeriesSearchResponse( - title, - href, - this.name, - TvType.TvSeries, - image, - null, - null - ) - } - } - - override suspend fun load(url: String): LoadResponse? { - val soup = app.get(url).document - val title = soup.selectFirst(".hidden-lg > div:nth-child(1) > h4")?.text() ?: "" - val description = soup.selectFirst("p#wrap")?.text()?.trim() - val poster = - soup.selectFirst(".col-md-5 > div:nth-child(1) > div:nth-child(1) > img")?.attr("src") - val episodes = mutableListOf() - soup.select("div.alert").forEach { - val season = it?.selectFirst("h4")?.text()?.filter { c -> c.isDigit() }?.toIntOrNull() - it?.select("div > div > a")?.forEach { entry -> - val link = fixUrlNull(entry?.attr("href")) ?: return@forEach - val text = entry?.text() ?: "" - val name = text.replace(Regex("(^(\\d+)\\.)"), "") - val epNum = text.substring(0, text.indexOf(".")).toIntOrNull() - episodes.add( - Episode( - name = name, - data = link, - season = season, - episode = epNum - ) - ) - } - } - val otherInfoBody = soup.select("div.col-sm-8 div.panel-body").toString() - //Fetch casts - val casts = otherInfoBody.substringAfter("Stars : ") - .substringBefore("Genre : ").let { - Jsoup.parse(it).select("a") - }.mapNotNull { - val castName = it?.text() ?: return@mapNotNull null - ActorData( - Actor( - name = castName - ) - ) - } - //Fetch year - val year = otherInfoBody.substringAfter("

    Release :

    ") - .substringBefore(" year string: $it") - Jsoup.parse(it).select("p")[1] - }?.text()?.take(4)?.toIntOrNull() - //Fetch genres - val genre = otherInfoBody.substringAfter("

    Genre :

    ") - .substringBefore("

    Release :

    ").let { - //Log.i(this.name, "Result => genre string: $it") - Jsoup.parse(it).select("a") - }.mapNotNull { it?.text()?.trim() ?: return@mapNotNull null } - - return when (val tvType = if (episodes.isEmpty()) TvType.Movie else TvType.TvSeries) { - TvType.TvSeries -> { - TvSeriesLoadResponse( - title, - url, - this.name, - tvType, - episodes.reversed(), - fixUrlNull(poster), - year = year, - description, - actors = casts, - tags = genre - ) - } - TvType.Movie -> { - MovieLoadResponse( - title, - url, - this.name, - tvType, - url, - fixUrlNull(poster), - year = year, - description, - actors = casts, - tags = genre - ) - } - else -> null - } - } - - data class ServerJson( - @JsonProperty("0") val zero: String?, - @JsonProperty("key") val key: Boolean?, - @JsonProperty("val") val stream: String?, - @JsonProperty("val_bak") val streambackup: String?, - @JsonProperty("pos") val pos: Int?, - @JsonProperty("type") val type: String?, - @JsonProperty("subs") val subs: List?, - @JsonProperty("prev_epi_title") val prevEpiTitle: String?, - @JsonProperty("prev_epi_url") val prevEpiUrl: String?, - @JsonProperty("next_epi_title") val nextEpiTitle: String?, - @JsonProperty("next_epi_url") val nextEpiUrl: String? - ) - - data class Subs( - @JsonProperty("id") val id: Int?, - @JsonProperty("movieId") val movieId: Int?, - @JsonProperty("tvId") val tvId: Int?, - @JsonProperty("episodeId") val episodeId: Int?, - @JsonProperty("default") val default: Int?, - @JsonProperty("IsShow") val IsShow: Int?, - @JsonProperty("name") val name: String, - @JsonProperty("path") val path: String?, - @JsonProperty("downlink") val downlink: String?, - @JsonProperty("source_file_name") val sourceFileName: String?, - @JsonProperty("createtime") val createtime: Int? - ) - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val doc = app.get(data).document - val idplayer = doc.selectFirst("#divU")?.text() - val idplayer2 = doc.selectFirst("#divP")?.text() - val movieid = doc.selectFirst("div.row input#hId")!!.attr("value") - val tvType = try { - doc.selectFirst(".col-md-5 > div:nth-child(1) > div:nth-child(1) > img")!!.attr("src") - ?: "" - } catch (e: Exception) { - "" - } - val ajaxlink = - if (tvType.contains("movie")) "$mainUrl/home/index/GetMInfoAjax" else "$mainUrl/home/index/GetEInfoAjax" - listOf( - idplayer, - idplayer2, - ).mapNotNull { playerID -> - val url = app.post( - ajaxlink, - headers = mapOf( - "Host" to "secretlink.xyz", - "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://secretlink.xyz", - "DNT" to "1", - "Connection" to "keep-alive", - "Referer" to data, - "Sec-Fetch-Dest" to "empty", - "Sec-Fetch-Mode" to "cors", - "Sec-Fetch-Site" to "same-origin", - ), - data = mapOf( - Pair("pass", movieid), - Pair("param", playerID ?: ""), - ) - ).text.replace("\\\"", "\"").replace("\"{", "{").replace("}\"", "}") - .replace("\\\\\\/", "\\/") - val json = parseJson(url) - listOfNotNull( - json.stream, - json.streambackup - ).apmap { stream -> - val cleanstreamurl = stream.replace("\\/", "/").replace("\\\\\\", "") - if (cleanstreamurl.isNotBlank()) { - callback( - ExtractorLink( - "Soap2Day", - "Soap2Day", - cleanstreamurl, - "https://soap2day.ac", - Qualities.Unknown.value, - isM3u8 = false - ) - ) - } - } - json.subs?.forEach { subtitle -> - val sublink = mainUrl + subtitle.path - listOf( - sublink, - subtitle.downlink - ).mapNotNull { subs -> - if (subs != null) { - if (subs.isNotBlank()) { - subtitleCallback( - SubtitleFile(subtitle.name, subs) - ) - } - } - } - } - } - return true - } -} diff --git a/SoaptwoDayProvider/src/main/kotlin/com/lagradost/SoaptwoDayProviderPlugin.kt b/SoaptwoDayProvider/src/main/kotlin/com/lagradost/SoaptwoDayProviderPlugin.kt deleted file mode 100644 index e006f92..0000000 --- a/SoaptwoDayProvider/src/main/kotlin/com/lagradost/SoaptwoDayProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class SoaptwoDayProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(SoaptwoDayProvider()) - } -} \ No newline at end of file diff --git a/SuperStream/build.gradle.kts b/SuperStream/build.gradle.kts deleted file mode 100644 index 5593518..0000000 --- a/SuperStream/build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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", - "Movie", - "AnimeMovie", - "TvSeries", - ) - iconUrl = "https://raw.githubusercontent.com/recloudstream/cloudstream-extensions/master/SuperStream/icon.png" -} diff --git a/SuperStream/icon.png b/SuperStream/icon.png deleted file mode 100644 index 2f0c206..0000000 Binary files a/SuperStream/icon.png and /dev/null differ diff --git a/SuperStream/src/main/AndroidManifest.xml b/SuperStream/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/SuperStream/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/SuperStream/src/main/kotlin/com/lagradost/SuperStream.kt b/SuperStream/src/main/kotlin/com/lagradost/SuperStream.kt deleted file mode 100644 index 4fb0202..0000000 --- a/SuperStream/src/main/kotlin/com/lagradost/SuperStream.kt +++ /dev/null @@ -1,663 +0,0 @@ -package com.lagradost - -import android.util.Base64 -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.SuperStream.CipherUtils.getVerify -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.APIHolder.capitalize -import com.lagradost.cloudstream3.APIHolder.unixTime -import com.lagradost.cloudstream3.LoadResponse.Companion.addImdbId -import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.AppUtils.toJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.getQualityFromName -import com.lagradost.nicehttp.NiceResponse -import java.nio.charset.StandardCharsets -import java.security.MessageDigest -import java.security.NoSuchAlgorithmException -import javax.crypto.Cipher -import javax.crypto.spec.IvParameterSpec -import javax.crypto.spec.SecretKeySpec -import kotlin.math.roundToInt - -const val TYPE_SERIES = 2 -const val TYPE_MOVIES = 1 - -class SuperStream : MainAPI() { - override var name = "SuperStream" - override val hasMainPage = true - override val hasChromecastSupport = true - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - TvType.Anime, - TvType.AnimeMovie, - ) - - // 0 to get nsfw - private val hideNsfw = 1 - - override val instantLinkLoading = true - - val headers = mapOf( - "Platform" to "android", - "Accept" to "charset=utf-8", - ) - - // Random 32 length string - private fun randomToken(): String { - return (0..31).joinToString("") { - (('0'..'9') + ('a'..'f')).random().toString() - } - } - - private val token = randomToken() - - private object CipherUtils { - private const val ALGORITHM = "DESede" - private const val TRANSFORMATION = "DESede/CBC/PKCS5Padding" - fun encrypt(str: String, key: String, iv: String): String? { - return try { - val cipher: Cipher = Cipher.getInstance(TRANSFORMATION) - val bArr = ByteArray(24) - val bytes: ByteArray = key.toByteArray() - var length = if (bytes.size <= 24) bytes.size else 24 - System.arraycopy(bytes, 0, bArr, 0, length) - while (length < 24) { - bArr[length] = 0 - length++ - } - cipher.init( - 1, - SecretKeySpec(bArr, ALGORITHM), - IvParameterSpec(iv.toByteArray()) - ) - - String(Base64.encode(cipher.doFinal(str.toByteArray()), 2), StandardCharsets.UTF_8) - } catch (e: Exception) { - e.printStackTrace() - null - } - } - - fun md5(str: String): String? { - return MD5Util.md5(str)?.let { HexDump.toHexString(it).lowercase() } - } - - fun getVerify(str: String?, str2: String, str3: String): String? { - if (str != null) { - return md5(md5(str2) + str3 + str) - } - return null - } - } - - private object HexDump { - private val HEX_DIGITS = charArrayOf( - '0', - '1', - '2', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - 'A', - 'B', - 'C', - 'D', - 'E', - 'F' - ) - - @JvmOverloads - fun toHexString(bArr: ByteArray, i: Int = 0, i2: Int = bArr.size): String { - val cArr = CharArray(i2 * 2) - var i3 = 0 - for (i4 in i until i + i2) { - val b = bArr[i4].toInt() - val i5 = i3 + 1 - val cArr2 = HEX_DIGITS - cArr[i3] = cArr2[b ushr 4 and 15] - i3 = i5 + 1 - cArr[i5] = cArr2[b and 15] - } - return String(cArr) - } - } - - private object MD5Util { - fun md5(str: String): ByteArray? { - return this.md5(str.toByteArray()) - } - - fun md5(bArr: ByteArray?): ByteArray? { - return try { - val digest = MessageDigest.getInstance("MD5") - digest.update(bArr ?: return null) - digest.digest() - } catch (e: NoSuchAlgorithmException) { - e.printStackTrace() - null - } - } - } - - private suspend fun queryApi(query: String): NiceResponse { - val encryptedQuery = CipherUtils.encrypt(query, key, iv)!! - val appKeyHash = CipherUtils.md5(appKey)!! - val newBody = - """{"app_key":"$appKeyHash","verify":"${ - getVerify( - encryptedQuery, - appKey, - key - ) - }","encrypt_data":"$encryptedQuery"}""" - val base64Body = String(Base64.encode(newBody.toByteArray(), Base64.DEFAULT)) - - val data = mapOf( - "data" to base64Body, - "appid" to "27", - "platform" to "android", - "version" to "129", - // Probably best to randomize this - "medium" to "Website&token$token" - ) - - return app.post(apiUrl, headers = headers, data = data) - } - - private suspend inline fun queryApiParsed(query: String): T { - return queryApi(query).parsed() - } - - private fun getExpiryDate(): Long { - // Current time + 12 hours - return unixTime + 60 * 60 * 12 - } - - private data class PostJSON( - @JsonProperty("id") val id: Int? = null, - @JsonProperty("title") val title: String? = null, - @JsonProperty("poster") val poster: String? = null, - @JsonProperty("poster_2") val poster2: String? = null, - @JsonProperty("box_type") val boxType: Int? = null, - @JsonProperty("imdb_rating") val imdbRating: String? = null, - @JsonProperty("quality_tag") val quality_tag: String? = null, - ) - - private data class ListJSON( - @JsonProperty("code") val code: Int? = null, - @JsonProperty("type") val type: String? = null, - @JsonProperty("name") val name: String? = null, - @JsonProperty("box_type") val boxType: Int? = null, - @JsonProperty("list") val list: ArrayList = arrayListOf(), - ) - - private data class DataJSON( - @JsonProperty("data") val data: ArrayList = arrayListOf() - ) - - // We do not want content scanners to notice this scraping going on so we've hidden all constants - // The source has its origins in China so I added some extra security with banned words - // Mayhaps a tiny bit unethical, but this source is just too good :) - // If you are copying this code please use precautions so they do not change their api. - - // Free Tibet, The Tienanmen Square protests of 1989 - private val iv = base64Decode("d0VpcGhUbiE=") - private val key = base64Decode("MTIzZDZjZWRmNjI2ZHk1NDIzM2FhMXc2") - private val ip = base64Decode("aHR0cHM6Ly8xNTIuMzIuMTQ5LjE2MA==") - private val apiUrl = - "$ip${base64Decode("L2FwaS9hcGlfY2xpZW50L2luZGV4Lw==")}" - private val appKey = base64Decode("bW92aWVib3g=") - private val appId = base64Decode("Y29tLnRkby5zaG93Ym94") - - override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { - val json = queryApi( - """{"childmode":"$hideNsfw","app_version":"11.5","appid":"$appId","module":"Home_list_type_v2","channel":"Website","page":"$page","lang":"en","type":"all","pagelimit":"10","expired_date":"${getExpiryDate()}","platform":"android"} - """.trimIndent() - ).text - - // Cut off the first row (featured) - val pages = parseJson(json).data.let { it.subList(minOf(it.size, 1), it.size) } - .mapNotNull { - var name = it.name - if (name.isNullOrEmpty()) name = "Featured" - val postList = it.list.mapNotNull second@{ post -> - val type = if (post.boxType == 1) TvType.Movie else TvType.TvSeries - newMovieSearchResponse( - name = post.title ?: return@second null, - url = LoadData(post.id ?: return@mapNotNull null, post.boxType).toJson(), - type = type, - fix = false - ) { - posterUrl = post.poster ?: post.poster2 - quality = getQualityFromString(post.quality_tag ?: "") - } - } - if (postList.isEmpty()) return@mapNotNull null - HomePageList(name, postList) - } - return HomePageResponse(pages, hasNext = !pages.any { it.list.isEmpty() }) - } - - private data class Data( - @JsonProperty("id") val id: Int? = null, - @JsonProperty("box_type") val boxType: Int? = null, - @JsonProperty("title") val title: String? = null, - @JsonProperty("poster_org") val posterOrg: String? = null, - @JsonProperty("poster") val poster: String? = null, - @JsonProperty("cats") val cats: String? = null, - @JsonProperty("year") val year: Int? = null, - @JsonProperty("imdb_rating") val imdbRating: String? = null, - @JsonProperty("quality_tag") val qualityTag: String? = null, - ) - - private data class MainData( - @JsonProperty("data") val data: ArrayList = arrayListOf() - ) - - override suspend fun search(query: String): List { - - val apiQuery = - // Originally 8 pagelimit - """{"childmode":"$hideNsfw","app_version":"11.5","appid":"$appId","module":"Search3","channel":"Website","page":"1","lang":"en","type":"all","keyword":"$query","pagelimit":"20","expired_date":"${getExpiryDate()}","platform":"android"}""" - val searchResponse = parseJson(queryApi(apiQuery).text).data.mapNotNull { - val type = if (it.boxType == 1) TvType.Movie else TvType.TvSeries - newMovieSearchResponse( - name = it.title ?: return@mapNotNull null, - url = LoadData(it.id ?: return@mapNotNull null, it.boxType).toJson(), - type = type, - fix = false - ) { - posterUrl = it.posterOrg ?: it.poster - year = it.year - quality = getQualityFromString(it.qualityTag?.replace("-", "") ?: "") - } - } - return searchResponse - } - - private data class LoadData( - val id: Int, - val type: Int? - ) - - private data class MovieData( - @JsonProperty("id") val id: Int? = null, - @JsonProperty("title") val title: String? = null, - @JsonProperty("director") val director: String? = null, - @JsonProperty("writer") val writer: String? = null, - @JsonProperty("actors") val actors: String? = null, - @JsonProperty("runtime") val runtime: Int? = null, - @JsonProperty("poster") val poster: String? = null, - @JsonProperty("description") val description: String? = null, - @JsonProperty("cats") val cats: String? = null, - @JsonProperty("year") val year: Int? = null, - @JsonProperty("imdb_id") val imdbId: String? = null, - @JsonProperty("imdb_rating") val imdbRating: String? = null, - @JsonProperty("trailer") val trailer: String? = null, - @JsonProperty("released") val released: String? = null, - @JsonProperty("content_rating") val contentRating: String? = null, - @JsonProperty("tmdb_id") val tmdbId: Int? = null, - @JsonProperty("tomato_meter") val tomatoMeter: Int? = null, - @JsonProperty("poster_org") val posterOrg: String? = null, - @JsonProperty("trailer_url") val trailerUrl: String? = null, - @JsonProperty("imdb_link") val imdbLink: String? = null, - @JsonProperty("box_type") val boxType: Int? = null, - ) - - private data class MovieDataProp( - @JsonProperty("data") val data: MovieData? = MovieData() - ) - - - private data class SeriesDataProp( - @JsonProperty("code") val code: Int? = null, - @JsonProperty("msg") val msg: String? = null, - @JsonProperty("data") val data: SeriesData? = SeriesData() - ) - - private data class SeriesSeasonProp( - @JsonProperty("code") val code: Int? = null, - @JsonProperty("msg") val msg: String? = null, - @JsonProperty("data") val data: ArrayList? = arrayListOf() - ) -// data class PlayProgress ( -// -// @JsonProperty("over" ) val over : Int? = null, -// @JsonProperty("seconds" ) val seconds : Int? = null, -// @JsonProperty("mp4_id" ) val mp4Id : Int? = null, -// @JsonProperty("last_time" ) val lastTime : Int? = null -// -//) - - private data class SeriesEpisode( - @JsonProperty("id") val id: Int? = null, - @JsonProperty("tid") val tid: Int? = null, - @JsonProperty("mb_id") val mbId: Int? = null, - @JsonProperty("imdb_id") val imdbId: String? = null, - @JsonProperty("imdb_id_status") val imdbIdStatus: Int? = null, - @JsonProperty("srt_status") val srtStatus: Int? = null, - @JsonProperty("season") val season: Int? = null, - @JsonProperty("episode") val episode: Int? = null, - @JsonProperty("state") val state: Int? = null, - @JsonProperty("title") val title: String? = null, - @JsonProperty("thumbs") val thumbs: String? = null, - @JsonProperty("thumbs_bak") val thumbsBak: String? = null, - @JsonProperty("thumbs_original") val thumbsOriginal: String? = null, - @JsonProperty("poster_imdb") val posterImdb: Int? = null, - @JsonProperty("synopsis") val synopsis: String? = null, - @JsonProperty("runtime") val runtime: Int? = null, - @JsonProperty("view") val view: Int? = null, - @JsonProperty("download") val download: Int? = null, - @JsonProperty("source_file") val sourceFile: Int? = null, - @JsonProperty("code_file") val codeFile: Int? = null, - @JsonProperty("add_time") val addTime: Int? = null, - @JsonProperty("update_time") val updateTime: Int? = null, - @JsonProperty("released") val released: String? = null, - @JsonProperty("released_timestamp") val releasedTimestamp: Long? = null, - @JsonProperty("audio_lang") val audioLang: String? = null, - @JsonProperty("quality_tag") val qualityTag: String? = null, - @JsonProperty("3d") val _3d: Int? = null, - @JsonProperty("remark") val remark: String? = null, - @JsonProperty("pending") val pending: String? = null, - @JsonProperty("imdb_rating") val imdbRating: String? = null, - @JsonProperty("display") val display: Int? = null, - @JsonProperty("sync") val sync: Int? = null, - @JsonProperty("tomato_meter") val tomatoMeter: Int? = null, - @JsonProperty("tomato_meter_count") val tomatoMeterCount: Int? = null, - @JsonProperty("tomato_audience") val tomatoAudience: Int? = null, - @JsonProperty("tomato_audience_count") val tomatoAudienceCount: Int? = null, - @JsonProperty("thumbs_min") val thumbsMin: String? = null, - @JsonProperty("thumbs_org") val thumbsOrg: String? = null, - @JsonProperty("imdb_link") val imdbLink: String? = null, -// @JsonProperty("quality_tags") val qualityTags: ArrayList = arrayListOf(), -// @JsonProperty("play_progress" ) val playProgress : PlayProgress? = PlayProgress() - - ) - - private data class SeriesLanguage( - @JsonProperty("title") val title: String? = null, - @JsonProperty("lang") val lang: String? = null - ) - - private data class SeriesData( - @JsonProperty("id") val id: Int? = null, - @JsonProperty("mb_id") val mbId: Int? = null, - @JsonProperty("title") val title: String? = null, - @JsonProperty("display") val display: Int? = null, - @JsonProperty("state") val state: Int? = null, - @JsonProperty("vip_only") val vipOnly: Int? = null, - @JsonProperty("code_file") val codeFile: Int? = null, - @JsonProperty("director") val director: String? = null, - @JsonProperty("writer") val writer: String? = null, - @JsonProperty("actors") val actors: String? = null, - @JsonProperty("add_time") val addTime: Int? = null, - @JsonProperty("poster") val poster: String? = null, - @JsonProperty("poster_imdb") val posterImdb: Int? = null, - @JsonProperty("banner_mini") val bannerMini: String? = null, - @JsonProperty("description") val description: String? = null, - @JsonProperty("imdb_id") val imdbId: String? = null, - @JsonProperty("cats") val cats: String? = null, - @JsonProperty("year") val year: Int? = null, - @JsonProperty("collect") val collect: Int? = null, - @JsonProperty("view") val view: Int? = null, - @JsonProperty("download") val download: Int? = null, - @JsonProperty("update_time") val updateTime: String? = null, - @JsonProperty("released") val released: String? = null, - @JsonProperty("released_timestamp") val releasedTimestamp: Int? = null, - @JsonProperty("episode_released") val episodeReleased: String? = null, - @JsonProperty("episode_released_timestamp") val episodeReleasedTimestamp: Int? = null, - @JsonProperty("max_season") val maxSeason: Int? = null, - @JsonProperty("max_episode") val maxEpisode: Int? = null, - @JsonProperty("remark") val remark: String? = null, - @JsonProperty("imdb_rating") val imdbRating: String? = null, - @JsonProperty("content_rating") val contentRating: String? = null, - @JsonProperty("tmdb_id") val tmdbId: Int? = null, - @JsonProperty("tomato_url") val tomatoUrl: String? = null, - @JsonProperty("tomato_meter") val tomatoMeter: Int? = null, - @JsonProperty("tomato_meter_count") val tomatoMeterCount: Int? = null, - @JsonProperty("tomato_meter_state") val tomatoMeterState: String? = null, - @JsonProperty("reelgood_url") val reelgoodUrl: String? = null, - @JsonProperty("audience_score") val audienceScore: Int? = null, - @JsonProperty("audience_score_count") val audienceScoreCount: Int? = null, - @JsonProperty("no_tomato_url") val noTomatoUrl: Int? = null, - @JsonProperty("order_year") val orderYear: Int? = null, - @JsonProperty("episodate_id") val episodateId: String? = null, - @JsonProperty("weights_day") val weightsDay: Double? = null, - @JsonProperty("poster_min") val posterMin: String? = null, - @JsonProperty("poster_org") val posterOrg: String? = null, - @JsonProperty("banner_mini_min") val bannerMiniMin: String? = null, - @JsonProperty("banner_mini_org") val bannerMiniOrg: String? = null, - @JsonProperty("trailer_url") val trailerUrl: String? = null, - @JsonProperty("years") val years: ArrayList = arrayListOf(), - @JsonProperty("season") val season: ArrayList = arrayListOf(), - @JsonProperty("history") val history: ArrayList = arrayListOf(), - @JsonProperty("imdb_link") val imdbLink: String? = null, - @JsonProperty("episode") val episode: ArrayList = arrayListOf(), -// @JsonProperty("is_collect") val isCollect: Int? = null, - @JsonProperty("language") val language: ArrayList = arrayListOf(), - @JsonProperty("box_type") val boxType: Int? = null, - @JsonProperty("year_year") val yearYear: String? = null, - @JsonProperty("season_episode") val seasonEpisode: String? = null - ) - - - override suspend fun load(url: String): LoadResponse { - val loadData = parseJson(url) - // val module = if(type === "TvType.Movie") "Movie_detail" else "*tv series module*" - - val isMovie = loadData.type == TYPE_MOVIES - - if (isMovie) { // 1 = Movie - val apiQuery = - """{"childmode":"$hideNsfw","uid":"","app_version":"11.5","appid":"$appId","module":"Movie_detail","channel":"Website","mid":"${loadData.id}","lang":"en","expired_date":"${getExpiryDate()}","platform":"android","oss":"","group":""}""" - val data = (queryApiParsed(apiQuery)).data - ?: throw RuntimeException("API error") - - return newMovieLoadResponse( - data.title ?: "", - url, - TvType.Movie, - LinkData( - data.id ?: throw RuntimeException("No movie ID"), - TYPE_MOVIES, - null, - null - ), - ) { - this.posterUrl = data.posterOrg ?: data.poster - this.year = data.year - this.plot = data.description - this.tags = data.cats?.split(",")?.map { it.capitalize() } - this.rating = data.imdbRating?.split("/")?.get(0)?.toIntOrNull() - addTrailer(data.trailerUrl) - this.addImdbId(data.imdbId) - } - } else { // 2 Series - val apiQuery = - """{"childmode":"$hideNsfw","uid":"","app_version":"11.5","appid":"$appId","module":"TV_detail_1","display_all":"1","channel":"Website","lang":"en","expired_date":"${getExpiryDate()}","platform":"android","tid":"${loadData.id}"}""" - val data = (queryApiParsed(apiQuery)).data - ?: throw RuntimeException("API error") - - val episodes = data.season.mapNotNull { - val seasonQuery = - """{"childmode":"$hideNsfw","app_version":"11.5","year":"0","appid":"$appId","module":"TV_episode","display_all":"1","channel":"Website","season":"$it","lang":"en","expired_date":"${getExpiryDate()}","platform":"android","tid":"${loadData.id}"}""" - (queryApiParsed(seasonQuery)).data - }.flatten() - - return newTvSeriesLoadResponse( - data.title ?: "", - url, - TvType.TvSeries, - episodes.mapNotNull { - Episode( - LinkData( - it.tid ?: it.id ?: return@mapNotNull null, - TYPE_SERIES, - it.season, - it.episode - ).toJson(), - it.title, - it.season, - it.episode, - it.thumbs ?: it.thumbsBak ?: it.thumbsMin ?: it.thumbsOriginal - ?: it.thumbsOrg, - it.imdbRating?.toDoubleOrNull()?.times(10)?.roundToInt(), - it.synopsis, - it.releasedTimestamp - ) - } - ) { - this.year = data.year - this.plot = data.description - this.posterUrl = data.posterOrg ?: data.poster - this.rating = data.imdbRating?.split("/")?.get(0)?.toIntOrNull() - this.tags = data.cats?.split(",")?.map { it.capitalize() } - this.addImdbId(data.imdbId) - } - } - } - - - private data class LinkData( - val id: Int, - val type: Int, - val season: Int?, - val episode: Int? - ) - - - private data class LinkDataProp( - @JsonProperty("code") val code: Int? = null, - @JsonProperty("msg") val msg: String? = null, - @JsonProperty("data") val data: ParsedLinkData? = ParsedLinkData() - ) - - private data class LinkList( - @JsonProperty("path") val path: String? = null, - @JsonProperty("quality") val quality: String? = null, - @JsonProperty("real_quality") val realQuality: String? = null, - @JsonProperty("format") val format: String? = null, - @JsonProperty("size") val size: String? = null, - @JsonProperty("size_bytes") val sizeBytes: Long? = null, - @JsonProperty("count") val count: Int? = null, - @JsonProperty("dateline") val dateline: Long? = null, - @JsonProperty("fid") val fid: Int? = null, - @JsonProperty("mmfid") val mmfid: Int? = null, - @JsonProperty("h265") val h265: Int? = null, - @JsonProperty("hdr") val hdr: Int? = null, - @JsonProperty("filename") val filename: String? = null, - @JsonProperty("original") val original: Int? = null, - @JsonProperty("colorbit") val colorbit: Int? = null, - @JsonProperty("success") val success: Int? = null, - @JsonProperty("timeout") val timeout: Int? = null, - @JsonProperty("vip_link") val vipLink: Int? = null, - @JsonProperty("fps") val fps: Int? = null, - @JsonProperty("bitstream") val bitstream: String? = null, - @JsonProperty("width") val width: Int? = null, - @JsonProperty("height") val height: Int? = null - ) - - private data class ParsedLinkData( - @JsonProperty("seconds") val seconds: Int? = null, - @JsonProperty("quality") val quality: ArrayList = arrayListOf(), - @JsonProperty("list") val list: ArrayList = arrayListOf() - ) - - private data class SubtitleDataProp( - @JsonProperty("code") val code: Int? = null, - @JsonProperty("msg") val msg: String? = null, - @JsonProperty("data") val data: PrivateSubtitleData? = PrivateSubtitleData() - ) - - private data class Subtitles( - @JsonProperty("sid") val sid: Int? = null, - @JsonProperty("mid") val mid: String? = null, - @JsonProperty("file_path") val filePath: String? = null, - @JsonProperty("lang") val lang: String? = null, - @JsonProperty("language") val language: String? = null, - @JsonProperty("delay") val delay: Int? = null, - @JsonProperty("point") val point: String? = null, - @JsonProperty("order") val order: Int? = null, - @JsonProperty("admin_order") val adminOrder: Int? = null, - @JsonProperty("myselect") val myselect: Int? = null, - @JsonProperty("add_time") val addTime: Long? = null, - @JsonProperty("count") val count: Int? = null - ) - - private data class SubtitleList( - - @JsonProperty("language") val language: String? = null, - @JsonProperty("subtitles") val subtitles: ArrayList = arrayListOf() - - ) - - private data class PrivateSubtitleData( - @JsonProperty("select") val select: ArrayList = arrayListOf(), - @JsonProperty("list") val list: ArrayList = arrayListOf() - ) - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - fun LinkList.toExtractorLink(): ExtractorLink? { - return ExtractorLink( - this@SuperStream.name, - this.filename ?: "", - this.path?.replace("\\/", "") ?: return null, - "", - getQualityFromName(this.quality), - ) - } - - fun Subtitles.toSubtitleFile(): SubtitleFile? { - return SubtitleFile( - this.language ?: this.lang ?: "", - this.filePath ?: return null - ) - } - - val parsed = parseJson(data) - - // No childmode when getting links - val query = if (parsed.type == TYPE_MOVIES) { - """{"childmode":"0","uid":"","app_version":"11.5","appid":"$appId","module":"Movie_downloadurl_v3","channel":"Website","mid":"${parsed.id}","lang":"","expired_date":"${getExpiryDate()}","platform":"android","oss":"1","group":""}""" - } else { - val episode = parsed.episode ?: throw RuntimeException("No episode number!") - val season = parsed.season ?: throw RuntimeException("No season number!") - """{"childmode":"0","app_version":"11.5","module":"TV_downloadurl_v3","channel":"Website","episode":"$episode","expired_date":"${getExpiryDate()}","platform":"android","tid":"${parsed.id}","oss":"1","uid":"","appid":"$appId","season":"$season","lang":"en","group":""}""" - } - - val linkData = queryApiParsed(query) - linkData.data?.list?.forEach { - callback.invoke(it.toExtractorLink() ?: return@forEach) - } - - // Should really run this query for every link :( - val fid = linkData.data?.list?.firstOrNull { it.fid != null }?.fid - - val subtitleQuery = if (parsed.type == TYPE_MOVIES) { - """{"childmode":"0","fid":"$fid","uid":"","app_version":"11.5","appid":"$appId","module":"Movie_srt_list_v2","channel":"Website","mid":"${parsed.id}","lang":"en","expired_date":"${getExpiryDate()}","platform":"android"}""" - } else { - """{"childmode":"0","fid":"$fid","app_version":"11.5","module":"TV_srt_list_v2","channel":"Website","episode":"${parsed.episode}","expired_date":"${getExpiryDate()}","platform":"android","tid":"${parsed.id}","uid":"","appid":"$appId","season":"${parsed.season}","lang":"en"}""" - } - - val subtitles = queryApiParsed(subtitleQuery).data - subtitles?.list?.forEach { - it.subtitles.forEach second@{ sub -> - subtitleCallback.invoke(sub.toSubtitleFile() ?: return@second) - } - } - - return true - } -} \ No newline at end of file diff --git a/SuperStream/src/main/kotlin/com/lagradost/SuperStreamPlugin.kt b/SuperStream/src/main/kotlin/com/lagradost/SuperStreamPlugin.kt deleted file mode 100644 index 7297a79..0000000 --- a/SuperStream/src/main/kotlin/com/lagradost/SuperStreamPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class SuperStreamPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(SuperStream()) - } -} \ No newline at end of file diff --git a/TenshiProvider/build.gradle.kts b/TenshiProvider/build.gradle.kts deleted file mode 100644 index d958ae3..0000000 --- a/TenshiProvider/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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", - "Movie", - "OVA", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=tenshi.moe&sz=24" -} \ No newline at end of file diff --git a/TenshiProvider/src/main/AndroidManifest.xml b/TenshiProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/TenshiProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/TenshiProvider/src/main/kotlin/com/lagradost/TenshiProvider.kt b/TenshiProvider/src/main/kotlin/com/lagradost/TenshiProvider.kt deleted file mode 100644 index 1fcf6a3..0000000 --- a/TenshiProvider/src/main/kotlin/com/lagradost/TenshiProvider.kt +++ /dev/null @@ -1,352 +0,0 @@ -package com.lagradost - -import android.annotation.SuppressLint -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.network.DdosGuardKiller -import com.lagradost.cloudstream3.network.getHeaders -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.getQualityFromName -import org.jsoup.nodes.Document -import java.net.URI -import java.text.SimpleDateFormat -import java.util.* - -class TenshiProvider : MainAPI() { - companion object { - //var token: String? = null - //var cookie: Map = mapOf() - - 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 - } - } - - override var mainUrl = "https://tenshi.moe" - override var name = "Tenshi.moe" - override val hasQuickSearch = false - override val hasMainPage = true - override val supportedTypes = setOf(TvType.Anime, TvType.AnimeMovie, TvType.OVA) - private var ddosGuardKiller = DdosGuardKiller(true) - - /*private fun loadToken(): Boolean { - return try { - val response = get(mainUrl) - cookie = response.cookies - val document = Jsoup.parse(response.text) - token = document.selectFirst("""meta[name="csrf-token"]""").attr("content") - token != null - } catch (e: Exception) { - false - } - }*/ - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val items = ArrayList() - val soup = app.get(mainUrl, interceptor = ddosGuardKiller).document - for (section in soup.select("#content > section")) { - try { - if (section.attr("id") == "toplist-tabs") { - for (top in section.select(".tab-content > [role=\"tabpanel\"]")) { - val title = "Top - " + top.attr("id").split("-")[1].replaceFirstChar { - if (it.isLowerCase()) it.titlecase( - Locale.UK - ) else it.toString() - } - val anime = top.select("li > a").map { - AnimeSearchResponse( - it.selectFirst(".thumb-title")!!.text(), - fixUrl(it.attr("href")), - this.name, - TvType.Anime, - it.selectFirst("img")!!.attr("src"), - null, - EnumSet.of(DubStatus.Subbed), - ) - } - items.add(HomePageList(title, anime)) - } - } else { - val title = section.selectFirst("h2")!!.text() - val anime = section.select("li > a").map { - AnimeSearchResponse( - it.selectFirst(".thumb-title")?.text() ?: "", - fixUrl(it.attr("href")), - this.name, - TvType.Anime, - it.selectFirst("img")!!.attr("src"), - null, - EnumSet.of(DubStatus.Subbed), - ) - } - items.add(HomePageList(title, anime)) - } - } catch (e: Exception) { - e.printStackTrace() - } - } - if (items.size <= 0) throw ErrorLoadingException() - return HomePageResponse(items) - } - - private fun getIsMovie(type: String, id: Boolean = false): Boolean { - if (!id) return type == "Movie" - - val movies = listOf("rrso24fa", "e4hqvtym", "bl5jdbqn", "u4vtznut", "37t6h2r4", "cq4azcrj") - val aniId = type.replace("$mainUrl/anime/", "") - return movies.contains(aniId) - } - - private fun parseSearchPage(soup: Document): List { - val items = soup.select("ul.thumb > li > a") - return items.map { - val href = fixUrl(it.attr("href")) - val img = fixUrl(it.selectFirst("img")!!.attr("src")) - val title = it.attr("title") - if (getIsMovie(href, true)) { - MovieSearchResponse( - title, href, this.name, TvType.Movie, img, null - ) - } else { - AnimeSearchResponse( - title, - href, - this.name, - TvType.Anime, - img, - null, - EnumSet.of(DubStatus.Subbed), - ) - } - } - } - - @SuppressLint("SimpleDateFormat") - private fun dateParser(dateString: String?): Date? { - if (dateString == null) return null - try { - val format = SimpleDateFormat("dd 'of' MMM',' yyyy") - val data = format.parse( - dateString.replace("th ", " ").replace("st ", " ").replace("nd ", " ") - .replace("rd ", " ") - ) ?: return null - return data - } catch (e: Exception) { - return null - } - } - -// data class TenshiSearchResponse( -// @JsonProperty("url") var url : String, -// @JsonProperty("title") var title : String, -// @JsonProperty("cover") var cover : String, -// @JsonProperty("genre") var genre : String, -// @JsonProperty("year") var year : Int, -// @JsonProperty("type") var type : String, -// @JsonProperty("eps") var eps : String, -// @JsonProperty("cen") var cen : String -// ) - -// override suspend fun quickSearch(query: String): ArrayList? { -// if (!autoLoadToken()) return quickSearch(query) -// val url = "$mainUrl/anime/search" -// val response = khttp.post( -// url, -// data=mapOf("q" to query), -// headers=mapOf("x-csrf-token" to token, "x-requested-with" to "XMLHttpRequest"), -// cookies = cookie -// -// ) -// -// val items = mapper.readValue>(response.text) -// -// if (items.isEmpty()) return ArrayList() -// -// val returnValue = ArrayList() -// for (i in items) { -// val href = fixUrl(i.url) -// val title = i.title -// val img = fixUrl(i.cover) -// val year = i.year -// -// returnValue.add( -// if (getIsMovie(i.type)) { -// MovieSearchResponse( -// title, href, getSlug(href), this.name, TvType.Movie, img, year -// ) -// } else { -// AnimeSearchResponse( -// title, href, getSlug(href), this.name, -// TvType.Anime, img, year, null, -// EnumSet.of(DubStatus.Subbed), -// null, null -// ) -// } -// ) -// } -// return returnValue -// } - - override suspend fun search(query: String): List { - val url = "$mainUrl/anime" - var document = app.get( - url, - params = mapOf("q" to query), - cookies = mapOf("loop-view" to "thumb"), - interceptor = ddosGuardKiller - ).document - - val returnValue = parseSearchPage(document).toMutableList() - - while (!document.select("""a.page-link[rel="next"]""").isEmpty()) { - val link = document.selectFirst("""a.page-link[rel="next"]""")?.attr("href") - if (!link.isNullOrBlank()) { - document = app.get( - link, - cookies = mapOf("loop-view" to "thumb"), - interceptor = ddosGuardKiller - ).document - returnValue.addAll(parseSearchPage(document)) - } else { - break - } - } - - return returnValue - } - - override suspend fun load(url: String): LoadResponse { - var document = app.get( - url, - cookies = mapOf("loop-view" to "thumb"), - interceptor = ddosGuardKiller - ).document - - val canonicalTitle = document.selectFirst("header.entry-header > h1.mb-3")!!.text().trim() - val episodeNodes = document.select("li[class*=\"episode\"] > a").toMutableList() - val totalEpisodePages = if (document.select(".pagination").size > 0) - document.select(".pagination .page-item a.page-link:not([rel])").last()!!.text() - .toIntOrNull() - else 1 - - if (totalEpisodePages != null && totalEpisodePages > 1) { - for (pageNum in 2..totalEpisodePages) { - document = app.get( - "$url?page=$pageNum", - cookies = mapOf("loop-view" to "thumb"), - interceptor = ddosGuardKiller - ).document - episodeNodes.addAll(document.select("li[class*=\"episode\"] > a")) - } - } - - val episodes = ArrayList(episodeNodes.map { - val title = it.selectFirst(".episode-title")?.text()?.trim() - newEpisode(it.attr("href")) { - this.name = if (title == "No Title") null else title - this.posterUrl = it.selectFirst("img")?.attr("src") - addDate(dateParser(it?.selectFirst(".episode-date")?.text()?.trim())) - this.description = it.attr("data-content").trim() - } - }) - - val similarAnime = document.select("ul.anime-loop > li > a").mapNotNull { element -> - val href = element.attr("href") ?: return@mapNotNull null - val title = - element.selectFirst("> .overlay > .thumb-title")?.text() ?: return@mapNotNull null - val img = element.selectFirst("> img")?.attr("src") - AnimeSearchResponse(title, href, this.name, TvType.Anime, img) - } - - val type = document.selectFirst("a[href*=\"$mainUrl/type/\"]")?.text()?.trim() - - return newAnimeLoadResponse(canonicalTitle, url, getType(type ?: "")) { - recommendations = similarAnime - posterUrl = document.selectFirst("img.cover-image")?.attr("src") - plot = document.selectFirst(".entry-description > .card-body")?.text()?.trim() - tags = - document.select("li.genre.meta-data > span.value") - .map { it?.text()?.trim().toString() } - - synonyms = - document.select("li.synonym.meta-data > div.info-box > span.value") - .map { it?.text()?.trim().toString() } - - engName = - document.selectFirst("span.value > span[title=\"English\"]")?.parent()?.text() - ?.trim() - japName = - document.selectFirst("span.value > span[title=\"Japanese\"]")?.parent()?.text() - ?.trim() - - val pattern = Regex("(\\d{4})") - val yearText = document.selectFirst("li.release-date .value")!!.text() - year = pattern.find(yearText)?.groupValues?.get(1)?.toIntOrNull() - - addEpisodes(DubStatus.Subbed, episodes) - - showStatus = when (document.selectFirst("li.status > .value")?.text()?.trim()) { - "Ongoing" -> ShowStatus.Ongoing - "Completed" -> ShowStatus.Completed - else -> null - } - } - } - - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val soup = app.get(data, interceptor = ddosGuardKiller).document - - data class Quality( - @JsonProperty("src") val src: String, - @JsonProperty("size") val size: Int - ) - - for (source in soup.select("""[aria-labelledby="mirror-dropdown"] > li > a.dropdown-item""")) { - val release = source.text().replace("/", "").trim() - val sourceHTML = app.get( - "https://tenshi.moe/embed?v=${source.attr("href").split("v=")[1].split("&")[0]}", - headers = mapOf("Referer" to data), interceptor = ddosGuardKiller - ).text - - val match = Regex("""sources: (\[(?:.|\s)+?type: ['"]video/.*?['"](?:.|\s)+?])""").find( - sourceHTML - ) - if (match != null) { - val qualities = parseJson>( - match.destructured.component1() - .replace("'", "\"") - .replace(Regex("""(\w+): """), "\"\$1\": ") - .replace(Regex("""\s+"""), "") - .replace(",}", "}") - .replace(",]", "]") - ) - qualities.forEach { - callback.invoke( - ExtractorLink( - this.name, - "${this.name} $release", - fixUrl(it.src), - this.mainUrl, - getQualityFromName("${it.size}"), - headers = getHeaders(emptyMap(), - ddosGuardKiller.savedCookiesMap[URI(this.mainUrl).host] - ?: emptyMap() - ).toMap() - ) - ) - } - } - } - - return true - } -} diff --git a/TenshiProvider/src/main/kotlin/com/lagradost/TenshiProviderPlugin.kt b/TenshiProvider/src/main/kotlin/com/lagradost/TenshiProviderPlugin.kt deleted file mode 100644 index 5961ef9..0000000 --- a/TenshiProvider/src/main/kotlin/com/lagradost/TenshiProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class TenshiProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(TenshiProvider()) - } -} \ No newline at end of file diff --git a/TheFlixToProvider/build.gradle.kts b/TheFlixToProvider/build.gradle.kts deleted file mode 100644 index defd7e1..0000000 --- a/TheFlixToProvider/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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=theflix.to&sz=24" -} \ No newline at end of file diff --git a/TheFlixToProvider/src/main/AndroidManifest.xml b/TheFlixToProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/TheFlixToProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/TheFlixToProvider/src/main/kotlin/com/lagradost/TheFlixToProvider.kt b/TheFlixToProvider/src/main/kotlin/com/lagradost/TheFlixToProvider.kt deleted file mode 100644 index 84e0876..0000000 --- a/TheFlixToProvider/src/main/kotlin/com/lagradost/TheFlixToProvider.kt +++ /dev/null @@ -1,604 +0,0 @@ -package com.lagradost - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addActors -import com.lagradost.cloudstream3.network.cookies -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.getQualityFromName - - -class TheFlixToProvider : MainAPI() { - companion object { - var latestCookies: Map = emptyMap() - } - - override var name = "TheFlix.to" - override var mainUrl = "https://theflix.to" - override val instantLinkLoading = false - override val hasMainPage = true - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - ) - - - - data class HomeJson( - @JsonProperty("props") val props: HomeProps = HomeProps(), - ) - - data class HomeProps( - @JsonProperty("pageProps") val pageProps: PageProps = PageProps(), - ) - - data class PageProps( - @JsonProperty("moviesListTrending") val moviesListTrending: MoviesListTrending = MoviesListTrending(), - @JsonProperty("moviesListNewArrivals") val moviesListNewArrivals: MoviesListNewArrivals = MoviesListNewArrivals(), - @JsonProperty("tvsListTrending") val tvsListTrending: TvsListTrending = TvsListTrending(), - @JsonProperty("tvsListNewEpisodes") val tvsListNewEpisodes: TvsListNewEpisodes = TvsListNewEpisodes(), - ) - - - data class MoviesListTrending( - @JsonProperty("docs") val docs: ArrayList = arrayListOf(), - @JsonProperty("total") val total: Int? = null, - @JsonProperty("page") val page: Int? = null, - @JsonProperty("limit") val limit: Int? = null, - @JsonProperty("pages") val pages: Int? = null, - @JsonProperty("type") val type: String? = null, - ) - - data class MoviesListNewArrivals( - @JsonProperty("docs") val docs: ArrayList = arrayListOf(), - @JsonProperty("total") val total: Int? = null, - @JsonProperty("page") val page: Int? = null, - @JsonProperty("limit") val limit: Int? = null, - @JsonProperty("pages") val pages: Int? = null, - @JsonProperty("type") val type: String? = null, - ) - - data class TvsListTrending( - @JsonProperty("docs") val docs: ArrayList = arrayListOf(), - @JsonProperty("total") val total: Int? = null, - @JsonProperty("page") val page: Int? = null, - @JsonProperty("limit") val limit: Int? = null, - @JsonProperty("pages") val pages: Int? = null, - @JsonProperty("type") val type: String? = null, - ) - - data class TvsListNewEpisodes( - @JsonProperty("docs") val docs: ArrayList = arrayListOf(), - @JsonProperty("total") val total: Int? = null, - @JsonProperty("page") val page: Int? = null, - @JsonProperty("limit") val limit: Int? = null, - @JsonProperty("pages") val pages: Int? = null, - @JsonProperty("type") val type: String? = null, - ) - - data class Docs( - @JsonProperty("name") val name: String = String(), - @JsonProperty("originalLanguage") val originalLanguage: String? = null, - @JsonProperty("popularity") val popularity: Double? = null, - @JsonProperty("runtime") val runtime: Int? = null, - @JsonProperty("status") val status: String? = null, - @JsonProperty("voteAverage") val voteAverage: Double? = null, - @JsonProperty("voteCount") val voteCount: Int? = null, - @JsonProperty("cast") val cast: String? = null, - @JsonProperty("director") val director: String? = null, - @JsonProperty("overview") val overview: String? = null, - @JsonProperty("posterUrl") val posterUrl: String? = null, - @JsonProperty("releaseDate") val releaseDate: String? = null, - @JsonProperty("createdAt") val createdAt: String? = null, - @JsonProperty("updatedAt") val updatedAt: String? = null, - @JsonProperty("conversionDate") val conversionDate: String? = null, - @JsonProperty("id") val id: Int? = null, - @JsonProperty("available") val available: Boolean? = null, - @JsonProperty("videos" ) val videos : ArrayList? = arrayListOf(), - ) - - - private suspend fun getCookies(): Map { - // val cookieResponse = app.post( - // "https://theflix.to:5679/authorization/session/continue?contentUsageType=Viewing", - // headers = mapOf( - // "Host" to "theflix.to:5679", - // "User-Agent" to "Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0", - // "Accept" to "application/json, text/plain," - // "Accept-Language" to "en-US,en;q=0.5", - // "Content-Type" to "application/json;charset=utf-8", - // "Content-Length" to "35", - // "Origin" to "https://theflix.to", - // "DNT" to "1", - // "Connection" to "keep-alive", - // "Referer" to "https://theflix.to/", - // "Sec-Fetch-Dest" to "empty", - // "Sec-Fetch-Mode" to "cors", - // "Sec-Fetch-Site" to "same-site",)).okhttpResponse.headers.values("Set-Cookie") - - val cookies = app.post( - "$mainUrl:5679/authorization/session/continue?contentUsageType=Viewing", - headers = mapOf( - "Host" to "theflix.to:5679", - "User-Agent" to USER_AGENT, - "Accept" to "application/json, text/plain, */*", - "Accept-Language" to "en-US,en;q=0.5", - "Content-Type" to "application/json;charset=utf-8", - "Content-Length" to "35", - "Origin" to mainUrl, - "DNT" to "1", - "Connection" to "keep-alive", - "Referer" to mainUrl, - "Sec-Fetch-Dest" to "empty", - "Sec-Fetch-Mode" to "cors", - "Sec-Fetch-Site" to "same-site",) - ).cookies - /* val cookieRegex = Regex("(theflix\\..*?id\\=[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)") - val findcookie = cookieRegex.findAll(cookieResponse.toString()).map { it.value }.toList() - val cookiesstring = findcookie.toString().replace(", ","; ").replace("[","").replace("]","") - val cookiesmap = mapOf("Cookie" to cookiesstring) */ - latestCookies = cookies - return latestCookies - } - - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val items = ArrayList() - val doc = app.get(mainUrl).document - val scriptText = doc.selectFirst("script[type=application/json]")!!.data() - if (scriptText.contains("moviesListTrending")) { - val json = parseJson(scriptText) - val homePageProps = json.props.pageProps - listOf( - Triple( - homePageProps.moviesListNewArrivals.docs, - homePageProps.moviesListNewArrivals.type, - "New Movie arrivals" - ), - Triple( - homePageProps.moviesListTrending.docs, - homePageProps.moviesListTrending.type, - "Trending Movies" - ), - Triple( - homePageProps.tvsListTrending.docs, - homePageProps.tvsListTrending.type, - "Trending TV Series" - ), - Triple( - homePageProps.tvsListNewEpisodes.docs, - homePageProps.tvsListNewEpisodes.type, - "New Episodes" - ) - ).map { (docs, type, homename) -> - val home = docs.map { info -> - val title = info.name - val poster = info.posterUrl - val typeinfo = - if (type?.contains("TV") == true) TvType.TvSeries else TvType.Movie - val link = - if (typeinfo == TvType.Movie) "$mainUrl/movie/${info.id}-${cleanTitle(title)}" - else "$mainUrl/tv-show/${info.id}-${cleanTitle(title).replace("?","")}/season-1/episode-1" - TvSeriesSearchResponse( - title, - link, - this.name, - typeinfo, - poster, - null, - null, - ) - } - items.add(HomePageList(homename, home)) - } - - } - - if (items.size <= 0) throw ErrorLoadingException() - return HomePageResponse(items) - } - - data class SearchJson( - @JsonProperty("props") val props: SearchProps = SearchProps(), - ) - - data class SearchProps( - @JsonProperty("pageProps") val pageProps: SearchPageProps = SearchPageProps(), - ) - - data class SearchPageProps( - @JsonProperty("mainList") val mainList: SearchMainList = SearchMainList(), - ) - - data class SearchMainList( - @JsonProperty("docs") val docs: ArrayList = arrayListOf(), - @JsonProperty("total") val total: Int? = null, - @JsonProperty("page") val page: Int? = null, - @JsonProperty("limit") val limit: Int? = null, - @JsonProperty("pages") val pages: Int? = null, - @JsonProperty("type") val type: String? = null, - ) - - - override suspend fun search(query: String): List { - val search = ArrayList() - val urls = listOf( - "$mainUrl/movies/trending?search=$query", - "$mainUrl/tv-shows/trending?search=$query" - ) - urls.apmap { url -> - val doc = app.get(url).document - val scriptText = doc.selectFirst("script[type=application/json]")!!.data() - if (scriptText.contains("pageProps")) { - val json = parseJson(scriptText) - val searchPageProps = json.props.pageProps.mainList - val pair = listOf(Pair(searchPageProps.docs, searchPageProps.type)) - pair.map { (docs, type) -> - docs.map { info -> - val title = info.name - val poster = info.posterUrl - val typeinfo = - if (type?.contains("TV") == true) TvType.TvSeries else TvType.Movie - val link = if (typeinfo == TvType.Movie) "$mainUrl/movie/${info.id}-${ - cleanTitle(title) - }" - else "$mainUrl/tv-show/${info.id}-${cleanTitle(title)}/season-1/episode-1" - if (typeinfo == TvType.Movie) { - search.add( - MovieSearchResponse( - title, - link, - this.name, - TvType.Movie, - poster, - null - ) - ) - } else { - search.add( - TvSeriesSearchResponse( - title, - link, - this.name, - TvType.TvSeries, - poster, - null, - null - ) - ) - } - } - } - } - } - return search - } - data class LoadMain ( - @JsonProperty("props" ) val props : LoadProps? = LoadProps(), - @JsonProperty("page" ) val page : String? = null, - @JsonProperty("buildId" ) val buildId : String? = null, - @JsonProperty("runtimeConfig" ) val runtimeConfig : RuntimeConfig? = RuntimeConfig(), - @JsonProperty("isFallback" ) val isFallback : Boolean? = null, - @JsonProperty("gssp" ) val gssp : Boolean? = null, - @JsonProperty("customServer" ) val customServer : Boolean? = null, - @JsonProperty("appGip" ) val appGip : Boolean? = null - ) - - data class LoadProps ( - @JsonProperty("pageProps" ) val pageProps : LoadPageProps? = LoadPageProps(), - @JsonProperty("__N_SSP" ) val _NSSP : Boolean? = null - ) - - data class LoadPageProps ( - @JsonProperty("selectedTv" ) val selectedTv : TheFlixMetadata? = TheFlixMetadata(), - @JsonProperty("movie") val movie: TheFlixMetadata? = TheFlixMetadata(), - @JsonProperty("recommendationsList" ) val recommendationsList : RecommendationsList? = RecommendationsList(), - @JsonProperty("basePageSegments" ) val basePageSegments : ArrayList? = arrayListOf() - ) - - data class TheFlixMetadata ( - @JsonProperty("episodeRuntime" ) val episodeRuntime : Int? = null, - @JsonProperty("name" ) val name : String? = null, - @JsonProperty("numberOfSeasons" ) val numberOfSeasons : Int? = null, - @JsonProperty("numberOfEpisodes" ) val numberOfEpisodes : Int? = null, - @JsonProperty("originalLanguage" ) val originalLanguage : String? = null, - @JsonProperty("popularity" ) val popularity : Double? = null, - @JsonProperty("status" ) val status : String? = null, - @JsonProperty("voteAverage" ) val voteAverage : Double? = null, - @JsonProperty("voteCount" ) val voteCount : Int? = null, - @JsonProperty("cast" ) val cast : String? = null, - @JsonProperty("director" ) val director : String? = null, - @JsonProperty("overview" ) val overview : String? = null, - @JsonProperty("posterUrl" ) val posterUrl : String? = null, - @JsonProperty("releaseDate" ) val releaseDate : String? = null, - @JsonProperty("createdAt" ) val createdAt : String? = null, - @JsonProperty("updatedAt" ) val updatedAt : String? = null, - @JsonProperty("id" ) val id : Int? = null, - @JsonProperty("available" ) val available : Boolean? = null, - @JsonProperty("genres" ) val genres : ArrayList? = arrayListOf(), - @JsonProperty("seasons" ) val seasons : ArrayList? = arrayListOf(), - @JsonProperty("videos" ) val videos : ArrayList? = arrayListOf(), - @JsonProperty("runtime" ) val runtime : Int? = null, - ) - data class Seasons( - @JsonProperty("name") val name: String? = null, - @JsonProperty("numberOfEpisodes") val numberOfEpisodes: Int? = null, - @JsonProperty("seasonNumber") val seasonNumber: Int? = null, - @JsonProperty("overview") val overview: String? = null, - @JsonProperty("posterUrl") val posterUrl: String? = null, - @JsonProperty("releaseDate") val releaseDate: String? = null, - @JsonProperty("createdAt") val createdAt: String? = null, - @JsonProperty("updatedAt") val updatedAt: String? = null, - @JsonProperty("id") val id: Int? = null, - @JsonProperty("episodes") val episodes: ArrayList? = arrayListOf() - ) - - data class Episodes( - @JsonProperty("episodeNumber") val episodeNumber: Int? = null, - @JsonProperty("name") val name: String? = null, - @JsonProperty("seasonNumber") val seasonNumber: Int? = null, - @JsonProperty("voteAverage") val voteAverage: Double? = null, - @JsonProperty("voteCount") val voteCount: Int? = null, - @JsonProperty("overview") val overview: String? = null, - @JsonProperty("releaseDate") val releaseDate: String? = null, - @JsonProperty("createdAt") val createdAt: String? = null, - @JsonProperty("updatedAt") val updatedAt: String? = null, - @JsonProperty("id") val id: Int? = null, - @JsonProperty("videos") val videos: ArrayList? = arrayListOf() - ) - - - data class Genres ( - @JsonProperty("name" ) val name : String? = null, - @JsonProperty("id" ) val id : Int? = null - ) - - data class RuntimeConfig ( - @JsonProperty("AddThisService" ) val AddThisService : RuntimeConfigData? = RuntimeConfigData(), - @JsonProperty("Application" ) val Application : RuntimeConfigData? = RuntimeConfigData(), - @JsonProperty("GtmService" ) val GtmService : RuntimeConfigData? = RuntimeConfigData(), - @JsonProperty("Services" ) val Services : RuntimeConfigData? = RuntimeConfigData(), - ) - - data class RuntimeConfigData( - @JsonProperty("PublicId" ) val PublicId : String? = null, - @JsonProperty("ContentUsageType" ) val ContentUsageType : String? = null, - @JsonProperty("IsDevelopmentMode" ) val IsDevelopmentMode : Boolean? = null, - @JsonProperty("IsDevelopmentOrProductionMode" ) val IsDevelopmentOrProductionMode : Boolean? = null, - @JsonProperty("IsProductionMode" ) val IsProductionMode : Boolean? = null, - @JsonProperty("IsStagingMode" ) val IsStagingMode : Boolean? = null, - @JsonProperty("IsTestMode" ) val IsTestMode : Boolean? = null, - @JsonProperty("Mode" ) val Mode : String? = null, - @JsonProperty("Name" ) val Name : String? = null, - @JsonProperty("Url" ) val Url : String? = null, - @JsonProperty("UseFilterInfoInUrl" ) val UseFilterInfoInUrl : Boolean? = null, - @JsonProperty("TrackingId" ) val TrackingId : String? = null, - @JsonProperty("Server" ) val Server : Server? = Server(), - @JsonProperty("TmdbServer" ) val TmdbServer : TmdbServer? = TmdbServer(), - ) - - data class TmdbServer ( - @JsonProperty("Url" ) val Url : String? = null - ) - - - data class Server ( - @JsonProperty("Url" ) val Url : String? = null - ) - - data class RecommendationsList ( - @JsonProperty("docs" ) val docs : ArrayList = arrayListOf(), - @JsonProperty("total" ) val total : Int? = null, - @JsonProperty("page" ) val page : Int? = null, - @JsonProperty("limit" ) val limit : Int? = null, - @JsonProperty("pages" ) val pages : Int? = null, - @JsonProperty("type" ) val type : String? = null, - ) - - private fun cleanTitle(title: String): String { - val dotTitle = title.substringBefore("/season") - if (dotTitle.contains(Regex("\\..\\."))) { //For titles containing more than two dots (S.W.A.T.) - return (dotTitle.removeSuffix(".") - .replace(" - ", "-") - .replace(".", "-").replace(" ", "-") - .replace("-&", "") - .replace(Regex("(:|-&)"), "") - .replace("'", "-")).lowercase() - } - return (title - .replace(" - ", "-") - .replace(" ", "-") - .replace("-&", "") - .replace("/", "-") - .replace(Regex("(:|-&|\\.)"), "") - .replace("'", "-")).lowercase() - } - - private suspend fun getLoadMan(url: String): LoadMain { - getCookies() - val og = app.get(url, headers = latestCookies) - val soup = og.document - val script = soup.selectFirst("script[type=application/json]")!!.data() - return parseJson(script) - } - - override suspend fun load(url: String): LoadResponse? { - val tvtype = if (url.contains("movie")) TvType.Movie else TvType.TvSeries - val json = getLoadMan(url) - val episodes = ArrayList() - val isMovie = tvtype == TvType.Movie - val pageMain = json.props?.pageProps - - val metadata: TheFlixMetadata? = if (isMovie) pageMain?.movie else pageMain?.selectedTv - - val available = metadata?.available - - val comingsoon = !available!! - - val movieId = metadata.id - - val movietitle = metadata.name - - val poster = metadata.posterUrl - - val description = metadata.overview - - if (!isMovie) { - metadata.seasons?.map { seasons -> - val seasonPoster = seasons.posterUrl ?: metadata.posterUrl - seasons.episodes?.forEach { epi -> - val episodenu = epi.episodeNumber - val seasonum = epi.seasonNumber - val title = epi.name - val epDesc = epi.overview - val test = epi.videos - val ratinginfo = (epi.voteAverage)?.times(10)?.toInt() - val rating = if (ratinginfo?.equals(0) == true) null else ratinginfo - val eps = Episode( - "$mainUrl/tv-show/$movieId-${cleanTitle(movietitle!!)}/season-$seasonum/episode-$episodenu", - title, - seasonum, - episodenu, - description = epDesc!!, - posterUrl = seasonPoster, - rating = rating, - ) - if (test!!.isNotEmpty()) { - episodes.add(eps) - } else { - //Nothing, will prevent seasons/episodes with no videos to be added - } - } - } - } - val rating = metadata.voteAverage?.toFloat()?.times(1000)?.toInt() - - val tags = metadata.genres?.mapNotNull { it.name } - - val recommendationsitem = pageMain?.recommendationsList?.docs?.map { loadDocs -> - val title = loadDocs.name - val posterrec = loadDocs.posterUrl - val link = if (isMovie) "$mainUrl/movie/${loadDocs.id}-${cleanTitle(title)}" - else "$mainUrl/tv-show/${loadDocs.id}-${cleanTitle(title)}/season-1/episode-1" - MovieSearchResponse( - title, - link, - this.name, - tvtype, - posterrec, - year = null - ) - } - - val year = metadata.releaseDate?.substringBefore("-") - - val runtime = metadata.runtime?.div(60) ?: metadata.episodeRuntime?.div(60) - val cast = metadata.cast?.split(",") - - return when (tvtype) { - TvType.TvSeries -> { - return newTvSeriesLoadResponse(movietitle!!, url, TvType.TvSeries, episodes) { - this.posterUrl = poster - this.year = year?.toIntOrNull() - this.plot = description - this.duration = runtime - addActors(cast) - this.tags = tags - this.recommendations = recommendationsitem - this.comingSoon = comingsoon - this.rating = rating - } - } - TvType.Movie -> { - newMovieLoadResponse(movietitle!!, url, TvType.Movie, url) { - this.year = year?.toIntOrNull() - this.posterUrl = poster - this.plot = description - this.duration = runtime - addActors(cast) - this.tags = tags - this.recommendations = recommendationsitem - this.comingSoon = comingsoon - this.rating = rating - } - } - else -> null - } - } - - - data class VideoData ( - @JsonProperty("url" ) val url : String? = null, - @JsonProperty("id" ) val id : String? = null, - @JsonProperty("type" ) val type : String? = null, - ) - - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val json = getLoadMan(data) - val authhost = json.runtimeConfig?.Services?.Server?.Url - val isMovie = data.contains("/movie/") - val qualityReg = Regex("(\\d+p)") - if (isMovie){ - json.props?.pageProps?.movie?.videos?.apmap { id -> - val jsonmovie = app.get("$authhost/movies/videos/$id/request-access?contentUsageType=Viewing", - headers = latestCookies).parsedSafe() ?: return@apmap false - val extractedlink = jsonmovie.url - if (!extractedlink.isNullOrEmpty()) { - val quality = qualityReg.find(extractedlink)?.value ?: "" - callback( - ExtractorLink( - name, - name, - extractedlink, - "", - getQualityFromName(quality), - false - ) - ) - } else null - } - } - else - { - val dataRegex = Regex("(season-(\\d+)\\/episode-(\\d+))") - val cleandatainfo = dataRegex.find(data)?.value?.replace(Regex("(season-|episode-)"),"")?.replace("/","x") - val tesatt = cleandatainfo.let { str -> - str?.split("x")?.mapNotNull { subStr -> subStr.toIntOrNull() } - } - val epID = tesatt?.getOrNull(1) - val seasonid = tesatt?.getOrNull(0) - json.props?.pageProps?.selectedTv?.seasons?.map { - it.episodes?.map { - val epsInfo = Triple(it.seasonNumber, it.episodeNumber, it.videos) - if (epsInfo.first == seasonid && epsInfo.second == epID) { - epsInfo.third?.apmap { id -> - val jsonserie = app.get("$authhost/tv/videos/$id/request-access?contentUsageType=Viewing", headers = latestCookies).parsedSafe() ?: return@apmap false - val extractedlink = jsonserie.url - if (!extractedlink.isNullOrEmpty()) { - val quality = qualityReg.find(extractedlink)?.value ?: "" - callback( - ExtractorLink( - name, - name, - extractedlink, - "", - getQualityFromName(quality), - false - ) - ) - } else null - } - } - } - } - } - return true - } -} diff --git a/TheFlixToProvider/src/main/kotlin/com/lagradost/TheFlixToProviderPlugin.kt b/TheFlixToProvider/src/main/kotlin/com/lagradost/TheFlixToProviderPlugin.kt deleted file mode 100644 index 6eb5cfa..0000000 --- a/TheFlixToProvider/src/main/kotlin/com/lagradost/TheFlixToProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class TheFlixToProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(TheFlixToProvider()) - } -} \ No newline at end of file diff --git a/TrailersTwoProvider/build.gradle.kts b/TrailersTwoProvider/build.gradle.kts deleted file mode 100644 index 8595414..0000000 --- a/TrailersTwoProvider/build.gradle.kts +++ /dev/null @@ -1,29 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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( - "Cartoon", - "Anime", - "Movie", - "AnimeMovie", - "TvSeries", - ) - - - iconUrl = "https://www.google.com/s2/favicons?domain=trailers.to&sz=24" -} \ No newline at end of file diff --git a/TrailersTwoProvider/src/main/AndroidManifest.xml b/TrailersTwoProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/TrailersTwoProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/TrailersTwoProvider/src/main/kotlin/com/lagradost/TrailersTwoProvider.kt b/TrailersTwoProvider/src/main/kotlin/com/lagradost/TrailersTwoProvider.kt deleted file mode 100644 index d7d5212..0000000 --- a/TrailersTwoProvider/src/main/kotlin/com/lagradost/TrailersTwoProvider.kt +++ /dev/null @@ -1,319 +0,0 @@ -package com.lagradost - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.metaproviders.TmdbLink -import com.lagradost.cloudstream3.metaproviders.TmdbProvider -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.cloudstream3.utils.SubtitleHelper - -class TrailersTwoProvider : TmdbProvider() { - val user = "cloudstream" - override val apiName = "Trailers.to" - override var name = "Trailers.to" - override var mainUrl = "https://trailers.to" - override val useMetaLoadResponse = true - override val instantLinkLoading = true - - data class TrailersEpisode( - // val tvShowItemID: Long?, - //val tvShow: String, - //val tvShowIMDB: String?, - //val tvShowTMDB: Long?, - @JsonProperty("ItemID") - val itemID: Int, - //val title: String, - //@JsonProperty("IMDb") - @JsonProperty("IMDb") - val imdb: String?, - //@JsonProperty("TMDb") - @JsonProperty("TMDb") - val tmdb: Int?, - //val releaseDate: String, - //val entryDate: String - ) - - data class TrailersMovie( - @JsonProperty("ItemID") - val itemID: Int, - @JsonProperty("IMDb") - val imdb: String?, - @JsonProperty("TMDb") - val tmdb: Int?, - //@JsonProperty("Title") - //val title: String?, - ) - - /*companion object { - private var tmdbToIdMovies: HashMap = hashMapOf() - private var imdbToIdMovies: HashMap = hashMapOf() - private var tmdbToIdTvSeries: HashMap = hashMapOf() - private var imdbToIdTvSeries: HashMap = hashMapOf() - - private const val startDate = 1900 - private const val endDate = 9999 - - fun getEpisode(tmdb: Int?, imdb: String?): Int? { - var currentId: Int? = null - if (tmdb != null) { - currentId = tmdbToIdTvSeries[tmdb] - } - if (imdb != null && currentId == null) { - currentId = imdbToIdTvSeries[imdb] - } - return currentId - } - - fun getMovie(tmdb: Int?, imdb: String?): Int? { - var currentId: Int? = null - if (tmdb != null) { - currentId = tmdbToIdMovies[tmdb] - } - if (imdb != null && currentId == null) { - currentId = imdbToIdMovies[imdb] - } - return currentId - } - - suspend fun fillData(isMovie: Boolean) { - if (isMovie) { - if (tmdbToIdMovies.isNotEmpty() || imdbToIdMovies.isNotEmpty()) { - return - } - parseJson>( - app.get( - "https://trailers.to/movies?from=$startDate-01-01&to=$endDate", - timeout = 30 - ).text - ).forEach { movie -> - movie.imdb?.let { - imdbToIdTvSeries[it] = movie.itemID - } - movie.tmdb?.let { - tmdbToIdTvSeries[it] = movie.itemID - } - } - } else { - if (tmdbToIdTvSeries.isNotEmpty() || imdbToIdTvSeries.isNotEmpty()) { - return - } - parseJson>( - app.get( - "https://trailers.to/episodes?from=$startDate-01-01&to=$endDate", - timeout = 30 - ).text - ).forEach { episode -> - episode.imdb?.let { - imdbToIdTvSeries[it] = episode.itemID - } - episode.tmdb?.let { - tmdbToIdTvSeries[it] = episode.itemID - } - } - } - } - }*/ - - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - //TvType.AnimeMovie, - //TvType.Anime, - //TvType.Cartoon - ) - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val mappedData = parseJson(data) - val (id, site) = if (mappedData.imdbID != null) listOf( - mappedData.imdbID, - "imdb" - ) else listOf(mappedData.tmdbID.toString(), "tmdb") - - val isMovie = mappedData.episode == null && mappedData.season == null - val (videoUrl, subtitleUrl) = if (isMovie) { - val suffix = "$user/$site/$id" - Pair( - "https://trailers.to/video/$suffix", - "https://trailers.to/subtitles/$suffix" - ) - } else { - val suffix = "$user/$site/$id/S${mappedData.season ?: 1}E${mappedData.episode ?: 1}" - Pair( - "https://trailers.to/video/$suffix", - "https://trailers.to/subtitles/$suffix" - ) - } - - callback.invoke( - ExtractorLink( - this.name, - this.name, - videoUrl, - "https://trailers.to", - Qualities.Unknown.value, - false, - ) - ) - - argamap( - { - val subtitles = - app.get(subtitleUrl).text - val subtitlesMapped = parseJson>(subtitles) - subtitlesMapped.forEach { - subtitleCallback.invoke( - SubtitleFile( - SubtitleHelper.fromTwoLettersToLanguage(it.LanguageCode ?: "en") - ?: "English", - "https://trailers.to/subtitles/${it.ContentHash ?: return@forEach}/${it.LanguageCode ?: return@forEach}.vtt" // ${it.MetaInfo?.SubFormat ?: "srt"}" - ) - ) - } - }, { - //https://trailers.to/en/quick-search?q=iron man - val name = mappedData.movieName - if (name != null && isMovie) { - app.get("https://trailers.to/en/quick-search?q=${name}").document.select("a.post-minimal") - .mapNotNull { - it?.attr("href") - }.map { Regex("""/movie/(\d+)/""").find(it)?.groupValues?.getOrNull(1) } - .firstOrNull()?.let { movieId -> - val correctUrl = app.get(videoUrl).url - callback.invoke( - ExtractorLink( - this.name, - "${this.name} Backup", - correctUrl.replace("/$user/0/", "/$user/$movieId/"), - "https://trailers.to", - Qualities.Unknown.value, - false, - ) - ) - } - } - } - ) - - /* - // the problem with this code is that it tages ages and the json file is 50mb or so for movies - fillData(isMovie) - val movieId = if (isMovie) { - getMovie(mappedData.tmdbID, mappedData.imdbID) - } else { - getEpisode(mappedData.tmdbID, mappedData.imdbID) - } ?: return@argamap - val request = app.get(data) - val endUrl = request.url - callback.invoke( - ExtractorLink( - this.name, - "${this.name} Backup", - endUrl.replace("/cloudstream/0/", "/cloudstream/$movieId/"), - "https://trailers.to", - Qualities.Unknown.value, - false, - ) - ) - */ - - return true - } -} - -// Auto generated -data class TrailersSubtitleFile( - @JsonProperty("SubtitleID") val SubtitleID: Int?, - @JsonProperty("ItemID") val ItemID: Int?, - @JsonProperty("ContentText") val ContentText: String?, - @JsonProperty("ContentHash") val ContentHash: String?, - @JsonProperty("LanguageCode") val LanguageCode: String?, - @JsonProperty("MetaInfo") val MetaInfo: MetaInfo?, - @JsonProperty("EntryDate") val EntryDate: String?, - @JsonProperty("ItemSubtitleAdaptations") val ItemSubtitleAdaptations: List?, - @JsonProperty("ReleaseNames") val ReleaseNames: List?, - @JsonProperty("SubFileNames") val SubFileNames: List?, - @JsonProperty("Framerates") val Framerates: List?, - @JsonProperty("IsRelevant") val IsRelevant: Boolean? -) - -data class QueryParameters( - @JsonProperty("imdbid") val imdbid: String? -) - -data class MetaInfo( - @JsonProperty("MatchedBy") val MatchedBy: String?, - @JsonProperty("IDSubMovieFile") val IDSubMovieFile: String?, - @JsonProperty("MovieHash") val MovieHash: String?, - @JsonProperty("MovieByteSize") val MovieByteSize: String?, - @JsonProperty("MovieTimeMS") val MovieTimeMS: String?, - @JsonProperty("IDSubtitleFile") val IDSubtitleFile: String?, - @JsonProperty("SubFileName") val SubFileName: String?, - @JsonProperty("SubActualCD") val SubActualCD: String?, - @JsonProperty("SubSize") val SubSize: String?, - @JsonProperty("SubHash") val SubHash: String?, - @JsonProperty("SubLastTS") val SubLastTS: String?, - @JsonProperty("SubTSGroup") val SubTSGroup: String?, - @JsonProperty("InfoReleaseGroup") val InfoReleaseGroup: String?, - @JsonProperty("InfoFormat") val InfoFormat: String?, - @JsonProperty("InfoOther") val InfoOther: String?, - @JsonProperty("IDSubtitle") val IDSubtitle: String?, - @JsonProperty("UserID") val UserID: String?, - @JsonProperty("SubLanguageID") val SubLanguageID: String?, - @JsonProperty("SubFormat") val SubFormat: String?, - @JsonProperty("SubSumCD") val SubSumCD: String?, - @JsonProperty("SubAuthorComment") val SubAuthorComment: String?, - @JsonProperty("SubAddDate") val SubAddDate: String?, - @JsonProperty("SubBad") val SubBad: String?, - @JsonProperty("SubRating") val SubRating: String?, - @JsonProperty("SubSumVotes") val SubSumVotes: String?, - @JsonProperty("SubDownloadsCnt") val SubDownloadsCnt: String?, - @JsonProperty("MovieReleaseName") val MovieReleaseName: String?, - @JsonProperty("MovieFPS") val MovieFPS: String?, - @JsonProperty("IDMovie") val IDMovie: String?, - @JsonProperty("IDMovieImdb") val IDMovieImdb: String?, - @JsonProperty("MovieName") val MovieName: String?, - @JsonProperty("MovieNameEng") val MovieNameEng: String?, - @JsonProperty("MovieYear") val MovieYear: String?, - @JsonProperty("MovieImdbRating") val MovieImdbRating: String?, - @JsonProperty("SubFeatured") val SubFeatured: String?, - @JsonProperty("UserNickName") val UserNickName: String?, - @JsonProperty("SubTranslator") val SubTranslator: String?, - @JsonProperty("ISO639") val ISO639: String?, - @JsonProperty("LanguageName") val LanguageName: String?, - @JsonProperty("SubComments") val SubComments: String?, - @JsonProperty("SubHearingImpaired") val SubHearingImpaired: String?, - @JsonProperty("UserRank") val UserRank: String?, - @JsonProperty("SeriesSeason") val SeriesSeason: String?, - @JsonProperty("SeriesEpisode") val SeriesEpisode: String?, - @JsonProperty("MovieKind") val MovieKind: String?, - @JsonProperty("SubHD") val SubHD: String?, - @JsonProperty("SeriesIMDBParent") val SeriesIMDBParent: String?, - @JsonProperty("SubEncoding") val SubEncoding: String?, - @JsonProperty("SubAutoTranslation") val SubAutoTranslation: String?, - @JsonProperty("SubForeignPartsOnly") val SubForeignPartsOnly: String?, - @JsonProperty("SubFromTrusted") val SubFromTrusted: String?, - @JsonProperty("QueryCached") val QueryCached: Int?, - @JsonProperty("SubTSGroupHash") val SubTSGroupHash: String?, - @JsonProperty("SubDownloadLink") val SubDownloadLink: String?, - @JsonProperty("ZipDownloadLink") val ZipDownloadLink: String?, - @JsonProperty("SubtitlesLink") val SubtitlesLink: String?, - @JsonProperty("QueryNumber") val QueryNumber: String?, - @JsonProperty("QueryParameters") val QueryParameters: QueryParameters?, - @JsonProperty("Score") val Score: Double? -) - -data class ItemSubtitleAdaptations( - @JsonProperty("ContentHash") val ContentHash: String?, - @JsonProperty("OffsetMs") val OffsetMs: Int?, - @JsonProperty("Framerate") val Framerate: Int?, - @JsonProperty("Views") val Views: Int?, - @JsonProperty("EntryDate") val EntryDate: String?, - @JsonProperty("Subtitle") val Subtitle: String? -) \ No newline at end of file diff --git a/TrailersTwoProvider/src/main/kotlin/com/lagradost/TrailersTwoProviderPlugin.kt b/TrailersTwoProvider/src/main/kotlin/com/lagradost/TrailersTwoProviderPlugin.kt deleted file mode 100644 index 459713d..0000000 --- a/TrailersTwoProvider/src/main/kotlin/com/lagradost/TrailersTwoProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class TrailersTwoProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(TrailersTwoProvider()) - } -} \ No newline at end of file diff --git a/VMoveeProvider/build.gradle.kts b/VMoveeProvider/build.gradle.kts deleted file mode 100644 index b4e9d11..0000000 --- a/VMoveeProvider/build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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=www.vmovee.watch&sz=24" -} \ No newline at end of file diff --git a/VMoveeProvider/src/main/AndroidManifest.xml b/VMoveeProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/VMoveeProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/VMoveeProvider/src/main/kotlin/com/lagradost/VMoveeProvider.kt b/VMoveeProvider/src/main/kotlin/com/lagradost/VMoveeProvider.kt deleted file mode 100644 index 57f1e7a..0000000 --- a/VMoveeProvider/src/main/kotlin/com/lagradost/VMoveeProvider.kt +++ /dev/null @@ -1,125 +0,0 @@ -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.getQualityFromName -import org.jsoup.Jsoup - -class VMoveeProvider : MainAPI() { - override var name = "VMovee" - override var mainUrl = "https://www.vmovee.watch" - - override val supportedTypes = setOf(TvType.Movie) - - override suspend fun search(query: String): List { - val url = "$mainUrl/?s=$query" - val response = app.get(url).text - val document = Jsoup.parse(response) - val searchItems = document.select("div.search-page > div.result-item > article") - if (searchItems.size == 0) return ArrayList() - val returnValue = ArrayList() - for (item in searchItems) { - val details = item.selectFirst("> div.details") - val imgHolder = item.selectFirst("> div.image > div.thumbnail > a") - // val href = imgHolder.attr("href") - val poster = imgHolder!!.selectFirst("> img")!!.attr("data-lazy-src") - val isTV = imgHolder.selectFirst("> span")!!.text() == "TV" - if (isTV) continue // no TV support yet - - val titleHolder = details!!.selectFirst("> div.title > a") - val title = titleHolder!!.text() - val href = titleHolder.attr("href") - val meta = details.selectFirst("> div.meta") - val year = meta!!.selectFirst("> span.year")!!.text().toIntOrNull() - // val rating = parseRating(meta.selectFirst("> span.rating").text().replace("IMDb ", "")) - // val descript = details.selectFirst("> div.contenido").text() - returnValue.add( - if (isTV) TvSeriesSearchResponse(title, href, this.name, TvType.TvSeries, poster, year, null) - else MovieSearchResponse(title, href, this.name, TvType.Movie, poster, year) - ) - } - return returnValue - } - - data class LoadLinksAjax( - @JsonProperty("embed_url") - val embedUrl: String, - ) - - data class ReeoovAPIData( - @JsonProperty("file") - val file: String, - @JsonProperty("label") - val label: String, - ) - - data class ReeoovAPI( - @JsonProperty("data") - val data: List, - ) - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val url = "$mainUrl/dashboard/admin-ajax.php" - val post = - app.post( - url, - headers = mapOf("referer" to url), - data = mapOf("action" to "doo_player_ajax", "post" to data, "nume" to "2", "type" to "movie") - ).text - - val ajax = parseJson(post) - var realUrl = ajax.embedUrl - if (realUrl.startsWith("//")) { - realUrl = "https:$realUrl" - } - - val request = app.get(realUrl) - val prefix = "https://reeoov.tube/v/" - if (request.url.startsWith(prefix)) { - val apiUrl = "https://reeoov.tube/api/source/${request.url.removePrefix(prefix)}" - val apiResponse = app.post( - apiUrl, - headers = mapOf("Referer" to request.url), - data = mapOf("r" to "https://www.vmovee.watch/", "d" to "reeoov.tube") - ).text - val apiData = parseJson(apiResponse) - for (d in apiData.data) { - callback.invoke( - ExtractorLink( - this.name, - this.name + " " + d.label, - d.file, - "https://reeoov.tube/", - getQualityFromName(d.label), - false - ) - ) - } - } - - return true - } - - override suspend fun load(url: String): LoadResponse { - val response = app.get(url).text - val document = Jsoup.parse(response) - - val sheader = document.selectFirst("div.sheader") - - val poster = sheader!!.selectFirst("> div.poster > img")!!.attr("data-lazy-src") - val data = sheader.selectFirst("> div.data") - val title = data!!.selectFirst("> h1")!!.text() - val descript = document.selectFirst("div#info > div")!!.text() - val id = document.select("div.starstruck").attr("data-id") - - return MovieLoadResponse(title, url, this.name, TvType.Movie, id, poster, null, descript, null, null) - } -} \ No newline at end of file diff --git a/VMoveeProvider/src/main/kotlin/com/lagradost/VMoveeProviderPlugin.kt b/VMoveeProvider/src/main/kotlin/com/lagradost/VMoveeProviderPlugin.kt deleted file mode 100644 index 3e60908..0000000 --- a/VMoveeProvider/src/main/kotlin/com/lagradost/VMoveeProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class VMoveeProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(VMoveeProvider()) - } -} \ No newline at end of file diff --git a/VidSrcProvider/build.gradle.kts b/VidSrcProvider/build.gradle.kts deleted file mode 100644 index 7fdd3a5..0000000 --- a/VidSrcProvider/build.gradle.kts +++ /dev/null @@ -1,24 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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", - ) - - } \ No newline at end of file diff --git a/VidSrcProvider/src/main/AndroidManifest.xml b/VidSrcProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/VidSrcProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/VidSrcProvider/src/main/kotlin/com/lagradost/VidSrcProvider.kt b/VidSrcProvider/src/main/kotlin/com/lagradost/VidSrcProvider.kt deleted file mode 100644 index 012cae9..0000000 --- a/VidSrcProvider/src/main/kotlin/com/lagradost/VidSrcProvider.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.lagradost - -import com.lagradost.cloudstream3.SubtitleFile -import com.lagradost.cloudstream3.TvType -//import com.lagradost.cloudstream3.extractors.VidSrcExtractor -import com.lagradost.cloudstream3.metaproviders.TmdbLink -import com.lagradost.cloudstream3.metaproviders.TmdbProvider -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor - -class VidSrcProvider : TmdbProvider() { - override val apiName = "VidSrc" - override var name = "VidSrc" - override var mainUrl = "https://v2.vidsrc.me" - override val useMetaLoadResponse = true - override val instantLinkLoading = false - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - ) - -// companion object { -// val extractor = VidSrcExtractor() -// } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val mappedData = parseJson(data) - val (id, site) = if (mappedData.imdbID != null) listOf( - mappedData.imdbID, - "imdb" - ) else listOf(mappedData.tmdbID.toString(), "tmdb") - val isMovie = mappedData.episode == null && mappedData.season == null - val embedUrl = if (isMovie) { - if (site == "imdb") "$mainUrl/embed/$id" else - "$mainUrl/embed/$id" - } else { - val suffix = "$id/${mappedData.season ?: 1}-${mappedData.episode ?: 1}" - if (site == "imdb") "$mainUrl/embed/$suffix" else - "$mainUrl/embed/$suffix" - } - - loadExtractor(embedUrl, null, subtitleCallback, callback) -// extractor.getSafeUrl(embedUrl, null, subtitleCallback, callback) - - return true - } -} \ No newline at end of file diff --git a/VidSrcProvider/src/main/kotlin/com/lagradost/VidSrcProviderPlugin.kt b/VidSrcProvider/src/main/kotlin/com/lagradost/VidSrcProviderPlugin.kt deleted file mode 100644 index 6dd1352..0000000 --- a/VidSrcProvider/src/main/kotlin/com/lagradost/VidSrcProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class VidSrcProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(VidSrcProvider()) - } -} \ No newline at end of file diff --git a/VidstreamBundle/build.gradle.kts b/VidstreamBundle/build.gradle.kts deleted file mode 100644 index a1bef16..0000000 --- a/VidstreamBundle/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // All of these properties are optional, you can safely remove them - - description = "Includes many providers with the same layout as Vidstream" - // 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", - "Movie", - "AnimeMovie", - "TvSeries", - ) - - - } \ No newline at end of file diff --git a/VidstreamBundle/src/main/AndroidManifest.xml b/VidstreamBundle/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/VidstreamBundle/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/VidstreamBundle/src/main/kotlin/com/lagradost/AsianEmbedHelper.kt b/VidstreamBundle/src/main/kotlin/com/lagradost/AsianEmbedHelper.kt deleted file mode 100644 index f6b71a1..0000000 --- a/VidstreamBundle/src/main/kotlin/com/lagradost/AsianEmbedHelper.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.lagradost - -import android.util.Log -import com.lagradost.cloudstream3.SubtitleFile -import com.lagradost.cloudstream3.apmap -import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor - -class AsianEmbedHelper { - companion object { - suspend fun getUrls( - url: String, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ) { - // Fetch links - val doc = app.get(url).document - val links = doc.select("div#list-server-more > ul > li.linkserver") - if (!links.isNullOrEmpty()) { - links.apmap { - val datavid = it.attr("data-video") ?: "" - //Log.i("AsianEmbed", "Result => (datavid) ${datavid}") - if (datavid.isNotBlank()) { - val res = loadExtractor(datavid, url, subtitleCallback, callback) - Log.i("AsianEmbed", "Result => ($res) (datavid) $datavid") - } - } - } - } - } -} \ No newline at end of file diff --git a/VidstreamBundle/src/main/kotlin/com/lagradost/AsianLoadProvider.kt b/VidstreamBundle/src/main/kotlin/com/lagradost/AsianLoadProvider.kt deleted file mode 100644 index 58bafdf..0000000 --- a/VidstreamBundle/src/main/kotlin/com/lagradost/AsianLoadProvider.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.lagradost - -import com.lagradost.cloudstream3.TvType - -/** Needs to inherit from MainAPI() to - * make the app know what functions to call - */ -class AsianLoadProvider : VidstreamProviderTemplate() { - override var name = "AsianLoad" - override var mainUrl = "https://asianembed.io" - override val homePageUrlList = listOf( - mainUrl, - "$mainUrl/recently-added-raw", - "$mainUrl/movies", - "$mainUrl/kshow", - "$mainUrl/popular", - "$mainUrl/ongoing-series" - ) - - override val iv = "9262859232435825" - override val secretKey = "93422192433952489752342908585752" - override val secretDecryptKey = secretKey - - override val supportedTypes = setOf(TvType.AsianDrama) -} diff --git a/VidstreamBundle/src/main/kotlin/com/lagradost/DramaSeeProvider.kt b/VidstreamBundle/src/main/kotlin/com/lagradost/DramaSeeProvider.kt deleted file mode 100644 index ae31a24..0000000 --- a/VidstreamBundle/src/main/kotlin/com/lagradost/DramaSeeProvider.kt +++ /dev/null @@ -1,218 +0,0 @@ -package com.lagradost - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.extractors.Vidstream -//import com.lagradost.cloudstream3.animeproviders.GogoanimeProvider.Companion.extractVidstream -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor - -class DramaSeeProvider : MainAPI() { - override var mainUrl = "https://dramasee.net" - override var name = "DramaSee" - override val hasQuickSearch = false - override val hasMainPage = true - override val hasChromecastSupport = false - override val hasDownloadSupport = true - override val supportedTypes = setOf(TvType.AsianDrama) - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val headers = mapOf("X-Requested-By" to mainUrl) - val document = app.get(mainUrl, headers = headers).document - val mainbody = document.getElementsByTag("body") - - return HomePageResponse( - mainbody.select("section.block_area.block_area_home")?.map { main -> - val title = main.select("h2.cat-heading").text() ?: "Main" - val inner = main.select("div.flw-item") ?: return@map null - - HomePageList( - title, - inner.mapNotNull { - val innerBody = it?.selectFirst("a") - // Fetch details - val link = fixUrlNull(innerBody?.attr("href")) ?: return@mapNotNull null - val image = fixUrlNull(it.select("img").attr("data-src")) ?: "" - val name = innerBody?.attr("title") ?: "" - //Log.i(this.name, "Result => (innerBody, image) ${innerBody} / ${image}") - MovieSearchResponse( - name, - link, - this.name, - TvType.AsianDrama, - image, - year = null, - id = null, - ) - }.distinctBy { c -> c.url }) - }?.filterNotNull() ?: listOf() - ) - } - - override suspend fun search(query: String): List { - val url = "$mainUrl/search?q=$query" - val document = app.get(url).document - val posters = document.select("div.film-poster") - - - return posters.mapNotNull { - val innerA = it.select("a") ?: return@mapNotNull null - val link = fixUrlNull(innerA.attr("href")) ?: return@mapNotNull null - val title = innerA.attr("title") ?: return@mapNotNull null - val year = - Regex(""".*\((\d{4})\)""").find(title)?.groupValues?.getOrNull(1)?.toIntOrNull() - val imgSrc = it.select("img")?.attr("data-src") ?: return@mapNotNull null - val image = fixUrlNull(imgSrc) - - MovieSearchResponse( - name = title, - url = link, - apiName = this.name, - type = TvType.Movie, - posterUrl = image, - year = year - ) - } - } - - override suspend fun load(url: String): LoadResponse { - val doc = app.get(url).document - val body = doc.getElementsByTag("body") - val inner = body?.select("div.anis-content") - - // Video details - val poster = fixUrlNull(inner?.select("img.film-poster-img")?.attr("src")) ?: "" - //Log.i(this.name, "Result => (imgLinkCode) ${imgLinkCode}") - val title = inner?.select("h2.film-name.dynamic-name")?.text() ?: "" - val year = if (title.length > 5) { - title.substring(title.length - 5) - .trim().trimEnd(')').toIntOrNull() - } else { - null - } - //Log.i(this.name, "Result => (year) ${title.substring(title.length - 5)}") - val descript = body?.firstOrNull()?.select("div.film-description.m-hide")?.text() - val tags = inner?.select("div.item.item-list > a") - ?.mapNotNull { it?.text()?.trim() ?: return@mapNotNull null } - val recs = body.select("div.flw-item")?.mapNotNull { - val a = it.select("a") ?: return@mapNotNull null - val aUrl = fixUrlNull(a.attr("href")) ?: return@mapNotNull null - val aImg = fixUrlNull(it.select("img")?.attr("data-src")) - val aName = a.attr("title") ?: return@mapNotNull null - val aYear = aName.trim().takeLast(5).removeSuffix(")").toIntOrNull() - MovieSearchResponse( - url = aUrl, - name = aName, - type = TvType.Movie, - posterUrl = aImg, - year = aYear, - apiName = this.name - ) - } - - // Episodes Links - val episodeUrl = body.select("a.btn.btn-radius.btn-primary.btn-play").attr("href") - val episodeDoc = app.get(episodeUrl).document - - - val episodeList = episodeDoc.select("div.ss-list.ss-list-min > a").mapNotNull { ep -> - val episodeNumber = ep.attr("data-number").toIntOrNull() - val epLink = fixUrlNull(ep.attr("href")) ?: return@mapNotNull null - -// if (epLink.isNotBlank()) { -// // Fetch video links -// val epVidLinkEl = app.get(epLink, referer = mainUrl).document -// val ajaxUrl = epVidLinkEl.select("div#js-player")?.attr("embed") -// //Log.i(this.name, "Result => (ajaxUrl) ${ajaxUrl}") -// if (!ajaxUrl.isNullOrEmpty()) { -// val innerPage = app.get(fixUrl(ajaxUrl), referer = epLink).document -// val listOfLinks = mutableListOf() -// innerPage.select("div.player.active > main > div")?.forEach { em -> -// val href = fixUrlNull(em.attr("src")) ?: "" -// if (href.isNotBlank()) { -// listOfLinks.add(href) -// } -// } -// -// //Log.i(this.name, "Result => (listOfLinks) ${listOfLinks.toJson()}") -// -// } -// } - Episode( - name = null, - season = null, - episode = episodeNumber, - data = epLink, - posterUrl = null, - date = null - ) - } - - //If there's only 1 episode, consider it a movie. - if (episodeList.size == 1) { - return MovieLoadResponse( - name = title, - url = url, - apiName = this.name, - type = TvType.Movie, - dataUrl = episodeList.first().data, - posterUrl = poster, - year = year, - plot = descript, - recommendations = recs, - tags = tags - ) - } - return TvSeriesLoadResponse( - name = title, - url = url, - apiName = this.name, - type = TvType.AsianDrama, - episodes = episodeList, - posterUrl = poster, - year = year, - plot = descript, - recommendations = recs, - tags = tags - ) - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - println("DATATATAT $data") - - val document = app.get(data).document - val iframeUrl = document.select("iframe").attr("src") - val iframe = app.get(iframeUrl) - val iframeDoc = iframe.document - - argamap({ - iframeDoc.select(".list-server-items > .linkserver") - .forEach { element -> - val status = element.attr("data-status") ?: return@forEach - if (status != "1") return@forEach - val extractorData = element.attr("data-video") ?: return@forEach - loadExtractor(extractorData, iframe.url, subtitleCallback, callback) - } - }, { - val iv = "9262859232435825" - val secretKey = "93422192433952489752342908585752" - val secretDecryptKey = "93422192433952489752342908585752" - Vidstream.extractVidstream( - iframe.url, - this.name, - callback, - iv, - secretKey, - secretDecryptKey, - isUsingAdaptiveKeys = false, - isUsingAdaptiveData = true, - iframeDocument = iframeDoc - ) - }) - return true - } -} diff --git a/VidstreamBundle/src/main/kotlin/com/lagradost/KdramaHoodProvider.kt b/VidstreamBundle/src/main/kotlin/com/lagradost/KdramaHoodProvider.kt deleted file mode 100644 index c68571c..0000000 --- a/VidstreamBundle/src/main/kotlin/com/lagradost/KdramaHoodProvider.kt +++ /dev/null @@ -1,294 +0,0 @@ -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.AppUtils.toJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.getQualityFromName -import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.Jsoup - -class KdramaHoodProvider : MainAPI() { - override var mainUrl = "https://kdramahood.com" - override var name = "KDramaHood" - override val hasQuickSearch = false - override val hasMainPage = true - override val hasChromecastSupport = false - override val hasDownloadSupport = true - override val supportedTypes = setOf(TvType.AsianDrama) - - private data class ResponseDatas( - @JsonProperty("label") val label: String, - @JsonProperty("file") val file: String - ) - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val doc = app.get("$mainUrl/home2").document - val home = ArrayList() - - // Hardcoded homepage cause of site implementation - // Recently added - val recentlyInner = doc.selectFirst("div.peliculas") - val recentlyAddedTitle = recentlyInner!!.selectFirst("h1")?.text() ?: "Recently Added" - val recentlyAdded = recentlyInner.select("div.item_2.items > div.fit.item").mapNotNull { - val innerA = it.select("div.image > a") ?: return@mapNotNull null - val link = fixUrlNull(innerA.attr("href")) ?: return@mapNotNull null - val image = fixUrlNull(innerA.select("img").attr("src")) - - val innerData = it.selectFirst("div.data") - val title = innerData!!.selectFirst("h1")?.text() ?: return@mapNotNull null - val year = try { - val yearText = innerData.selectFirst("span.titulo_o") - ?.text()?.takeLast(11)?.trim()?.take(4) ?: "" - //Log.i(this.name, "Result => (yearText) $yearText") - val rex = Regex("\\((\\d+)") - //Log.i(this.name, "Result => (rex value) ${rex.find(yearText)?.value}") - rex.find(yearText)?.value?.toIntOrNull() - } catch (e: Exception) { - null - } - - MovieSearchResponse( - name = title, - url = link, - apiName = this.name, - type = TvType.TvSeries, - posterUrl = image, - year = year - ) - }.distinctBy { it.url } ?: listOf() - home.add(HomePageList(recentlyAddedTitle, recentlyAdded)) - return HomePageResponse(home.filter { it.list.isNotEmpty() }) - } - - override suspend fun search(query: String): List { - val url = "$mainUrl/?s=$query" - val html = app.get(url).document - val document = html.getElementsByTag("body") - .select("div.item_1.items > div.item") ?: return listOf() - - return document.mapNotNull { - if (it == null) { - return@mapNotNull null - } - val innerA = it.selectFirst("div.boxinfo > a") ?: return@mapNotNull null - val link = fixUrlNull(innerA.attr("href")) ?: return@mapNotNull null - val title = innerA.select("span.tt")?.text() ?: return@mapNotNull null - - val year = it.selectFirst("span.year")?.text()?.toIntOrNull() - val image = fixUrlNull(it.selectFirst("div.image > img")?.attr("src")) - - MovieSearchResponse( - name = title, - url = link, - apiName = this.name, - type = TvType.Movie, - posterUrl = image, - year = year - ) - } - } - - override suspend fun load(url: String): LoadResponse { - val doc = app.get(url).document - val inner = doc.selectFirst("div.central") - - // Video details - val title = inner?.selectFirst("h1")?.text() ?: "" - val poster = fixUrlNull(doc.selectFirst("meta[property=og:image]")?.attr("content")) ?: "" - //Log.i(this.name, "Result => (poster) ${poster}") - val info = inner!!.selectFirst("div#info") - val descript = inner.selectFirst("div.contenidotv > div > p")?.text() - val year = try { - val startLink = "https://kdramahood.com/drama-release-year/" - var res: Int? = null - info?.select("div.metadatac")?.forEach { - if (res != null) { - return@forEach - } - if (it == null) { - return@forEach - } - val yearLink = it.select("a").attr("href") ?: return@forEach - if (yearLink.startsWith(startLink)) { - res = yearLink.substring(startLink.length).replace("/", "").toIntOrNull() - } - } - res - } catch (e: Exception) { - null - } - - val recs = doc.select("div.sidebartv > div.tvitemrel").mapNotNull { - val a = it?.select("a") ?: return@mapNotNull null - val aUrl = fixUrlNull(a.attr("href")) ?: return@mapNotNull null - val aImg = a.select("img") - val aCover = fixUrlNull(aImg.attr("src")) ?: fixUrlNull(aImg.attr("data-src")) - val aNameYear = a.select("div.datatvrel") ?: return@mapNotNull null - val aName = aNameYear.select("h4").text() ?: aImg.attr("alt") ?: return@mapNotNull null - val aYear = aName.trim().takeLast(5).removeSuffix(")").toIntOrNull() - MovieSearchResponse( - url = aUrl, - name = aName, - type = TvType.Movie, - posterUrl = aCover, - year = aYear, - apiName = this.name - ) - } - - // Episodes Links - val episodeList = inner.select("ul.episodios > li")?.mapNotNull { ep -> - //Log.i(this.name, "Result => (ep) ${ep}") - val listOfLinks = mutableListOf() - val count = ep.select("div.numerando")?.text()?.toIntOrNull() ?: 0 - val innerA = ep.select("div.episodiotitle > a") ?: return@mapNotNull null - //Log.i(this.name, "Result => (innerA) ${innerA}") - val epLink = fixUrlNull(innerA.attr("href")) ?: return@mapNotNull null - //Log.i(this.name, "Result => (epLink) ${epLink}") - if (epLink.isNotBlank()) { - // Fetch video links - val epVidLinkEl = app.get(epLink, referer = mainUrl).document - val epLinksContent = epVidLinkEl.selectFirst("div.player_nav > script")?.html() - ?.replace("ifr_target.src =", "
    ") - ?.replace("';", "
    ") - //Log.i(this.name, "Result => (epLinksContent) $epLinksContent") - if (!epLinksContent.isNullOrEmpty()) { - //Log.i(this.name, "Result => (epLinksContent) ${Jsoup.parse(epLinksContent)?.select("div")}") - Jsoup.parse(epLinksContent)?.select("div")?.forEach { em -> - val href = em?.html()?.trim()?.removePrefix("'") ?: return@forEach - //Log.i(this.name, "Result => (ep#$count link) $href") - if (href.isNotBlank()) { - listOfLinks.add(fixUrl(href)) - } - } - } - //Fetch default source and subtitles - epVidLinkEl.select("div.embed2")?.forEach { defsrc -> - if (defsrc == null) { - return@forEach - } - val scriptstring = defsrc.toString() - if (scriptstring.contains("sources: [{")) { - "(?<=playerInstance2.setup\\()([\\s\\S]*?)(?=\\);)".toRegex() - .find(scriptstring)?.value?.let { itemjs -> - listOfLinks.add("$mainUrl$itemjs") - } - } - } - } - Episode( - name = null, - season = null, - episode = count, - data = listOfLinks.distinct().toJson(), - posterUrl = poster, - date = null - ) - } - - //If there's only 1 episode, consider it a movie. - if (episodeList?.size == 1) { - return MovieLoadResponse( - name = title, - url = url, - apiName = this.name, - type = TvType.Movie, - dataUrl = episodeList[0].data, - posterUrl = poster, - year = year, - plot = descript, - recommendations = recs - ) - } - return TvSeriesLoadResponse( - name = title, - url = url, - apiName = this.name, - type = TvType.AsianDrama, - episodes = episodeList?.reversed() ?: emptyList(), - posterUrl = poster, - year = year, - plot = descript, - recommendations = recs - ) - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - var count = 0 - parseJson>(data).apmap { item -> - if (item.isNotBlank()) { - count++ - if (item.startsWith(mainUrl)) { - val text = item.substring(mainUrl.length) - //Log.i(this.name, "Result => (text) $text") - //Find video files - try { - "(?<=sources: )([\\s\\S]*?)(?<=])".toRegex().find(text)?.value?.let { vid -> - parseJson>(vid).forEach { src -> - //Log.i(this.name, "Result => (src) ${src.toJson()}") - callback( - ExtractorLink( - name = name, - url = src.file, - quality = getQualityFromName(src.label), - referer = mainUrl, - source = name - ) - ) - } - } - } catch (e: Exception) { - logError(e) - } - //Find subtitles - try { - "(?<=tracks: )([\\s\\S]*?)(?<=])".toRegex().find(text)?.value?.let { sub -> - val subtext = sub.replace("file:", "\"file\":") - .replace("label:", "\"label\":") - .replace("kind:", "\"kind\":") - parseJson>(subtext).forEach { src -> - //Log.i(this.name, "Result => (sub) ${src.toJson()}") - subtitleCallback( - SubtitleFile( - lang = src.label, - url = src.file - ) - ) - } - } - } catch (e: Exception) { - logError(e) - } - - } else { - val url = fixUrl(item.trim()) - //Log.i(this.name, "Result => (url) $url") - when { - url.startsWith("https://asianembed.io") -> { - com.lagradost.AsianEmbedHelper.getUrls(url, subtitleCallback, callback) - } - url.startsWith("https://embedsito.com") -> { - val extractor = com.lagradost.XStreamCdn() - extractor.domainUrl = "embedsito.com" - extractor.getUrl(url).forEach { link -> - callback.invoke(link) - } - } - else -> { - loadExtractor(url, mainUrl, subtitleCallback, callback) - } - } - } - } - } - return count > 0 - } -} \ No newline at end of file diff --git a/VidstreamBundle/src/main/kotlin/com/lagradost/MultiQuality.kt b/VidstreamBundle/src/main/kotlin/com/lagradost/MultiQuality.kt deleted file mode 100644 index 2f77415..0000000 --- a/VidstreamBundle/src/main/kotlin/com/lagradost/MultiQuality.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.lagradost - -import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.utils.ExtractorApi -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.cloudstream3.utils.getQualityFromName -import java.net.URI - -class MultiQuality : ExtractorApi() { - override var name = "MultiQuality" - override var mainUrl = "https://gogo-play.net" - private val sourceRegex = Regex("""file:\s*['"](.*?)['"],label:\s*['"](.*?)['"]""") - private val m3u8Regex = Regex(""".*?(\d*).m3u8""") - private val urlRegex = Regex("""(.*?)([^/]+$)""") - override val requiresReferer = false - - override fun getExtractorUrl(id: String): String { - return "$mainUrl/loadserver.php?id=$id" - } - - override suspend fun getUrl(url: String, referer: String?): List { - val extractedLinksList: MutableList = mutableListOf() - with(app.get(url)) { - sourceRegex.findAll(this.text).forEach { sourceMatch -> - val extractedUrl = sourceMatch.groupValues[1] - // Trusting this isn't mp4, may fuck up stuff - if (URI(extractedUrl).path.endsWith(".m3u8")) { - with(app.get(extractedUrl)) { - m3u8Regex.findAll(this.text).forEach { match -> - extractedLinksList.add( - ExtractorLink( - name, - name = name, - urlRegex.find(this.url)!!.groupValues[1] + match.groupValues[0], - url, - getQualityFromName(match.groupValues[1]), - isM3u8 = true - ) - ) - } - - } - } else if (extractedUrl.endsWith(".mp4")) { - extractedLinksList.add( - ExtractorLink( - name, - "$name ${sourceMatch.groupValues[2]}", - extractedUrl, - url.replace(" ", "%20"), - Qualities.Unknown.value, - ) - ) - } - } - return extractedLinksList - } - } -} \ No newline at end of file diff --git a/VidstreamBundle/src/main/kotlin/com/lagradost/OpenVidsProvider.kt b/VidstreamBundle/src/main/kotlin/com/lagradost/OpenVidsProvider.kt deleted file mode 100644 index 5496cb4..0000000 --- a/VidstreamBundle/src/main/kotlin/com/lagradost/OpenVidsProvider.kt +++ /dev/null @@ -1,133 +0,0 @@ -package com.lagradost - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.extractors.Vidstream -import com.lagradost.cloudstream3.metaproviders.TmdbLink -import com.lagradost.cloudstream3.metaproviders.TmdbProvider -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor -import java.time.ZonedDateTime -import java.time.format.DateTimeFormatter - -class OpenVidsProvider:TmdbProvider() { - override val apiName = "OpenVids" - override var name = "OpenVids" - override var mainUrl = "https://openvids.io" - override val useMetaLoadResponse = true - override val instantLinkLoading = false - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - ) - - data class OpenvidsMain( - @JsonProperty("ok" ) val ok : Boolean? = null, - @JsonProperty("servers" ) val servers : OpenvidServers? = OpenvidServers() - ) - - data class OpenvidServers ( - @JsonProperty("streamsb" ) val streamsb : OpenvidServersData? = OpenvidServersData(), - @JsonProperty("voxzer" ) val voxzer : OpenvidServersData? = OpenvidServersData(), - @JsonProperty("mixdrop" ) val mixdrop : OpenvidServersData? = OpenvidServersData(), - @JsonProperty("doodstream" ) val doodstream : OpenvidServersData? = OpenvidServersData(), - @JsonProperty("voe" ) val voe : OpenvidServersData? = OpenvidServersData(), - @JsonProperty("vidcloud" ) val vidcloud : OpenvidServersData? = OpenvidServersData() - ) - data class OpenvidServersData ( - @JsonProperty("code" ) val code : String? = null, - @JsonProperty("updatedAt" ) val updatedAt : String? = null, - @JsonProperty("encoded" ) val encoded : Boolean? = null - ) - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val mappedData = parseJson(data) - val (id, site) = if (mappedData.imdbID != null) listOf( - mappedData.imdbID, - "imdb" - ) else listOf(mappedData.tmdbID.toString(), "tmdb") - val isMovie = mappedData.episode == null && mappedData.season == null - val embedUrl = if (isMovie) { - if(site == "imdb") "$mainUrl/movie/$id" else - "$mainUrl/tmdb/movie/$id" - } else { - val suffix = "$id-${mappedData.season ?: 1}-${mappedData.episode ?: 1}" - if (site == "imdb") "$mainUrl/episode/$suffix" else - "$mainUrl/tmdb/episode/$suffix" - } - val zonedatetime = ZonedDateTime.now() - val timeformated = DateTimeFormatter.ISO_INSTANT.format(zonedatetime) - val headers = if (isMovie) { - mapOf( - "Host" to "openvids.io", - "User-Agent" to USER_AGENT, - "Accept" to "*/*", - "Accept-Language" to "en-US,en;q=0.5", - "Referer" to embedUrl, - "updatedAt" to timeformated, - "title" to "${mappedData.movieName}", - "year" to "2016", - "DNT" to "1", - "Alt-Used" to "openvids.io", - "Connection" to "keep-alive", - "Sec-Fetch-Dest" to "empty", - "Sec-Fetch-Mode" to "cors", - "Sec-Fetch-Site" to "same-origin", - ) - } else { - mapOf( - "Host" to "openvids.io", - "User-Agent" to USER_AGENT, - "Accept" to "*/*", - "Accept-Language" to "en-US,en;q=0.5", - "Referer" to embedUrl, - "updatedAt" to timeformated, - "title" to "${mappedData.movieName} - season 1", - "year" to "2021", - "e" to "${mappedData.episode}", - "s" to "${mappedData.season}", - "DNT" to "1", - "Alt-Used" to "openvids.io", - "Connection" to "keep-alive", - "Sec-Fetch-Dest" to "empty", - "Sec-Fetch-Mode" to "cors", - "Sec-Fetch-Site" to "same-origin", - ) - } - val json = app.get("$mainUrl/api/servers.json?imdb=${mappedData.imdbID}", headers = headers).parsedSafe() - - val listservers = listOf( - "https://streamsb.net/e/" to json?.servers?.streamsb?.code, - "https://player.voxzer.org/view/" to json?.servers?.voxzer?.code, - "https://mixdrop.co/e/" to json?.servers?.mixdrop?.code, - "https://dood.pm/e/" to json?.servers?.doodstream?.code, - "https://voe.sx/e/" to json?.servers?.voe?.code, - "https://membed.net/streaming.php?id=" to json?.servers?.vidcloud?.code - ).mapNotNull { (url, id) -> if(id==null) return@mapNotNull null else "$url$id" } - - if (json?.ok != true) return false - listservers.apmap { links -> - if (links.contains("membed")) { - val membed = VidEmbedProvider() - Vidstream.extractVidstream( - links, - this.name, - callback, - membed.iv, - membed.secretKey, - membed.secretDecryptKey, - membed.isUsingAdaptiveKeys, - membed.isUsingAdaptiveData) - } else - loadExtractor(links, data, subtitleCallback, callback) - } - return true - } - -} \ No newline at end of file diff --git a/VidstreamBundle/src/main/kotlin/com/lagradost/VidEmbedProvider.kt b/VidstreamBundle/src/main/kotlin/com/lagradost/VidEmbedProvider.kt deleted file mode 100644 index 59bd4a5..0000000 --- a/VidstreamBundle/src/main/kotlin/com/lagradost/VidEmbedProvider.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.lagradost - -import com.lagradost.cloudstream3.TvType - -/** Needs to inherit from MainAPI() to - * make the app know what functions to call - */ -class VidEmbedProvider : VidstreamProviderTemplate() { - // mainUrl is good to have as a holder for the url to make future changes easier. - override var mainUrl = "https://membed.net" - - // name is for how the provider will be named which is visible in the UI, no real rules for this. - override var name = "VidEmbed" - - override val homePageUrlList: List = listOf( - mainUrl, - "$mainUrl/movies", - "$mainUrl/series", - "$mainUrl/recommended-series", - "$mainUrl/cinema-movies" - ) - - override val iv = "9225679083961858" - override val secretKey = "25742532592138496744665879883281" - override val secretDecryptKey = secretKey - - // This is just extra metadata about what type of movies the provider has. - // Needed for search functionality. - override val supportedTypes = setOf(TvType.TvSeries, TvType.Movie) -} diff --git a/VidstreamBundle/src/main/kotlin/com/lagradost/Vidstream.kt b/VidstreamBundle/src/main/kotlin/com/lagradost/Vidstream.kt deleted file mode 100644 index e14fd52..0000000 --- a/VidstreamBundle/src/main/kotlin/com/lagradost/Vidstream.kt +++ /dev/null @@ -1,235 +0,0 @@ -package com.lagradost.cloudstream3.extractors - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.MultiQuality -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.AcraApplication.Companion.getKey -import com.lagradost.cloudstream3.mvvm.safeApiCall -import com.lagradost.cloudstream3.utils.* -import org.jsoup.Jsoup -import org.jsoup.nodes.Document -import java.net.URI -import javax.crypto.Cipher -import javax.crypto.spec.IvParameterSpec -import javax.crypto.spec.SecretKeySpec - -/** - * overrideMainUrl is necessary for for other vidstream clones like vidembed.cc - * If they diverge it'd be better to make them separate. - * */ -class Vidstream(val mainUrl: String) { - val name: String = "Vidstream" - - companion object { - data class GogoSources( - @JsonProperty("source") val source: List?, - @JsonProperty("sourceBk") val sourceBk: List?, - //val track: List, - //val advertising: List, - //val linkiframe: String - ) - - data class GogoSource( - @JsonProperty("file") val file: String, - @JsonProperty("label") val label: String?, - @JsonProperty("type") val type: String?, - @JsonProperty("default") val default: String? = null - ) - - // https://github.com/saikou-app/saikou/blob/3e756bd8e876ad7a9318b17110526880525a5cd3/app/src/main/java/ani/saikou/anime/source/extractors/GogoCDN.kt#L60 - // No Licence on the function - private fun cryptoHandler( - string: String, - iv: String, - secretKeyString: String, - encrypt: Boolean = true - ): String { - //println("IV: $iv, Key: $secretKeyString, encrypt: $encrypt, Message: $string") - val ivParameterSpec = IvParameterSpec(iv.toByteArray()) - val secretKey = SecretKeySpec(secretKeyString.toByteArray(), "AES") - val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") - return if (!encrypt) { - cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec) - String(cipher.doFinal(base64DecodeArray(string))) - } else { - cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec) - base64Encode(cipher.doFinal(string.toByteArray())) - } - } - - /** - * @param iframeUrl something like https://gogoplay4.com/streaming.php?id=XXXXXX - * @param mainApiName used for ExtractorLink names and source - * @param iv secret iv from site, required non-null if isUsingAdaptiveKeys is off - * @param secretKey secret key for decryption from site, required non-null if isUsingAdaptiveKeys is off - * @param secretDecryptKey secret key to decrypt the response json, required non-null if isUsingAdaptiveKeys is off - * @param isUsingAdaptiveKeys generates keys from IV and ID, see getKey() - * @param isUsingAdaptiveData generate encrypt-ajax data based on $("script[data-name='episode']")[0].dataset.value - * */ - suspend fun extractVidstream( - iframeUrl: String, - mainApiName: String, - callback: (ExtractorLink) -> Unit, - iv: String?, - secretKey: String?, - secretDecryptKey: String?, - // This could be removed, but i prefer it verbose - isUsingAdaptiveKeys: Boolean, - isUsingAdaptiveData: Boolean, - // If you don't want to re-fetch the document - iframeDocument: Document? = null - ) = safeApiCall { - // https://github.com/saikou-app/saikou/blob/3e756bd8e876ad7a9318b17110526880525a5cd3/app/src/main/java/ani/saikou/anime/source/extractors/GogoCDN.kt - // No Licence on the following code - // Also modified of https://github.com/jmir1/aniyomi-extensions/blob/master/src/en/gogoanime/src/eu/kanade/tachiyomi/animeextension/en/gogoanime/extractors/GogoCdnExtractor.kt - // License on the code above https://github.com/jmir1/aniyomi-extensions/blob/master/LICENSE - - if ((iv == null || secretKey == null || secretDecryptKey == null) && !isUsingAdaptiveKeys) - return@safeApiCall - - val id = Regex("id=([^&]+)").find(iframeUrl)!!.value.removePrefix("id=") - - var document: Document? = iframeDocument - val foundIv = - iv ?: (document ?: app.get(iframeUrl).document.also { document = it }) - .select("""div.wrapper[class*=container]""") - .attr("class").split("-").lastOrNull() ?: return@safeApiCall - val foundKey = secretKey ?: getKey(base64Decode(id) + foundIv) ?: return@safeApiCall - val foundDecryptKey = secretDecryptKey ?: foundKey - - val uri = URI(iframeUrl) - val mainUrl = "https://" + uri.host - - val encryptedId = cryptoHandler(id, foundIv, foundKey) - val encryptRequestData = if (isUsingAdaptiveData) { - // Only fetch the document if necessary - val realDocument = document ?: app.get(iframeUrl).document - val dataEncrypted = - realDocument.select("script[data-name='episode']").attr("data-value") - val headers = cryptoHandler(dataEncrypted, foundIv, foundKey, false) - "id=$encryptedId&alias=$id&" + headers.substringAfter("&") - } else { - "id=$encryptedId&alias=$id" - } - - val jsonResponse = - app.get( - "$mainUrl/encrypt-ajax.php?$encryptRequestData", - headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ) - val dataencrypted = - jsonResponse.text.substringAfter("{\"data\":\"").substringBefore("\"}") - val datadecrypted = cryptoHandler(dataencrypted, foundIv, foundDecryptKey, false) - val sources = AppUtils.parseJson(datadecrypted) - - fun invokeGogoSource( - source: GogoSource, - sourceCallback: (ExtractorLink) -> Unit - ) { - sourceCallback.invoke( - ExtractorLink( - mainApiName, - mainApiName, - source.file, - mainUrl, - getQualityFromName(source.label), - isM3u8 = source.type == "hls" || source.label?.contains( - "auto", - ignoreCase = true - ) == true - ) - ) - } - - sources.source?.forEach { - invokeGogoSource(it, callback) - } - sources.sourceBk?.forEach { - invokeGogoSource(it, callback) - } - } - } - - - private fun getExtractorUrl(id: String): String { - return "$mainUrl/streaming.php?id=$id" - } - - private fun getDownloadUrl(id: String): String { - return "$mainUrl/download?id=$id" - } - - private val normalApis = arrayListOf(MultiQuality()) - - // https://gogo-stream.com/streaming.php?id=MTE3NDg5 - suspend fun getUrl( - id: String, - isCasting: Boolean = false, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit, - ): Boolean { - val extractorUrl = getExtractorUrl(id) - argamap( - { - normalApis.apmap { api -> - val url = api.getExtractorUrl(id) - api.getSafeUrl( - url, - callback = callback, - subtitleCallback = subtitleCallback - ) - } - }, { - /** Stolen from GogoanimeProvider.kt extractor */ - val link = getDownloadUrl(id) - println("Generated vidstream download link: $link") - val page = app.get(link, referer = extractorUrl) - - val pageDoc = Jsoup.parse(page.text) - val qualityRegex = Regex("(\\d+)P") - - //a[download] - pageDoc.select(".dowload > a")?.apmap { element -> - val href = element.attr("href") ?: return@apmap - val qual = if (element.text() - .contains("HDP") - ) "1080" else qualityRegex.find(element.text())?.destructured?.component1() - .toString() - - if (!loadExtractor(href, link, subtitleCallback, callback)) { - callback.invoke( - ExtractorLink( - this.name, - name = this.name, - href, - page.url, - getQualityFromName(qual), - element.attr("href").contains(".m3u8") - ) - ) - } - } - }, { - with(app.get(extractorUrl)) { - val document = Jsoup.parse(this.text) - val primaryLinks = document.select("ul.list-server-items > li.linkserver") - //val extractedLinksList: MutableList = mutableListOf() - - // All vidstream links passed to extractors - primaryLinks.distinctBy { it.attr("data-video") }.forEach { element -> - val link = element.attr("data-video") - //val name = element.text() - - // Matches vidstream links with extractors - extractorApis.filter { !it.requiresReferer || !isCasting }.apmap { api -> - if (link.startsWith(api.mainUrl)) { - api.getSafeUrl(link, extractorUrl, subtitleCallback, callback) - } - } - } - } - } - ) - return true - } -} \ No newline at end of file diff --git a/VidstreamBundle/src/main/kotlin/com/lagradost/VidstreamBundlePlugin.kt b/VidstreamBundle/src/main/kotlin/com/lagradost/VidstreamBundlePlugin.kt deleted file mode 100644 index dfc1512..0000000 --- a/VidstreamBundle/src/main/kotlin/com/lagradost/VidstreamBundlePlugin.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context -import com.lagradost.cloudstream3.extractors.Vidstream - -@CloudstreamPlugin -class VidstreamBundlePlugin : Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerExtractorAPI(MultiQuality()) - registerExtractorAPI(XStreamCdn()) - registerExtractorAPI(LayarKaca()) - registerExtractorAPI(DBfilm()) - registerExtractorAPI(Luxubu()) - registerExtractorAPI(FEmbed()) - registerExtractorAPI(Fplayer()) - registerExtractorAPI(FeHD()) - registerMainAPI(VidEmbedProvider()) - registerMainAPI(OpenVidsProvider()) - registerMainAPI(KdramaHoodProvider()) - registerMainAPI(DramaSeeProvider()) - registerMainAPI(AsianLoadProvider()) - registerMainAPI(WatchAsianProvider()) - } -} \ No newline at end of file diff --git a/VidstreamBundle/src/main/kotlin/com/lagradost/VidstreamProviderTemplate.kt b/VidstreamBundle/src/main/kotlin/com/lagradost/VidstreamProviderTemplate.kt deleted file mode 100644 index 751783d..0000000 --- a/VidstreamBundle/src/main/kotlin/com/lagradost/VidstreamProviderTemplate.kt +++ /dev/null @@ -1,338 +0,0 @@ -package com.lagradost - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.extractors.Vidstream -//import com.lagradost.cloudstream3.animeproviders.GogoanimeProvider.Companion.extractVidstream -//import com.lagradost.cloudstream3.extractors.Vidstream -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.getQualityFromName -import org.jsoup.Jsoup -import java.net.URI - -/** Needs to inherit from MainAPI() to - * make the app know what functions to call - */ -open class VidstreamProviderTemplate : MainAPI() { - open val homePageUrlList = listOf() - open val vidstreamExtractorUrl: String? = null - - /** - * Used to generate encrypted video links. - * Try keys from other providers before cracking - * one yourself. - * */ - // Userscript to get the keys: - - /* - // ==UserScript== - // @name Easy keys - // @namespace Violentmonkey Scripts - // @match https://*/streaming.php* - // @grant none - // @version 1.0 - // @author LagradOst - // @description 4/16/2022, 2:05:31 PM - // ==/UserScript== - - let encrypt = CryptoJS.AES.encrypt; - CryptoJS.AES.encrypt = (message, key, cfg) => { - let realKey = CryptoJS.enc.Utf8.stringify(key); - let realIv = CryptoJS.enc.Utf8.stringify(cfg.iv); - - var result = encrypt(message, key, cfg); - let realResult = CryptoJS.enc.Utf8.stringify(result); - - popup = "Encrypt key: " + realKey + "\n\nIV: " + realIv + "\n\nMessage: " + message + "\n\nResult: " + realResult; - alert(popup); - - return result; - }; - - let decrypt = CryptoJS.AES.decrypt; - CryptoJS.AES.decrypt = (message, key, cfg) => { - let realKey = CryptoJS.enc.Utf8.stringify(key); - let realIv = CryptoJS.enc.Utf8.stringify(cfg.iv); - - let result = decrypt(message, key, cfg); - let realResult = CryptoJS.enc.Utf8.stringify(result); - - popup = "Decrypt key: " + realKey + "\n\nIV: " + realIv + "\n\nMessage: " + message + "\n\nResult: " + realResult; - alert(popup); - - return result; - }; - - */ - */ - - open val iv: String? = null - open val secretKey: String? = null - open val secretDecryptKey: String? = null - - /** Generated the key from IV and ID */ - open val isUsingAdaptiveKeys: Boolean = false - - /** - * Generate data for the encrypt-ajax automatically (only on supported sites) - * See $("script[data-name='episode']")[0].dataset.value - * */ - open val isUsingAdaptiveData: Boolean = false - - -// // mainUrl is good to have as a holder for the url to make future changes easier. -// override val mainUrl: String -// get() = "https://vidembed.cc" -// -// // name is for how the provider will be named which is visible in the UI, no real rules for this. -// override val name: String -// get() = "VidEmbed" - - // hasQuickSearch defines if quickSearch() should be called, this is only when typing the searchbar - // gives results on the site instead of bringing you to another page. - // if hasQuickSearch is true and quickSearch() hasn't been overridden you will get errors. - // VidEmbed actually has quick search on their site, but the function wasn't implemented. - override val hasQuickSearch = false - - // If getMainPage() is functional, used to display the homepage in app, an optional, but highly encouraged endevour. - override val hasMainPage = true - - // Searching returns a SearchResponse, which can be one of the following: AnimeSearchResponse, MovieSearchResponse, TorrentSearchResponse, TvSeriesSearchResponse - // Each of the classes requires some different data, but always has some critical things like name, poster and url. - override suspend fun search(query: String): ArrayList { - // Simply looking at devtools network is enough to spot a request like: - // https://vidembed.cc/search.html?keyword=neverland where neverland is the query, can be written as below. - val link = "$mainUrl/search.html?keyword=$query" - val html = app.get(link).text - val soup = Jsoup.parse(html) - - return ArrayList(soup.select(".listing.items > .video-block").map { li -> - // Selects the href in - val href = fixUrl(li.selectFirst("a")!!.attr("href")) - val poster = li.selectFirst("img")?.attr("src") - - // .text() selects all the text in the element, be careful about doing this while too high up in the html hierarchy - val title = li.selectFirst(".name")!!.text() - // Use get(0) and toIntOrNull() to prevent any possible crashes, [0] or toInt() will error the search on unexpected values. - val year = li.selectFirst(".date")?.text()?.split("-")?.get(0)?.toIntOrNull() - - TvSeriesSearchResponse( - // .trim() removes unwanted spaces in the start and end. - if (!title.contains("Episode")) title else title.split("Episode")[0].trim(), - href, - this.name, - TvType.TvSeries, - poster, year, - // You can't get the episodes from the search bar. - null - ) - }) - } - - - // Load, like the name suggests loads the info page, where all the episodes and data usually is. - // Like search you should return either of: AnimeLoadResponse, MovieLoadResponse, TorrentLoadResponse, TvSeriesLoadResponse. - override suspend fun load(url: String): LoadResponse? { - // Gets the url returned from searching. - val html = app.get(url).text - val soup = Jsoup.parse(html) - - var title = soup.selectFirst("h1,h2,h3")!!.text() - title = if (!title.contains("Episode")) title else title.split("Episode")[0].trim() - - val description = soup.selectFirst(".post-entry")?.text()?.trim() - var poster: String? = null - var year: Int? = null - - val episodes = - soup.select(".listing.items.lists > .video-block").withIndex().map { (_, li) -> - val epTitle = if (li.selectFirst(".name") != null) - if (li.selectFirst(".name")!!.text().contains("Episode")) - "Episode " + li.selectFirst(".name")!!.text().split("Episode")[1].trim() - else - li.selectFirst(".name")!!.text() - else "" - val epThumb = li.selectFirst("img")?.attr("src") - val epDate = li.selectFirst(".meta > .date")!!.text() - - if (poster == null) { - poster = li.selectFirst("img")?.attr("onerror")?.split("=")?.get(1) - ?.replace(Regex("[';]"), "") - } - - val epNum = Regex("""Episode (\d+)""").find(epTitle)?.destructured?.component1() - ?.toIntOrNull() - if (year == null) { - year = epDate.split("-")[0].toIntOrNull() - } - newEpisode(li.selectFirst("a")!!.attr("href")) { - this.episode = epNum - this.posterUrl = epThumb - addDate(epDate) - } - }.reversed() - - // Make sure to get the type right to display the correct UI. - val tvType = - if (episodes.size == 1 && episodes[0].name == title) TvType.Movie else TvType.TvSeries - - return when (tvType) { - TvType.TvSeries -> { - TvSeriesLoadResponse( - title, - url, - this.name, - tvType, - episodes, - poster, - year, - description, - ShowStatus.Ongoing, - null, - null - ) - } - TvType.Movie -> { - MovieLoadResponse( - title, - url, - this.name, - tvType, - episodes[0].data, - poster, - year, - description, - null, - null - ) - } - else -> null - } - } - - // This loads the homepage, which is basically a collection of search results with labels. - // Optional function, but make sure to enable hasMainPage if you program this. - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val urls = homePageUrlList - val homePageList = ArrayList() - // .pmap {} is used to fetch the different pages in parallel - urls.apmap { url -> - val response = app.get(url, timeout = 20).text - val document = Jsoup.parse(response) - document.select("div.main-inner").forEach { inner -> - // Always trim your text unless you want the risk of spaces at the start or end. - val title = inner.select(".widget-title").text().trim() - val elements = inner.select(".video-block").map { - val link = fixUrl(it.select("a").attr("href")) - val image = it.select(".picture > img").attr("src") - val name = - it.select("div.name").text().trim().replace(Regex("""[Ee]pisode \d+"""), "") - val isSeries = (name.contains("Season") || name.contains("Episode")) - - if (isSeries) { - newTvSeriesSearchResponse(name, link) { - posterUrl = image - } - } else { - newMovieSearchResponse(name, link) { - posterUrl = image - } - } - } - - homePageList.add( - HomePageList( - title, elements - ) - ) - } - } - return HomePageResponse(homePageList) - } - - // loadLinks gets the raw .mp4 or .m3u8 urls from the data parameter in the episodes class generated in load() - // See Episode(...) in this provider. - // The data are usually links, but can be any other string to help aid loading the links. - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - // These callbacks are functions you should call when you get a link to a subtitle file or media file. - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - // "?: return" is a very useful statement which returns if the iframe link isn't found. - val iframeLink = - Jsoup.parse(app.get(data).text).selectFirst("iframe")?.attr("src") ?: return false - -// extractVidstream( -// iframeLink, -// this.name, -// callback, -// iv, -// secretKey, -// secretDecryptKey, -// isUsingAdaptiveKeys, -// isUsingAdaptiveData -// ) - // In this case the video player is a vidstream clone and can be handled by the vidstream extractor. - // This case is a both unorthodox and you normally do not call extractors as they detect the url returned and does the rest. - val vidstreamObject = Vidstream(vidstreamExtractorUrl ?: mainUrl) - // https://vidembed.cc/streaming.php?id=MzUwNTY2&... -> MzUwNTY2 - val id = Regex("""id=([^&]*)""").find(iframeLink)?.groupValues?.get(1) - - if (id != null) { - vidstreamObject.getUrl(id, isCasting, subtitleCallback, callback) - } - - val html = app.get(fixUrl(iframeLink)).text - val soup = Jsoup.parse(html) - - val servers = soup.select(".list-server-items > .linkserver").mapNotNull { li -> - if (!li?.attr("data-video").isNullOrEmpty()) { - Pair(li.text(), fixUrl(li.attr("data-video"))) - } else { - null - } - } - servers.apmap { - // When checking strings make sure to make them lowercase and trimmed because edgecases like "beta server " wouldn't work otherwise. - if (it.first.trim().equals("beta server", ignoreCase = true)) { - // Group 1: link, Group 2: Label - // Regex can be used to effectively parse small amounts of json without bothering with writing a json class. - val sourceRegex = - Regex("""sources:[\W\w]*?file:\s*["'](.*?)["'][\W\w]*?label:\s*["'](.*?)["']""") - val trackRegex = - Regex("""tracks:[\W\w]*?file:\s*["'](.*?)["'][\W\w]*?label:\s*["'](.*?)["']""") - - // Having a referer is often required. It's a basic security check most providers have. - // Try to replicate what your browser does. - val serverHtml = app.get(it.second, headers = mapOf("referer" to iframeLink)).text - sourceRegex.findAll(serverHtml).forEach { match -> - callback.invoke( - ExtractorLink( - this.name, - match.groupValues.getOrNull(2)?.let { "${this.name} $it" } ?: this.name, - match.groupValues[1], - it.second, - // Useful function to turn something like "1080p" to an app quality. - getQualityFromName(match.groupValues.getOrNull(2) ?: ""), - // Kinda risky - // isM3u8 makes the player pick the correct extractor for the source. - // If isM3u8 is wrong the player will error on that source. - URI(match.groupValues[1]).path.endsWith(".m3u8"), - ) - ) - } - trackRegex.findAll(serverHtml).forEach { match -> - subtitleCallback.invoke( - SubtitleFile( - match.groupValues.getOrNull(2) ?: "Unknown", - match.groupValues[1] - ) - ) - } - } - } - - return true - } -} diff --git a/VidstreamBundle/src/main/kotlin/com/lagradost/WatchAsianProvider.kt b/VidstreamBundle/src/main/kotlin/com/lagradost/WatchAsianProvider.kt deleted file mode 100644 index 84fd13a..0000000 --- a/VidstreamBundle/src/main/kotlin/com/lagradost/WatchAsianProvider.kt +++ /dev/null @@ -1,251 +0,0 @@ -package com.lagradost - -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.extractors.Vidstream -//import com.lagradost.cloudstream3.animeproviders.GogoanimeProvider.Companion.extractVidstream -//import com.lagradost.cloudstream3.extractors.XStreamCdn -//import com.lagradost.cloudstream3.extractors.helper.AsianEmbedHelper -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.AppUtils.toJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor - -class WatchAsianProvider : MainAPI() { - override var mainUrl = "https://watchasian.cx" - override var name = "WatchAsian" - override val hasQuickSearch = false - override val hasMainPage = true - override val hasChromecastSupport = false - override val hasDownloadSupport = true - override val supportedTypes = setOf(TvType.AsianDrama) - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val headers = mapOf("X-Requested-By" to mainUrl) - val doc = app.get(mainUrl, headers = headers).document - val rowPair = mutableListOf>() - doc.select("div.block-tab").forEach { - it?.select("ul.tab > li")?.mapNotNull { row -> - val link = row?.attr("data-tab") ?: return@mapNotNull null - val title = row.text() ?: return@mapNotNull null - Pair(title, link) - }?.let { it1 -> - rowPair.addAll( - it1 - ) - } - } - - return HomePageResponse( - rowPair.mapNotNull { row -> - val main = (doc.select("div.tab-content.${row.second}") - ?: doc.select("div.tab-content.${row.second}.selected")) - ?: return@mapNotNull null - - val title = row.first - val inner = main.select("li") ?: return@mapNotNull null - - HomePageList( - title, - inner.map { - // Get inner div from article - val innerBody = it?.selectFirst("a") - // Fetch details - val link = fixUrlNull(innerBody?.attr("href")) ?: return@map null - val image = - fixUrlNull(innerBody?.select("img")?.attr("data-original")) ?: "" - val name = (innerBody?.selectFirst("h3.title")?.text() ?: innerBody?.text()) - ?: "" - //Log.i(this.name, "Result => (innerBody, image) ${innerBody} / ${image}") - MovieSearchResponse( - name, - link, - this.name, - TvType.TvSeries, - image, - year = null, - id = null, - ) - }.filterNotNull().distinctBy { c -> c.url }) - }.filter { a -> a.list.isNotEmpty() } - ) - } - - override suspend fun search(query: String): List { - val url = "$mainUrl/search?type=movies&keyword=$query" - val document = app.get(url).document.getElementsByTag("body") - .select("div.block.tab-container > div > ul > li") ?: return listOf() - - return document.mapNotNull { - val innerA = it?.selectFirst("a") ?: return@mapNotNull null - val link = fixUrlNull(innerA.attr("href")) ?: return@mapNotNull null - val title = it.select("h3.title").text() ?: return@mapNotNull null - if (title.isEmpty()) { - return@mapNotNull null - } - val year = null - val imgsrc = innerA.select("img").attr("data-original") ?: return@mapNotNull null - val image = fixUrlNull(imgsrc) - //Log.i(this.name, "Result => (img movie) $title / $link") - MovieSearchResponse( - title, - link, - this.name, - TvType.Movie, - image, - year - ) - }.distinctBy { a -> a.url } - } - - override suspend fun load(url: String): LoadResponse { - val body = app.get(url).document - // Declare vars - val isDramaDetail = url.contains("/drama-detail/") - var poster: String? = null - var title = "" - var descript: String? = null - var year: Int? = null - var tags: List? = null - if (isDramaDetail) { - val main = body.select("div.details") - val inner = main.select("div.info") - // Video details - poster = fixUrlNull(main.select("div.img > img").attr("src")) - //Log.i(this.name, "Result => (imgLinkCode) ${imgLinkCode}") - title = inner.select("h1").firstOrNull()?.text() ?: "" - //Log.i(this.name, "Result => (year) ${title.substring(title.length - 5)}") - descript = inner.text() - - inner.select("p").forEach { p -> - val caption = - p?.selectFirst("span")?.text()?.trim()?.lowercase()?.removeSuffix(":")?.trim() - ?: return@forEach - when (caption) { - "genre" -> { - tags = p.select("a").mapNotNull { it?.text()?.trim() } - } - "released" -> { - year = p.select("a").text().trim()?.toIntOrNull() - } - } - } - } else { - poster = body.select("meta[itemprop=\"image\"]")?.attr("content") ?: "" - title = body.selectFirst("div.block.watch-drama")?.selectFirst("h1") - ?.text() ?: "" - year = null - descript = body.select("meta[name=\"description\"]")?.attr("content") - } - //Fallback year from title - if (year == null) { - year = if (title.length > 5) { - title.replace(")", "").replace("(", "").substring(title.length - 5) - .trim().trimEnd(')').toIntOrNull() - } else { - null - } - } - - // Episodes Links - //Log.i(this.name, "Result => (all eps) ${body.select("ul.list-episode-item-2.all-episode > li")}") - val episodeList = body.select("ul.list-episode-item-2.all-episode > li").mapNotNull { ep -> - //Log.i(this.name, "Result => (epA) ${ep.select("a")}") - val innerA = ep.select("a") ?: return@mapNotNull null - //Log.i(this.name, "Result => (innerA) ${fixUrlNull(innerA.attr("href"))}") - val epLink = fixUrlNull(innerA.attr("href")) ?: return@mapNotNull null - - val regex = "(?<=episode-).*?(?=.html)".toRegex() - val count = regex.find(epLink, mainUrl.length)?.value?.toIntOrNull() ?: 0 - //Log.i(this.name, "Result => $epLink (regexYear) ${count}") - Episode( - name = null, - season = null, - episode = count, - data = epLink, - posterUrl = poster, - date = null - ) - } - //If there's only 1 episode, consider it a movie. - if (episodeList.size == 1) { - //Clean title - title = title.trim().removeSuffix("Episode 1") - val streamlink = getServerLinks(episodeList[0].data) - //Log.i(this.name, "Result => (streamlink) $streamlink") - return MovieLoadResponse( - name = title, - url = url, - apiName = this.name, - type = TvType.Movie, - dataUrl = streamlink, - posterUrl = poster, - year = year, - plot = descript, - tags = tags - ) - } - return TvSeriesLoadResponse( - name = title, - url = url, - apiName = this.name, - type = TvType.AsianDrama, - episodes = episodeList.reversed(), - posterUrl = poster, - year = year, - plot = descript, - tags = tags - ) - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val links = if (data.startsWith(mainUrl)) { - getServerLinks(data) - } else { - data - } - var count = 0 - parseJson>(links).apmap { item -> - count++ - val url = fixUrl(item.trim()) - //Log.i(this.name, "Result => (url) $url") - when { - url.startsWith("https://asianembed.io") || url.startsWith("https://asianload.io") -> { - val iv = "9262859232435825" - val secretKey = "93422192433952489752342908585752" - Vidstream.extractVidstream( - url, this.name, callback, iv, secretKey, secretKey, - isUsingAdaptiveKeys = false, - isUsingAdaptiveData = false - ) - AsianEmbedHelper.getUrls(url, subtitleCallback, callback) - } - url.startsWith("https://embedsito.com") -> { - val extractor = XStreamCdn() - extractor.domainUrl = "embedsito.com" - extractor.getSafeUrl( - url, - subtitleCallback = subtitleCallback, - callback = callback, - ) - } - else -> { - loadExtractor(url, mainUrl, subtitleCallback, callback) - } - } - } - return count > 0 - } - - private suspend fun getServerLinks(url: String): String { - val moviedoc = app.get(url, referer = mainUrl).document - return moviedoc.select("div.anime_muti_link > ul > li") - .mapNotNull { - fixUrlNull(it?.attr("data-video")) ?: return@mapNotNull null - }.toJson() - } -} diff --git a/VidstreamBundle/src/main/kotlin/com/lagradost/XStreamCdn.kt b/VidstreamBundle/src/main/kotlin/com/lagradost/XStreamCdn.kt deleted file mode 100644 index f6de868..0000000 --- a/VidstreamBundle/src/main/kotlin/com/lagradost/XStreamCdn.kt +++ /dev/null @@ -1,93 +0,0 @@ -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 LayarKaca: XStreamCdn() { - override val name: String = "LayarKaca-xxi" - override val mainUrl: String = "https://layarkacaxxi.icu" -} - -class DBfilm: XStreamCdn() { - override val name: String = "DBfilm" - override val mainUrl: String = "https://dbfilm.bar" -} - -class Luxubu : XStreamCdn(){ - override val name: String = "FE" - override val mainUrl: String = "https://www.luxubu.review" -} - -class FEmbed: XStreamCdn() { - override val name: String = "FEmbed" - override val mainUrl: String = "https://www.fembed.com" -} - -class Fplayer: XStreamCdn() { - override val name: String = "Fplayer" - override val mainUrl: String = "https://fplayer.info" -} - -class FeHD: XStreamCdn() { - override val name: String = "FeHD" - override val mainUrl: String = "https://fembed-hd.com" - override var domainUrl: String = "fembed-hd.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/WatchCartoonOnlineProvider/build.gradle.kts b/WatchCartoonOnlineProvider/build.gradle.kts deleted file mode 100644 index f7ae6b7..0000000 --- a/WatchCartoonOnlineProvider/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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", - "Cartoon", - "Anime", - "TvSeries", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=www.wcostream.com&sz=24" -} \ No newline at end of file diff --git a/WatchCartoonOnlineProvider/src/main/AndroidManifest.xml b/WatchCartoonOnlineProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/WatchCartoonOnlineProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/WatchCartoonOnlineProvider/src/main/kotlin/com/lagradost/WatchCartoonOnlineProvider.kt b/WatchCartoonOnlineProvider/src/main/kotlin/com/lagradost/WatchCartoonOnlineProvider.kt deleted file mode 100644 index edb9086..0000000 --- a/WatchCartoonOnlineProvider/src/main/kotlin/com/lagradost/WatchCartoonOnlineProvider.kt +++ /dev/null @@ -1,270 +0,0 @@ -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 org.jsoup.Jsoup -import org.mozilla.javascript.Context -import org.mozilla.javascript.Scriptable -import java.util.* - - -class WatchCartoonOnlineProvider : MainAPI() { - override var name = "WatchCartoonOnline" - override var mainUrl = "https://www.wcostream.com" - - override val supportedTypes = setOf( - TvType.Cartoon, - TvType.Anime, - TvType.AnimeMovie, - TvType.TvSeries - ) - - override suspend fun search(query: String): List { - val url = "https://www.wcostream.com/search" - - var response = - app.post( - url, - headers = mapOf("Referer" to url), - data = mapOf("catara" to query, "konuara" to "series") - ).text - var document = Jsoup.parse(response) - var items = document.select("div#blog > div.cerceve").toList() - - val returnValue = ArrayList() - - for (item in items) { - val header = item.selectFirst("> div.iccerceve") - val titleHeader = header!!.selectFirst("> div.aramadabaslik > a") - val title = titleHeader!!.text() - val href = fixUrl(titleHeader.attr("href")) - val poster = fixUrl(header.selectFirst("> a > img")!!.attr("src")) - val genreText = item.selectFirst("div.cerceve-tur-ve-genre")!!.ownText() - if (genreText.contains("cartoon")) { - returnValue.add(TvSeriesSearchResponse(title, href, this.name, TvType.Cartoon, poster, null, null)) - } else { - val isDubbed = genreText.contains("dubbed") - val set: EnumSet = - EnumSet.of(if (isDubbed) DubStatus.Dubbed else DubStatus.Subbed) - returnValue.add( - AnimeSearchResponse( - title, - href, - this.name, - TvType.Anime, - poster, - null, - set, - ) - ) - } - } - - // "episodes-search", is used for finding movies, anime episodes should be filtered out - response = - app.post( - url, - headers = mapOf("Referer" to url), - data = mapOf("catara" to query, "konuara" to "episodes") - ).text - document = Jsoup.parse(response) - items = document.select("#catlist-listview2 > ul > li") - .filter { it -> it?.text() != null && !it.text().toString().contains("Episode") } - - for (item in items) { - val titleHeader = item.selectFirst("a") - val title = titleHeader!!.text() - val href = fixUrl(titleHeader.attr("href")) - //val isDubbed = title.contains("dubbed") - //val set: EnumSet = - // EnumSet.of(if (isDubbed) DubStatus.Dubbed else DubStatus.Subbed) - returnValue.add( - TvSeriesSearchResponse( - title, - href, - this.name, - TvType.AnimeMovie, - null, - null, - null, - ) - ) - } - - return returnValue - } - - override suspend fun load(url: String): LoadResponse { - val isMovie = !url.contains("/anime/") - val response = app.get(url).text - val document = Jsoup.parse(response) - - return if (!isMovie) { - val title = document.selectFirst("td.vsbaslik > h2")!!.text() - val poster = fixUrlNull(document.selectFirst("div#cat-img-desc > div > img")?.attr("src")) - val plot = document.selectFirst("div.iltext")!!.text() - val genres = document.select("div#cat-genre > div.wcobtn > a").map { it.text() } - val episodes = document.select("div#catlist-listview > ul > li > a").reversed().map { - val text = it.text() - val match = Regex("Season ([0-9]*) Episode ([0-9]*).*? (.*)").find(text) - val href = it.attr("href") - if (match != null) { - val last = match.groupValues[3] - return@map Episode( - href, - if (last.startsWith("English")) null else last, - match.groupValues[1].toIntOrNull(), - match.groupValues[2].toIntOrNull(), - ) - } - val match2 = Regex("Episode ([0-9]*).*? (.*)").find(text) - if (match2 != null) { - val last = match2.groupValues[2] - return@map Episode( - href, - if (last.startsWith("English")) null else last, - null, - match2.groupValues[1].toIntOrNull(), - ) - } - return@map Episode( - href, - text - ) - } - TvSeriesLoadResponse( - title, - url, - this.name, - TvType.TvSeries, - episodes, - poster, - null, - plot, - null, - null, - tags = genres - ) - } else { - val title = document.selectFirst(".iltext .Apple-style-span")?.text().toString() - val b = document.select(".iltext b") - val description = if (b.isNotEmpty()) { - b.last()!!.html().split("
    ")[0] - } else null - - TvSeriesLoadResponse( - title, - url, - this.name, - TvType.TvSeries, - listOf(Episode(url,title)), - null, - null, - description, - null, - null - ) - } - } - - data class LinkResponse( - // @JsonProperty("cdn") - // val cdn: String, - @JsonProperty("enc") - val enc: String, - @JsonProperty("hd") - val hd: String, - @JsonProperty("server") - val server: String, - ) - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val response = app.get(data).text - /*val embedUrl = fixUrl( - Regex("itemprop=\"embedURL\" content=\"(.*?)\"").find(response.text)?.groupValues?.get(1) ?: return false - )*/ - val start = response.indexOf("itemprop=\"embedURL") - val foundJS = Regex("").find(response, start)?.groupValues?.get(1) - ?.replace("document.write", "var returnValue = ") - - val rhino = Context.enter() - rhino.initStandardObjects() - rhino.optimizationLevel = -1 - val scope: Scriptable = rhino.initStandardObjects() - - 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" + - "};" - - rhino.evaluateString(scope, decodeBase64 + foundJS, "JavaScript", 1, null) - val jsEval = scope.get("returnValue", scope) ?: return false - val src = fixUrl(Regex("src=\"(.*?)\"").find(jsEval as String)?.groupValues?.get(1) ?: return false) - - val embedResponse = app.get( - (src), - headers = mapOf("Referer" to data) - ) - - val getVidLink = fixUrl( - Regex("get\\(\"(.*?)\"").find(embedResponse.text)?.groupValues?.get(1) ?: return false - ) - val linkResponse = app.get( - getVidLink, headers = mapOf( - "sec-ch-ua" to "\"Chromium\";v=\"91\", \" Not;A Brand\";v=\"99\"", - "sec-ch-ua-mobile" to "?0", - "sec-fetch-dest" to "empty", - "sec-fetch-mode" to "cors", - "sec-fetch-site" to "same-origin", - "accept" to "*/*", - "x-requested-with" to "XMLHttpRequest", - "referer" to src.replace(" ", "%20"), - "user-agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", - "cookie" to "countrytabs=0" - ) - ) - - val link = parseJson(linkResponse.text) - - val hdLink = "${link.server}/getvid?evid=${link.hd}" - val sdLink = "${link.server}/getvid?evid=${link.enc}" - - if (link.hd.isNotBlank()) - callback.invoke( - ExtractorLink( - this.name, - this.name + " HD", - hdLink, - "", - Qualities.P720.value - ) - ) - - if (link.enc.isNotBlank()) - callback.invoke( - ExtractorLink( - this.name, - this.name + " SD", - sdLink, - "", - Qualities.P480.value - ) - ) - - return true - } -} diff --git a/WatchCartoonOnlineProvider/src/main/kotlin/com/lagradost/WatchCartoonOnlineProviderPlugin.kt b/WatchCartoonOnlineProvider/src/main/kotlin/com/lagradost/WatchCartoonOnlineProviderPlugin.kt deleted file mode 100644 index 60d5758..0000000 --- a/WatchCartoonOnlineProvider/src/main/kotlin/com/lagradost/WatchCartoonOnlineProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class WatchCartoonOnlineProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(WatchCartoonOnlineProvider()) - } -} \ No newline at end of file diff --git a/WcofunProvider/build.gradle.kts b/WcofunProvider/build.gradle.kts deleted file mode 100644 index 8d78944..0000000 --- a/WcofunProvider/build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // 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.wcofun.com&sz=24" -} \ No newline at end of file diff --git a/WcofunProvider/src/main/AndroidManifest.xml b/WcofunProvider/src/main/AndroidManifest.xml deleted file mode 100644 index 29aec9d..0000000 --- a/WcofunProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/WcofunProvider/src/main/kotlin/com/lagradost/WcofunProvider.kt b/WcofunProvider/src/main/kotlin/com/lagradost/WcofunProvider.kt deleted file mode 100644 index b89bdcf..0000000 --- a/WcofunProvider/src/main/kotlin/com/lagradost/WcofunProvider.kt +++ /dev/null @@ -1,172 +0,0 @@ -package com.lagradost - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import org.jsoup.Jsoup -import org.jsoup.nodes.Element - -class WcofunProvider : MainAPI() { - override var mainUrl = "https://www.wcofun.com" - override var name = "WCO Fun" - override val hasMainPage = true - override val hasDownloadSupport = true - - override val supportedTypes = setOf( - TvType.Anime, - TvType.AnimeMovie, - TvType.OVA - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get(mainUrl).document - - val homePageList = ArrayList() - - document.select("div#sidebar_right,div#sidebar_right2").forEach { block -> - val header = block.previousElementSibling()?.ownText() ?: return@forEach - val animes = block.select("ul.items li").mapNotNull { - it.toSearchResult() - } - if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes)) - } - - return HomePageResponse(homePageList) - - } - - private fun getProperAnimeLink(uri: String): String { - return if (uri.contains("/anime/")) { - uri - } else { - var title = uri.substringAfter("$mainUrl/") - title = when { - (title.contains(Regex("-season-[0-9]+-episode"))) && title.contains("-dubbed") -> title.substringBefore("-season") - (title.contains(Regex("-season-[0-9]+-episode"))) && title.contains("-subbed") -> title.replace(Regex("-season-[0-9]+-episode-[0-9]+"), "") - title.contains("-subbed") -> title.replace(Regex("-episode-[0-9]+"), "") - title.contains("-dubbed") -> title.substringBefore("-episode") - else -> title - } - "$mainUrl/anime/$title" - } - } - - private fun Element.toSearchResult(): AnimeSearchResponse? { - val header = this.selectFirst("div.recent-release-episodes a")?.text() - val title = header?.trim() ?: return null - val href = getProperAnimeLink(this.selectFirst("a")!!.attr("href")) - val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src")) - val epNum = header.let { eps -> - Regex("Episode\\s?([0-9]+)").find(eps)?.groupValues?.getOrNull(1)?.toIntOrNull() - } - val isDub = header.contains("Dubbed") - val isSub = header.contains("Subbed") - return newAnimeSearchResponse(title, href, TvType.Anime) { - this.posterUrl = posterUrl - addDubStatus(isDub, isSub, epNum, epNum) - } - } - - override suspend fun search(query: String): List { - val document = app.post( - "$mainUrl/search", - referer = mainUrl, - data = mapOf("catara" to query, "konuara" to "series") - ).document - - return document.select("div#sidebar_right2 li").mapNotNull { - it.toSearchResult() - } - } - - override suspend fun load(url: String): LoadResponse? { - val document = app.get(url).document - val title = document.selectFirst("div.h1-tag a")?.text() ?: return null - val eps = document.select("div#sidebar_right3 div.cat-eps") - val type = if (eps.size == 1 || eps.first()?.text() - ?.contains(Regex("Episode\\s?[0-9]+")) != true - ) TvType.AnimeMovie else TvType.Anime - val episodes = eps.map { - val name = it.select("a").text() - val link = it.selectFirst("a")!!.attr("href") - Episode(link, name = name) - }.reversed() - - return newAnimeLoadResponse(title, url, type) { - posterUrl = fixUrlNull(document.selectFirst("img.img5")?.attr("src")) - addEpisodes(DubStatus.Subbed, episodes) - plot = document.select("div#sidebar_cat > p").text() - this.tags = document.select("div#sidebar_cat a").map { it.text() } - } - } - - private suspend fun getIframe(url: String): String? { - val document = app.get(url).document - val scriptData = - document.select("script").find { it.data().contains("= \"\";") }?.data() ?: return null - val subtractionNumber = - Regex("""(?<=\.replace\(/\\D/g,''\)\) - )\d+""").find(scriptData)?.value?.toInt() - ?: return null - val html = Regex("""(?<=\["|, ").+?(?=")""").findAll(scriptData).map { - val number = base64Decode(it.value).replace(Regex("\\D"), "").toInt() - (number - subtractionNumber).toChar() - }.joinToString("") - return Jsoup.parse(html).select("iframe").attr("src").let { fixUrl(it) } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - getIframe(data)?.let { iframe -> - val link = app.get(iframe, referer = data).text.let { - fixUrlNull( - Regex("\"(/inc/embed/getvidlink.php.*)\"").find(it)?.groupValues?.getOrNull( - 1 - ) - ) - } - app.get( - link ?: return@let, - referer = iframe, - headers = mapOf("x-requested-with" to "XMLHttpRequest") - ).parsedSafe()?.let { - listOf( - Pair(it.hd, "HD"), - Pair(it.enc, "SD") - ).map { source -> - suspendSafeApiCall { - callback.invoke( - ExtractorLink( - "${this.name} ${source.second}", - "${this.name} ${source.second}", - "${it.server}/getvid?evid=${source.first}", - mainUrl, - if (source.second == "HD") Qualities.P720.value else Qualities.P480.value - ) - ) - } - } - } - } - - return true - } - - data class Sources( - @JsonProperty("enc") val enc: String?, - @JsonProperty("server") val server: String?, - @JsonProperty("cdn") val cdn: String?, - @JsonProperty("hd") val hd: String?, - ) - - -} \ No newline at end of file diff --git a/WcofunProvider/src/main/kotlin/com/lagradost/WcofunProviderPlugin.kt b/WcofunProvider/src/main/kotlin/com/lagradost/WcofunProviderPlugin.kt deleted file mode 100644 index 993b06d..0000000 --- a/WcofunProvider/src/main/kotlin/com/lagradost/WcofunProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.lagradost - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class WcofunProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(WcofunProvider()) - } -} \ No newline at end of file