From b0905c3c68d50a7a606729dbe76a2ac393989e88 Mon Sep 17 00:00:00 2001 From: tuan041 <30403510+tuan041@users.noreply.github.com> Date: Sun, 4 Sep 2022 20:26:07 +0700 Subject: [PATCH 01/17] Update PhimmoichillProvider.kt (#14) --- .../kotlin/com/lagradost/PhimmoichillProvider.kt | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/PhimmoichillProvider/src/main/kotlin/com/lagradost/PhimmoichillProvider.kt b/PhimmoichillProvider/src/main/kotlin/com/lagradost/PhimmoichillProvider.kt index 1820ec8..1f53fc7 100644 --- a/PhimmoichillProvider/src/main/kotlin/com/lagradost/PhimmoichillProvider.kt +++ b/PhimmoichillProvider/src/main/kotlin/com/lagradost/PhimmoichillProvider.kt @@ -96,19 +96,9 @@ class PhimmoichillProvider : MainAPI() { val rating = document.select("ul.entry-meta.block-film li:nth-child(7) span").text().toRatingInt() val actors = document.select("ul.entry-meta.block-film li:last-child a").map { it.text() } - val recommendations = document.select("ul#list-film-realted li.item").mapNotNull { - val titleHeader = it.select("p") ?: return@mapNotNull null - val recUrl = titleHeader.attr("href") ?: return@mapNotNull null - val recTitle = titleHeader.text() ?: return@mapNotNull null - val poster = it.select("img").attr("src") - MovieSearchResponse( - recTitle, - recUrl, - this.name, - TvType.Movie, - poster - ) - } + val recommendations = document.select("ul#list-film-realted li.item").map { + it.toSearchResult() + } return if (tvType == TvType.TvSeries) { val docEpisodes = app.get(link).document From 0bfba1ba5d38819ff5655d6dbf6c046172696d66 Mon Sep 17 00:00:00 2001 From: Hexated <37908684+hexated@users.noreply.github.com> Date: Mon, 5 Sep 2022 06:13:58 +0700 Subject: [PATCH 02/17] added Anizm & Hdfilmcehennemi (Turkish) (#17) * added Anizm & Hdfilmcehennemi * changes Dramaid domain --- Anizm/build.gradle.kts | 27 +++ Anizm/src/main/AndroidManifest.xml | 2 + Anizm/src/main/kotlin/com/lagradost/Anizm.kt | 195 +++++++++++++++++ .../main/kotlin/com/lagradost/AnizmPlugin.kt | 14 ++ DramaidProvider/build.gradle.kts | 6 +- .../kotlin/com/lagradost/DramaidProvider.kt | 2 +- Hdfilmcehennemi/build.gradle.kts | 26 +++ Hdfilmcehennemi/src/main/AndroidManifest.xml | 2 + .../kotlin/com/lagradost/Hdfilmcehennemi.kt | 205 ++++++++++++++++++ .../com/lagradost/HdfilmcehennemiPlugin.kt | 14 ++ KuronimeProvider/build.gradle.kts | 4 +- .../kotlin/com/lagradost/KuronimeProvider.kt | 2 +- LayarKacaProvider/build.gradle.kts | 8 +- .../kotlin/com/lagradost/LayarKacaProvider.kt | 2 +- NontonAnimeIDProvider/build.gradle.kts | 4 +- .../com/lagradost/NontonAnimeIDProvider.kt | 2 +- 16 files changed, 500 insertions(+), 15 deletions(-) create mode 100644 Anizm/build.gradle.kts create mode 100644 Anizm/src/main/AndroidManifest.xml create mode 100644 Anizm/src/main/kotlin/com/lagradost/Anizm.kt create mode 100644 Anizm/src/main/kotlin/com/lagradost/AnizmPlugin.kt create mode 100644 Hdfilmcehennemi/build.gradle.kts create mode 100644 Hdfilmcehennemi/src/main/AndroidManifest.xml create mode 100644 Hdfilmcehennemi/src/main/kotlin/com/lagradost/Hdfilmcehennemi.kt create mode 100644 Hdfilmcehennemi/src/main/kotlin/com/lagradost/HdfilmcehennemiPlugin.kt diff --git a/Anizm/build.gradle.kts b/Anizm/build.gradle.kts new file mode 100644 index 0000000..6c69805 --- /dev/null +++ b/Anizm/build.gradle.kts @@ -0,0 +1,27 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "tr" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + authors = listOf("Hexated") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "AnimeMovie", + "Anime", + "OVA", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=anizm.net&sz=%size%" +} \ No newline at end of file diff --git a/Anizm/src/main/AndroidManifest.xml b/Anizm/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/Anizm/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/Anizm/src/main/kotlin/com/lagradost/Anizm.kt b/Anizm/src/main/kotlin/com/lagradost/Anizm.kt new file mode 100644 index 0000000..eefabe2 --- /dev/null +++ b/Anizm/src/main/kotlin/com/lagradost/Anizm.kt @@ -0,0 +1,195 @@ +package com.lagradost + +import android.util.Log +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer +import com.lagradost.cloudstream3.mvvm.safeApiCall +import com.lagradost.cloudstream3.utils.* +import org.jsoup.Jsoup +import org.jsoup.nodes.Element + + +class Anizm : MainAPI() { + override var mainUrl = "https://anizm.net" + override var name = "Anizm" + override val hasMainPage = true + override var lang = "tr" + override val hasDownloadSupport = true + + override val supportedTypes = setOf( + TvType.Anime, + TvType.AnimeMovie, + TvType.OVA + ) + + companion object { + private const val mainServer = "https://anizmplayer.com" + } + + override val mainPage = mainPageOf( + "$mainUrl/anime-izle?sayfa=" to "Son Eklenen Animeler", + ) + + override suspend fun getMainPage( + page: Int, + request: MainPageRequest + ): HomePageResponse { + val document = app.get(request.data + page).document + val home = document.select("div.restrictedWidth div#episodesMiddle").mapNotNull { + it.toSearchResult() + } + return newHomePageResponse(request.name, home) + } + + private fun getProperAnimeLink(uri: String): String { + return if (uri.contains("-bolum")) { + "$mainUrl/${uri.substringAfter("$mainUrl/").replace(Regex("-[0-9]+-bolum.*"), "")}" + } else { + uri + } + } + + private fun Element.toSearchResult(): AnimeSearchResponse? { + val href = getProperAnimeLink(this.selectFirst("a")!!.attr("href")) + val title = this.selectFirst("div.title, h5.animeTitle a")?.text() ?: return null + val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src")) + val episode = this.selectFirst("div.truncateText")?.text()?.let { + Regex("([0-9]+).\\s?Bölüm").find(it)?.groupValues?.getOrNull(1)?.toIntOrNull() + } + + return newAnimeSearchResponse(title, href, TvType.Anime) { + this.posterUrl = posterUrl + addSub(episode) + } + } + + override suspend fun search(query: String): List { + val document = app.get( + "$mainUrl/fullViewSearch?search=$query&skip=0", + headers = mapOf("X-Requested-With" to "XMLHttpRequest") + ).document + + return document.select("div.searchResultItem").mapNotNull { + it.toSearchResult() + } + } + + override suspend fun load(url: String): LoadResponse { + val document = app.get(url).document + + val title = document.selectFirst("h2.anizm_pageTitle a")!!.text().trim() + val type = + if (document.select("div.ui.grid div.four.wide").size == 1) TvType.Movie else TvType.Anime + val trailer = document.select("div.yt-hd-thumbnail-inner-container iframe").attr("src") + val episodes = document.select("div.ui.grid div.four.wide").map { + val name = it.select("div.episodeBlock").text() + val link = fixUrl(it.selectFirst("a")?.attr("href").toString()) + Episode(link, name) + } + return newAnimeLoadResponse(title, url, type) { + posterUrl = fixUrlNull(document.selectFirst("div.infoPosterImg > img")?.attr("src")) + this.year = document.select("div.infoSta ul li:first-child").text().trim().toIntOrNull() + addEpisodes(DubStatus.Subbed, episodes) + plot = document.select("div.infoDesc").text().trim() + this.tags = document.select("span.dataValue span.ui.label").map { it.text() } + addTrailer(trailer) + } + } + + private suspend fun invokeLokalSource( + url: String, + translator: String, + sourceCallback: (ExtractorLink) -> Unit + ) { + app.get(url, referer = "$mainUrl/").document.select("script").find { script -> + script.data().contains("eval(function(p,a,c,k,e,d)") + }?.let { + val key = getAndUnpack(it.data()).substringAfter("FirePlayer(\"").substringBefore("\",") + val referer = "$mainServer/video/$key" + val link = "$mainServer/player/index.php?data=$key&do=getVideo" + Log.i("hexated", link) + app.post( + link, + data = mapOf("hash" to key, "r" to "$mainUrl/"), + referer = referer, + headers = mapOf( + "Accept" to "*/*", + "Origin" to mainServer, + "Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8", + "X-Requested-With" to "XMLHttpRequest" + ) + ).parsedSafe()?.videoSource?.let { m3uLink -> + M3u8Helper.generateM3u8( + "${this.name} ($translator)", + m3uLink, + referer + ).forEach(sourceCallback) + } + } + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + val document = app.get(data).document + document.select("div.episodeTranslators div#fansec").map { + Pair(it.select("a").attr("translator"), it.select("div.title").text()) + }.apmap { (url, translator) -> + safeApiCall { + app.get( + url, + referer = data, + headers = mapOf( + "Accept" to "application/json, text/javascript, */*; q=0.01", + "X-Requested-With" to "XMLHttpRequest" + ) + ).parsedSafe()?.data?.let { + Jsoup.parse(it).select("a").apmap { video -> + app.get( + video.attr("video"), + referer = data, + headers = mapOf( + "Accept" to "application/json, text/javascript, */*; q=0.01", + "X-Requested-With" to "XMLHttpRequest" + ) + ).parsedSafe()?.player?.let { iframe -> + Jsoup.parse(iframe).select("iframe").attr("src").let { link -> + when { + link.startsWith(mainServer) -> { + invokeLokalSource(link, translator, callback) + } + else -> { + loadExtractor( + fixUrl(link), + "$mainUrl/", + subtitleCallback, + callback + ) + } + } + } + } + } + } + } + } + return true + } + + data class Source( + @JsonProperty("videoSource") val videoSource: String?, + ) + + data class Videos( + @JsonProperty("player") val player: String?, + ) + + data class Translators( + @JsonProperty("data") val data: String?, + ) + +} \ No newline at end of file diff --git a/Anizm/src/main/kotlin/com/lagradost/AnizmPlugin.kt b/Anizm/src/main/kotlin/com/lagradost/AnizmPlugin.kt new file mode 100644 index 0000000..8080247 --- /dev/null +++ b/Anizm/src/main/kotlin/com/lagradost/AnizmPlugin.kt @@ -0,0 +1,14 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class AnizmPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(Anizm()) + } +} \ No newline at end of file diff --git a/DramaidProvider/build.gradle.kts b/DramaidProvider/build.gradle.kts index 3fc2c3a..c217391 100644 --- a/DramaidProvider/build.gradle.kts +++ b/DramaidProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 1 +version = 2 cloudstream { @@ -7,7 +7,7 @@ cloudstream { // All of these properties are optional, you can safely remove them // description = "Lorem Ipsum" - // authors = listOf("Cloudburst") + authors = listOf("Hexated") /** * Status int as the following: @@ -22,5 +22,5 @@ cloudstream { "Movie", ) - iconUrl = "https://www.google.com/s2/favicons?domain=185.224.83.103&sz=%size%" + iconUrl = "https://www.google.com/s2/favicons?domain=dramaid.asia&sz=%size%" } \ No newline at end of file diff --git a/DramaidProvider/src/main/kotlin/com/lagradost/DramaidProvider.kt b/DramaidProvider/src/main/kotlin/com/lagradost/DramaidProvider.kt index 318a5ca..8dcf2b7 100644 --- a/DramaidProvider/src/main/kotlin/com/lagradost/DramaidProvider.kt +++ b/DramaidProvider/src/main/kotlin/com/lagradost/DramaidProvider.kt @@ -10,7 +10,7 @@ import org.jsoup.Jsoup import org.jsoup.nodes.Element class DramaidProvider : MainAPI() { - override var mainUrl = "https://185.224.83.103" + override var mainUrl = "https://dramaid.asia" override var name = "DramaId" override val hasQuickSearch = false override val hasMainPage = true diff --git a/Hdfilmcehennemi/build.gradle.kts b/Hdfilmcehennemi/build.gradle.kts new file mode 100644 index 0000000..c10c4e9 --- /dev/null +++ b/Hdfilmcehennemi/build.gradle.kts @@ -0,0 +1,26 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "tr" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + authors = listOf("Hexated") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "TvSeries", + "Movie", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=hdfilmcehennemi.live&sz=%size%" +} \ No newline at end of file diff --git a/Hdfilmcehennemi/src/main/AndroidManifest.xml b/Hdfilmcehennemi/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/Hdfilmcehennemi/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/Hdfilmcehennemi/src/main/kotlin/com/lagradost/Hdfilmcehennemi.kt b/Hdfilmcehennemi/src/main/kotlin/com/lagradost/Hdfilmcehennemi.kt new file mode 100644 index 0000000..2ff8264 --- /dev/null +++ b/Hdfilmcehennemi/src/main/kotlin/com/lagradost/Hdfilmcehennemi.kt @@ -0,0 +1,205 @@ +package com.lagradost + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.LoadResponse.Companion.addActors +import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer +import com.lagradost.cloudstream3.mvvm.safeApiCall +import com.lagradost.cloudstream3.utils.* +import org.jsoup.nodes.Element + +class Hdfilmcehennemi : MainAPI() { + override var mainUrl = "https://www.hdfilmcehennemi.live" + override var name = "hdfilmcehennemi" + override val hasMainPage = true + override var lang = "tr" + override val hasQuickSearch = true + override val hasDownloadSupport = true + override val supportedTypes = setOf( + TvType.Movie, + TvType.TvSeries, + ) + + override val mainPage = mainPageOf( + "$mainUrl/category/tavsiye-filmler-izle1/page/" to "Tavsiye Filmler Kategorisi", + "$mainUrl/yabancidizi/page/" to "Son Eklenen Yabancı Diziler", + "$mainUrl/imdb-7-puan-uzeri-filmler/page/" to "Imdb 7+ Filmler", + "$mainUrl/en-cok-yorumlananlar/page/" to "En Çok Yorumlananlar", + "$mainUrl/en-cok-begenilen-filmleri-izle/page/" to "En Çok Beğenilenler", + ) + + override suspend fun getMainPage( + page: Int, + request: MainPageRequest + ): HomePageResponse { + val document = app.get(request.data + page).document + val home = document.select("div.card-body div.row div.col-6.col-sm-3.poster-container") + .mapNotNull { + it.toSearchResult() + } + return newHomePageResponse(request.name, home) + } + + private fun Element.toSearchResult(): SearchResponse? { + val title = this.selectFirst("a")?.text() ?: return null + val href = fixUrlNull(this.selectFirst("a")?.attr("href")) ?: return null + val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("data-src")) + return newMovieSearchResponse(title, href, TvType.Movie) { + this.posterUrl = posterUrl + } + + } + + private fun Media.toSearchResponse(): SearchResponse? { + return newMovieSearchResponse( + title ?: return null, + "$mainUrl/$slugPrefix$slug", + TvType.TvSeries, + ) { + this.posterUrl = "$mainUrl/uploads/poster/$poster" + } + } + + override suspend fun quickSearch(query: String): List = search(query) + + override suspend fun search(query: String): List { + return app.post( + "$mainUrl/search/", + data = mapOf("query" to query), + referer = "$mainUrl/", + headers = mapOf( + "Accept" to "application/json, text/javascript, */*; q=0.01", + "X-Requested-With" to "XMLHttpRequest" + ) + ).parsedSafe()?.result?.mapNotNull { media -> + media.toSearchResponse() + } ?: throw ErrorLoadingException("Invalid Json reponse") + } + + override suspend fun load(url: String): LoadResponse? { + val document = app.get(url).document + + val title = document.selectFirst("div.card-header > h1, div.card-header > h2")?.text() + ?: return null + val poster = fixUrlNull(document.selectFirst("img.img-fluid")?.attr("src")) + val tags = document.select("div.mb-0.lh-lg div:nth-child(5) a").map { it.text() } + val year = + document.selectFirst("div.mb-0.lh-lg div:nth-child(4) a")?.text()?.trim()?.toIntOrNull() + val tvType = if (document.select("nav#seasonsTabs").isNullOrEmpty() + ) TvType.Movie else TvType.TvSeries + val description = document.selectFirst("article.text-white > p")?.text()?.trim() + val rating = document.selectFirst("div.rating-votes div.rate span")?.text()?.toRatingInt() + val actors = document.select("div.mb-0.lh-lg div:last-child a.chip").map { + Actor(it.text(), it.select("img").attr("src")) + } + val recommendations = + document.select("div.swiper-wrapper div.poster.poster-pop").mapNotNull { + val recName = it.selectFirst("h2.title")?.text() ?: return@mapNotNull null + val recHref = + fixUrlNull(it.selectFirst("a")?.attr("href")) ?: return@mapNotNull null + val recPosterUrl = fixUrlNull(it.selectFirst("img")?.attr("data-src")) + newTvSeriesSearchResponse(recName, recHref, TvType.TvSeries) { + this.posterUrl = recPosterUrl + } + } + + return if (tvType == TvType.TvSeries) { + val trailer = + document.selectFirst("button.btn.btn-fragman.btn-danger")?.attr("data-trailer") + ?.let { + "https://www.youtube.com/embed/$it" + } + val episodes = document.select("div#seasonsTabs-tabContent div.card-list-item").map { + val href = it.select("a").attr("href") + val name = it.select("h3").text().trim() + val episode = it.select("h3").text().let { num -> + Regex("Sezon\\s?([0-9]+).").find(num)?.groupValues?.getOrNull(1)?.toIntOrNull() + } + val season = it.parents()[1].attr("id").substringAfter("-").toIntOrNull() + Episode( + href, + name, + season, + episode, + ) + } + newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) { + this.posterUrl = poster + this.year = year + this.plot = description + this.tags = tags + this.rating = rating + addActors(actors) + this.recommendations = recommendations + addTrailer(trailer) + } + } else { + val trailer = + document.selectFirst("nav.nav.card-nav.nav-slider a[data-bs-toggle=\"modal\"]") + ?.attr("data-trailer")?.let { + "https://www.youtube.com/embed/$it" + } + newMovieLoadResponse(title, url, TvType.Movie, url) { + this.posterUrl = poster + this.year = year + this.plot = description + this.tags = tags + this.rating = rating + addActors(actors) + this.recommendations = recommendations + addTrailer(trailer) + } + } + } + + private suspend fun invokeLocalSource( + source: String, + url: String, + sourceCallback: (ExtractorLink) -> Unit + ) { + val m3uLink = + app.get(url, referer = "$mainUrl/").document.select("script") + .find { + it.data().contains("var sources = [];") || it.data() + .contains("playerInstance =") + }?.data() + ?.substringAfter("[{file:\"")?.substringBefore("\"}]") ?: return + + M3u8Helper.generateM3u8( + source, + m3uLink, + if (url.startsWith(mainUrl)) "$mainUrl/" else "https://vidmoly.to/" + ).forEach(sourceCallback) + + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + app.get(data).document.select("nav.nav.card-nav.nav-slider a.nav-link").map { + Pair(it.attr("href"), it.text()) + }.apmap { (url, source) -> + safeApiCall { + app.get(url).document.select("div.card-video > iframe").attr("data-src") + .let { link -> + invokeLocalSource(source, link, callback) + } + } + } + return true + } + + data class Result( + @JsonProperty("result") val result: ArrayList? = arrayListOf(), + ) + + data class Media( + @JsonProperty("title") val title: String? = null, + @JsonProperty("poster") val poster: String? = null, + @JsonProperty("slug") val slug: String? = null, + @JsonProperty("slug_prefix") val slugPrefix: String? = null, + ) +} \ No newline at end of file diff --git a/Hdfilmcehennemi/src/main/kotlin/com/lagradost/HdfilmcehennemiPlugin.kt b/Hdfilmcehennemi/src/main/kotlin/com/lagradost/HdfilmcehennemiPlugin.kt new file mode 100644 index 0000000..bdee03b --- /dev/null +++ b/Hdfilmcehennemi/src/main/kotlin/com/lagradost/HdfilmcehennemiPlugin.kt @@ -0,0 +1,14 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class HdfilmcehennemiPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(Hdfilmcehennemi()) + } +} \ No newline at end of file diff --git a/KuronimeProvider/build.gradle.kts b/KuronimeProvider/build.gradle.kts index f4ad752..51f4655 100644 --- a/KuronimeProvider/build.gradle.kts +++ b/KuronimeProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 1 +version = 2 cloudstream { @@ -7,7 +7,7 @@ cloudstream { // All of these properties are optional, you can safely remove them // description = "Lorem Ipsum" - // authors = listOf("Cloudburst") + authors = listOf("Hexated") /** * Status int as the following: diff --git a/KuronimeProvider/src/main/kotlin/com/lagradost/KuronimeProvider.kt b/KuronimeProvider/src/main/kotlin/com/lagradost/KuronimeProvider.kt index 5f68ec6..9b22dee 100644 --- a/KuronimeProvider/src/main/kotlin/com/lagradost/KuronimeProvider.kt +++ b/KuronimeProvider/src/main/kotlin/com/lagradost/KuronimeProvider.kt @@ -111,7 +111,7 @@ class KuronimeProvider : MainAPI() { val type = getType( document.selectFirst(".infodetail > ul > li:nth-child(7)")?.ownText()?.trim().toString() ) - val trailer = document.selectFirst("div.tply iframe")?.attr("data-lazy-src") + val trailer = document.selectFirst("div.tply iframe")?.attr("data-src") val year = Regex("\\d, ([0-9]*)").find( document.select(".infodetail > ul > li:nth-child(5)").text() )?.groupValues?.get(1)?.toIntOrNull() diff --git a/LayarKacaProvider/build.gradle.kts b/LayarKacaProvider/build.gradle.kts index aaf7e01..27c96ea 100644 --- a/LayarKacaProvider/build.gradle.kts +++ b/LayarKacaProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 1 +version = 2 cloudstream { @@ -7,7 +7,7 @@ cloudstream { // All of these properties are optional, you can safely remove them // description = "Lorem Ipsum" - // authors = listOf("Cloudburst") + authors = listOf("Hexated") /** * Status int as the following: @@ -23,5 +23,5 @@ cloudstream { "Movie", ) - - } \ No newline at end of file + iconUrl = "https://www.google.com/s2/favicons?domain=lk21.homes&sz=%size%" +} \ No newline at end of file diff --git a/LayarKacaProvider/src/main/kotlin/com/lagradost/LayarKacaProvider.kt b/LayarKacaProvider/src/main/kotlin/com/lagradost/LayarKacaProvider.kt index cd31c85..2d72c22 100644 --- a/LayarKacaProvider/src/main/kotlin/com/lagradost/LayarKacaProvider.kt +++ b/LayarKacaProvider/src/main/kotlin/com/lagradost/LayarKacaProvider.kt @@ -9,7 +9,7 @@ import org.jsoup.Jsoup import org.jsoup.nodes.Element class LayarKacaProvider : MainAPI() { - override var mainUrl = "https://lk21.xn--6frz82g" + override var mainUrl = "https://lk21.homes" override var name = "LayarKaca" override val hasMainPage = true override var lang = "id" diff --git a/NontonAnimeIDProvider/build.gradle.kts b/NontonAnimeIDProvider/build.gradle.kts index 233b380..97b31f7 100644 --- a/NontonAnimeIDProvider/build.gradle.kts +++ b/NontonAnimeIDProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 1 +version = 2 cloudstream { @@ -7,7 +7,7 @@ cloudstream { // All of these properties are optional, you can safely remove them // description = "Lorem Ipsum" - // authors = listOf("Cloudburst") + authors = listOf("Hexated") /** * Status int as the following: diff --git a/NontonAnimeIDProvider/src/main/kotlin/com/lagradost/NontonAnimeIDProvider.kt b/NontonAnimeIDProvider/src/main/kotlin/com/lagradost/NontonAnimeIDProvider.kt index bc75ac2..f73b932 100644 --- a/NontonAnimeIDProvider/src/main/kotlin/com/lagradost/NontonAnimeIDProvider.kt +++ b/NontonAnimeIDProvider/src/main/kotlin/com/lagradost/NontonAnimeIDProvider.kt @@ -157,7 +157,7 @@ class NontonAnimeIDProvider : MainAPI() { val type = getType(document.select("span.typeseries").text().trim()) val rating = document.select("span.nilaiseries").text().trim().toIntOrNull() val description = document.select(".entry-content.seriesdesc > p").text().trim() - val trailer = document.selectFirst("iframe#traileryt")?.attr("data-src") + val trailer = document.selectFirst("a.trailerbutton")?.attr("href") val episodes = if (document.select("button.buttfilter").isNotEmpty()) { val id = document.select("input[name=series_id]").attr("value") From 4a739d07bf86f1e8281dd2da9cff75b44e9605e3 Mon Sep 17 00:00:00 2001 From: Hexated <37908684+hexated@users.noreply.github.com> Date: Sat, 10 Sep 2022 04:02:06 +0700 Subject: [PATCH 03/17] moved some providers (#19) --- settings.gradle.kts | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 72ef97b..bfedf27 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -5,7 +5,34 @@ rootProject.name = "CloudstreamPlugins" // Plugins are included like this val disabled = listOf( - "EgyBestProvider", "FaselHDProvider", "AkwamProvider", "MyCimaProvider" + "EgyBestProvider", + "FaselHDProvider", + "AkwamProvider", + "MyCimaProvider", + "AnimeIndoProvider", + "AnimeSailProvider", + "Anizm", + "DramaidProvider", + "DubokuProvider", + "Gomunimeis", + "GomunimeProvider", + "Hdfilmcehennemi", + "HDrezkaProvider", + "IdlixProvider", + "KuramanimeProvider", + "KuronimeProvider", + "LayarKacaProvider", + "MultiplexProvider", + "NeonimeProvider", + "NontonAnimeIDProvider", + "OploverzProvider", + "OtakudesuProvider", + "PhimmoichillProvider", + "RebahinProvider", + "TocanimeProvider", + "UakinoProvider", + "UseeTv", + "YomoviesProvider" ) File(rootDir, ".").eachDir { dir -> From 5d025ac2f67401a17cf5f948f08967aa18e21d09 Mon Sep 17 00:00:00 2001 From: Fixer Date: Sat, 10 Sep 2022 01:36:33 +0200 Subject: [PATCH 04/17] Update StreamingcommunityProvider.kt --- .../src/main/kotlin/com/lagradost/StreamingcommunityProvider.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/StreamingcommunityProvider/src/main/kotlin/com/lagradost/StreamingcommunityProvider.kt b/StreamingcommunityProvider/src/main/kotlin/com/lagradost/StreamingcommunityProvider.kt index e1c0f6a..8ce80f5 100644 --- a/StreamingcommunityProvider/src/main/kotlin/com/lagradost/StreamingcommunityProvider.kt +++ b/StreamingcommunityProvider/src/main/kotlin/com/lagradost/StreamingcommunityProvider.kt @@ -128,7 +128,7 @@ data class TrailerElement( class StreamingcommunityProvider : MainAPI() { override var lang = "it" - override var mainUrl = "https://streamingcommunity.agency" + override var mainUrl = "https://streamingcommunity.blog" override var name = "Streamingcommunity" override val hasMainPage = true override val hasChromecastSupport = true From 1704197c9e6c1df84c55b5290fb8fb269c280557 Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Sun, 11 Sep 2022 01:02:01 +0200 Subject: [PATCH 05/17] Update HDMovie5 domain --- HDMovie5/build.gradle.kts | 4 ++-- HDMovie5/src/main/kotlin/com/lagradost/HDMovie5.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/HDMovie5/build.gradle.kts b/HDMovie5/build.gradle.kts index 7b872b7..13af911 100644 --- a/HDMovie5/build.gradle.kts +++ b/HDMovie5/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 2 +version = 3 cloudstream { @@ -22,5 +22,5 @@ cloudstream { "Movie", ) - iconUrl = "https://www.google.com/s2/favicons?domain=hdmovie2.plus&sz=%size%" + iconUrl = "https://www.google.com/s2/favicons?domain=hdmovie2.run&sz=%size%" } \ No newline at end of file diff --git a/HDMovie5/src/main/kotlin/com/lagradost/HDMovie5.kt b/HDMovie5/src/main/kotlin/com/lagradost/HDMovie5.kt index e6e3929..0daf69b 100644 --- a/HDMovie5/src/main/kotlin/com/lagradost/HDMovie5.kt +++ b/HDMovie5/src/main/kotlin/com/lagradost/HDMovie5.kt @@ -9,7 +9,7 @@ import org.jsoup.Jsoup import org.jsoup.nodes.Element class HDMovie5 : MainAPI() { - override var mainUrl = "https://hdmovie2.plus" + override var mainUrl = "https://hdmovie2.run" override var name = "HDMovie" override var lang = "hi" From c5046346ea1c97c9bd37d0343dd36c230a431e03 Mon Sep 17 00:00:00 2001 From: uzairhaider502 <112128545+uzairhaider502@users.noreply.github.com> Date: Fri, 16 Sep 2022 14:19:52 +0500 Subject: [PATCH 06/17] Add Hindi movie link (#21) * fixed PhimmoichillProvider * final fixed sources * fixed Kuramanime * added HorizontalImages * fixes NontonAnimeIDProvider mainPage Co-authored-by: hexated --- KuramanimeProvider/build.gradle.kts | 4 +- .../com/lagradost/KuramanimeProvider.kt | 26 ++---- NontonAnimeIDProvider/build.gradle.kts | 2 +- .../com/lagradost/NontonAnimeIDProvider.kt | 14 +-- PhimmoichillProvider/build.gradle.kts | 4 +- .../com/lagradost/PhimmoichillProvider.kt | 89 ++++++------------- UseeTv/build.gradle.kts | 2 +- .../src/main/kotlin/com/lagradost/UseeTv.kt | 2 +- 8 files changed, 49 insertions(+), 94 deletions(-) diff --git a/KuramanimeProvider/build.gradle.kts b/KuramanimeProvider/build.gradle.kts index ff8359b..4c82120 100644 --- a/KuramanimeProvider/build.gradle.kts +++ b/KuramanimeProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 1 +version = 2 cloudstream { @@ -7,7 +7,7 @@ cloudstream { // All of these properties are optional, you can safely remove them // description = "Lorem Ipsum" - // authors = listOf("Cloudburst") + authors = listOf("Hexated") /** * Status int as the following: diff --git a/KuramanimeProvider/src/main/kotlin/com/lagradost/KuramanimeProvider.kt b/KuramanimeProvider/src/main/kotlin/com/lagradost/KuramanimeProvider.kt index 48da3b3..f3fc00e 100644 --- a/KuramanimeProvider/src/main/kotlin/com/lagradost/KuramanimeProvider.kt +++ b/KuramanimeProvider/src/main/kotlin/com/lagradost/KuramanimeProvider.kt @@ -21,12 +21,6 @@ class KuramanimeProvider : 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) { "Selesai Tayang" -> ShowStatus.Completed @@ -80,20 +74,11 @@ class KuramanimeProvider : MainAPI() { } override suspend fun search(query: String): List { - val link = "$mainUrl/anime?search=$query&order_by=oldest" + val link = "$mainUrl/anime?search=$query&order_by=latest" val document = app.get(link).document - return document.select(".product__item").mapNotNull { - val title = it.selectFirst("div.product__item__text > h5")!!.text().trim() - val poster = it.selectFirst("a > div")!!.attr("data-setbg") - val tvType = - getType(it.selectFirst(".product__item__text > ul > li")!!.text().toString()) - val href = fixUrl(it.selectFirst("a")!!.attr("href")) - - newAnimeSearchResponse(title, href, tvType) { - this.posterUrl = poster - addDubStatus(dubExist = false, subExist = true) - } + return document.select("div#animeList div.col-lg-4.col-md-6.col-sm-6").mapNotNull { + it.toSearchResult() } } @@ -164,7 +149,10 @@ class KuramanimeProvider : MainAPI() { name, url, referer = "$mainUrl/", - quality = quality + quality = quality, + headers = mapOf( + "Range" to "bytes=0-" + ) ) ) } diff --git a/NontonAnimeIDProvider/build.gradle.kts b/NontonAnimeIDProvider/build.gradle.kts index 97b31f7..3407d44 100644 --- a/NontonAnimeIDProvider/build.gradle.kts +++ b/NontonAnimeIDProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 2 +version = 3 cloudstream { diff --git a/NontonAnimeIDProvider/src/main/kotlin/com/lagradost/NontonAnimeIDProvider.kt b/NontonAnimeIDProvider/src/main/kotlin/com/lagradost/NontonAnimeIDProvider.kt index f73b932..a6310bb 100644 --- a/NontonAnimeIDProvider/src/main/kotlin/com/lagradost/NontonAnimeIDProvider.kt +++ b/NontonAnimeIDProvider/src/main/kotlin/com/lagradost/NontonAnimeIDProvider.kt @@ -47,15 +47,15 @@ class NontonAnimeIDProvider : MainAPI() { document.select("section#postbaru").forEach { block -> val header = block.selectFirst("h2")!!.text().trim() - val animes = block.select("article.animeseries").map { + val animes = block.select("article.animeseries").mapNotNull { it.toSearchResult() } if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes)) } - document.select("aside#sidebar_right > div:nth-child(4)").forEach { block -> + document.select("aside#sidebar_right > div.side").forEach { block -> val header = block.selectFirst("h3")!!.ownText().trim() - val animes = block.select("li.fullwdth").map { + val animes = block.select("ul li.fullwdth").mapNotNull { it.toSearchResultPopular() } if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes)) @@ -91,9 +91,9 @@ class NontonAnimeIDProvider : MainAPI() { } } - private fun Element.toSearchResult(): AnimeSearchResponse { + private fun Element.toSearchResult(): AnimeSearchResponse? { val href = getProperAnimeLink(fixUrl(this.selectFirst("a")!!.attr("href"))) - val title = this.selectFirst("h3.title")!!.text() + val title = this.selectFirst("h3.title")?.text() ?: return null val posterUrl = fixUrl(this.select("img").attr("data-src")) return newAnimeSearchResponse(title, href, TvType.Anime) { @@ -103,9 +103,9 @@ class NontonAnimeIDProvider : MainAPI() { } - private fun Element.toSearchResultPopular(): AnimeSearchResponse { + private fun Element.toSearchResultPopular(): AnimeSearchResponse? { val href = getProperAnimeLink(fixUrl(this.selectFirst("a")!!.attr("href"))) - val title = this.select("h4").text().trim() + val title = this.selectFirst("h4")?.text()?.trim() ?: return null val posterUrl = fixUrl(this.select("img").attr("data-src")) return newAnimeSearchResponse(title, href, TvType.Anime) { diff --git a/PhimmoichillProvider/build.gradle.kts b/PhimmoichillProvider/build.gradle.kts index 84621af..4eee292 100644 --- a/PhimmoichillProvider/build.gradle.kts +++ b/PhimmoichillProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 1 +version = 2 cloudstream { @@ -7,7 +7,7 @@ cloudstream { // All of these properties are optional, you can safely remove them // description = "Lorem Ipsum" - // authors = listOf("Cloudburst") + authors = listOf("Hexated") /** * Status int as the following: diff --git a/PhimmoichillProvider/src/main/kotlin/com/lagradost/PhimmoichillProvider.kt b/PhimmoichillProvider/src/main/kotlin/com/lagradost/PhimmoichillProvider.kt index 1f53fc7..a08b1b2 100644 --- a/PhimmoichillProvider/src/main/kotlin/com/lagradost/PhimmoichillProvider.kt +++ b/PhimmoichillProvider/src/main/kotlin/com/lagradost/PhimmoichillProvider.kt @@ -1,6 +1,5 @@ package com.lagradost -import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.LoadResponse.Companion.addActors import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer @@ -8,7 +7,6 @@ import com.lagradost.cloudstream3.mvvm.safeApiCall import com.lagradost.cloudstream3.utils.* import org.jsoup.nodes.Element import java.net.URLDecoder -import java.util.ArrayList class PhimmoichillProvider : MainAPI() { override var mainUrl = "https://phimmoichill.net" @@ -41,7 +39,14 @@ class PhimmoichillProvider : MainAPI() { val home = document.select("li.item").mapNotNull { it.toSearchResult() } - return newHomePageResponse(request.name, home) + return newHomePageResponse( + list = HomePageList( + name = request.name, + list = home, + isHorizontalImages = true + ), + hasNext = true + ) } private fun decode(input: String): String? = URLDecoder.decode(input, "utf-8") @@ -97,7 +102,9 @@ class PhimmoichillProvider : MainAPI() { document.select("ul.entry-meta.block-film li:nth-child(7) span").text().toRatingInt() val actors = document.select("ul.entry-meta.block-film li:last-child a").map { it.text() } val recommendations = document.select("ul#list-film-realted li.item").map { - it.toSearchResult() + it.toSearchResult().apply { + this.posterUrl = decode(it.selectFirst("img")!!.attr("data-src").substringAfter("url=")) + } } return if (tvType == TvType.TvSeries) { @@ -145,81 +152,41 @@ class PhimmoichillProvider : MainAPI() { ): Boolean { val document = app.get(data).document - val key = document.select("div#content script").mapNotNull { script -> - if (script.data().contains("filmInfo.episodeID =")) { - val id = script.data().substringAfter("filmInfo.episodeID = parseInt('") - .substringBefore("');") + val key = document.select("div#content script") + .find { it.data().contains("filmInfo.episodeID =") }?.data()?.let { script -> + val id = script.substringAfter("filmInfo.episodeID = parseInt('") app.post( // Not mainUrl url = "https://phimmoichills.net/pmplayer.php", - data = mapOf("qcao" to id), + data = mapOf("qcao" to id, "sv" to "0"), referer = data, headers = mapOf( "X-Requested-With" to "XMLHttpRequest", "Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8" ) - ).text.also { println("HERERERR $it") }.substringAfterLast("iniPlayers(\"").substringBefore("\",") - } else { - null + ).text.substringAfterLast("iniPlayers(\"") + .substringBefore("\",") } - }.first() listOf( - Pair("https://so-trym.topphimmoi.org/hlspm/$key", "PMFAST"), - Pair("https://dash.megacdn.xyz/hlspm/$key", "PMHLS"), + Pair("https://so-trym.topphimmoi.org/raw/$key/index.m3u8", "PMFAST"), + Pair("https://dash.megacdn.xyz/raw/$key/index.m3u8", "PMHLS"), Pair("https://dash.megacdn.xyz/dast/$key/index.m3u8", "PMBK") ).apmap { (link, source) -> safeApiCall { - if (source == "PMBK") { - callback.invoke( - ExtractorLink( - source, - source, - link, - referer = "$mainUrl/", - quality = Qualities.P1080.value, - isM3u8 = true - ) + callback.invoke( + ExtractorLink( + source, + source, + link, + referer = "$mainUrl/", + quality = Qualities.P1080.value, + isM3u8 = true, ) - } else { - val playList = app.get(link, referer = "$mainUrl/") - .parsedSafe()?.main?.segments?.map { segment -> - PlayListItem( - segment.link, - (segment.du.toFloat() * 1_000_000).toLong() - ) - } - - callback.invoke( - ExtractorLinkPlayList( - source, - source, - playList ?: return@safeApiCall, - referer = "$mainUrl/", - quality = Qualities.P1080.value, - headers = mapOf( -// "If-None-Match" to "*", - "Origin" to mainUrl, - ) - ) - ) - } + ) } } return true } - data class Segment( - @JsonProperty("du") val du: String, - @JsonProperty("link") val link: String, - ) - - data class DataM3u( - @JsonProperty("segments") val segments: List?, - ) - - data class ResponseM3u( - @JsonProperty("2048p") val main: DataM3u?, - ) - } diff --git a/UseeTv/build.gradle.kts b/UseeTv/build.gradle.kts index 31e67a1..9045169 100644 --- a/UseeTv/build.gradle.kts +++ b/UseeTv/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 1 +version = 2 cloudstream { diff --git a/UseeTv/src/main/kotlin/com/lagradost/UseeTv.kt b/UseeTv/src/main/kotlin/com/lagradost/UseeTv.kt index cbb7af9..6d7908a 100644 --- a/UseeTv/src/main/kotlin/com/lagradost/UseeTv.kt +++ b/UseeTv/src/main/kotlin/com/lagradost/UseeTv.kt @@ -32,7 +32,7 @@ class UseeTv : MainAPI() { }.mapNotNull { it.toSearchResult() } - HomePageList(name, home) + HomePageList(name, home, true) }.filter { it.list.isNotEmpty() } return HomePageResponse(home) From 51120bcbf40476cc12b8409f2f8675abe0501ad3 Mon Sep 17 00:00:00 2001 From: LagradOst <46196380+Blatzar@users.noreply.github.com> Date: Fri, 16 Sep 2022 19:05:13 +0000 Subject: [PATCH 07/17] Disable Xcine --- XcineProvider/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/XcineProvider/build.gradle.kts b/XcineProvider/build.gradle.kts index baa891d..3b64aed 100644 --- a/XcineProvider/build.gradle.kts +++ b/XcineProvider/build.gradle.kts @@ -16,7 +16,7 @@ cloudstream { * 2: Slow * 3: Beta only * */ - status = 1 // will be 3 if unspecified + status = 0 // will be 3 if unspecified tvTypes = listOf( "AnimeMovie", "Anime", @@ -25,4 +25,4 @@ cloudstream { ) iconUrl = "https://www.google.com/s2/favicons?domain=xcine.me&sz=%size%" -} \ No newline at end of file +} From f3e15903a08dc6404fb8fab8cb9c4d2f9da66483 Mon Sep 17 00:00:00 2001 From: Saksham Shekher <95137948+OshekharO@users.noreply.github.com> Date: Mon, 19 Sep 2022 13:56:09 +0530 Subject: [PATCH 08/17] Update HDMovie5.kt (#22) --- HDMovie5/src/main/kotlin/com/lagradost/HDMovie5.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HDMovie5/src/main/kotlin/com/lagradost/HDMovie5.kt b/HDMovie5/src/main/kotlin/com/lagradost/HDMovie5.kt index 0daf69b..15fa845 100644 --- a/HDMovie5/src/main/kotlin/com/lagradost/HDMovie5.kt +++ b/HDMovie5/src/main/kotlin/com/lagradost/HDMovie5.kt @@ -9,7 +9,7 @@ import org.jsoup.Jsoup import org.jsoup.nodes.Element class HDMovie5 : MainAPI() { - override var mainUrl = "https://hdmovie2.run" + override var mainUrl = "https://hdmovie2.bond" override var name = "HDMovie" override var lang = "hi" From 73d3cfc8b858dd60ffd6cf9a893a347980b0ef23 Mon Sep 17 00:00:00 2001 From: Saksham Shekher <95137948+OshekharO@users.noreply.github.com> Date: Wed, 21 Sep 2022 04:04:03 +0530 Subject: [PATCH 09/17] Update domain (#24) * Update HDMovie5.kt * Update YomoviesProvider.kt --- HDMovie5/src/main/kotlin/com/lagradost/HDMovie5.kt | 2 +- .../src/main/kotlin/com/lagradost/YomoviesProvider.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/HDMovie5/src/main/kotlin/com/lagradost/HDMovie5.kt b/HDMovie5/src/main/kotlin/com/lagradost/HDMovie5.kt index 15fa845..591db74 100644 --- a/HDMovie5/src/main/kotlin/com/lagradost/HDMovie5.kt +++ b/HDMovie5/src/main/kotlin/com/lagradost/HDMovie5.kt @@ -9,7 +9,7 @@ import org.jsoup.Jsoup import org.jsoup.nodes.Element class HDMovie5 : MainAPI() { - override var mainUrl = "https://hdmovie2.bond" + override var mainUrl = "https://hdmovie2.rip" override var name = "HDMovie" override var lang = "hi" diff --git a/YomoviesProvider/src/main/kotlin/com/lagradost/YomoviesProvider.kt b/YomoviesProvider/src/main/kotlin/com/lagradost/YomoviesProvider.kt index e206af8..212b5e5 100644 --- a/YomoviesProvider/src/main/kotlin/com/lagradost/YomoviesProvider.kt +++ b/YomoviesProvider/src/main/kotlin/com/lagradost/YomoviesProvider.kt @@ -9,7 +9,7 @@ import com.lagradost.cloudstream3.utils.loadExtractor import org.jsoup.nodes.Element class YomoviesProvider : MainAPI() { - override var mainUrl = "https://yomovies.cloud" + override var mainUrl = "https://yomovies.homes" override var name = "Yomovies" override val hasMainPage = true override var lang = "hi" From 02b807aa6c09221f9abd6b5444fc3b1ac30c1810 Mon Sep 17 00:00:00 2001 From: Fixer Date: Fri, 23 Sep 2022 20:07:14 +0200 Subject: [PATCH 10/17] Update StreamingcommunityProvider.kt (#25) --- .../src/main/kotlin/com/lagradost/StreamingcommunityProvider.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/StreamingcommunityProvider/src/main/kotlin/com/lagradost/StreamingcommunityProvider.kt b/StreamingcommunityProvider/src/main/kotlin/com/lagradost/StreamingcommunityProvider.kt index 8ce80f5..b3cc074 100644 --- a/StreamingcommunityProvider/src/main/kotlin/com/lagradost/StreamingcommunityProvider.kt +++ b/StreamingcommunityProvider/src/main/kotlin/com/lagradost/StreamingcommunityProvider.kt @@ -128,7 +128,7 @@ data class TrailerElement( class StreamingcommunityProvider : MainAPI() { override var lang = "it" - override var mainUrl = "https://streamingcommunity.blog" + override var mainUrl = "https://streamingcommunity.tech" override var name = "Streamingcommunity" override val hasMainPage = true override val hasChromecastSupport = true From 95dddf7e3413343429a70a759dfb71d5441df3c8 Mon Sep 17 00:00:00 2001 From: Memexurer Date: Sun, 25 Sep 2022 12:41:31 +0200 Subject: [PATCH 11/17] Add video type info to filman provider (#27) --- .../src/main/kotlin/com/lagradost/FilmanProvider.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/FilmanProvider/src/main/kotlin/com/lagradost/FilmanProvider.kt b/FilmanProvider/src/main/kotlin/com/lagradost/FilmanProvider.kt index 1ecec45..4c60ace 100644 --- a/FilmanProvider/src/main/kotlin/com/lagradost/FilmanProvider.kt +++ b/FilmanProvider/src/main/kotlin/com/lagradost/FilmanProvider.kt @@ -137,8 +137,13 @@ class FilmanProvider : MainAPI() { document?.select(".link-to-video")?.apmap { item -> val decoded = base64Decode(item.select("a").attr("data-iframe")) + val videoType = item.parent()?.select("td:nth-child(2)")?.text() val link = tryParseJson(decoded)?.src ?: return@apmap - loadExtractor(link, subtitleCallback, callback) + loadExtractor(link, subtitleCallback) { extractedLink -> + run { + callback(ExtractorLink(extractedLink.source, extractedLink.name + " " + videoType, extractedLink.url, extractedLink.referer, extractedLink.quality, extractedLink.isM3u8, extractedLink.headers, extractedLink.extractorData)) + } + } } return true } From f446545188d2d49a69c3a26fa6796c75c3448ab5 Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Sun, 25 Sep 2022 12:41:56 +0200 Subject: [PATCH 12/17] bump ver --- FilmanProvider/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FilmanProvider/build.gradle.kts b/FilmanProvider/build.gradle.kts index 53dbbaf..f24d517 100644 --- a/FilmanProvider/build.gradle.kts +++ b/FilmanProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 1 +version = 2 cloudstream { @@ -23,4 +23,4 @@ cloudstream { ) iconUrl = "https://www.google.com/s2/favicons?domain=filman.cc&sz=%size%" -} \ No newline at end of file +} From d8ed50157c83222b23e42372ac45a5b5a029c198 Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Sun, 25 Sep 2022 15:14:42 +0200 Subject: [PATCH 13/17] added webflix --- WebFlix/build.gradle.kts | 26 ++ WebFlix/icon.png | Bin 0 -> 4839 bytes WebFlix/src/main/AndroidManifest.xml | 2 + .../kotlin/com/lagradost/WebFlixProvider.kt | 222 ++++++++++++++++++ .../com/lagradost/WebFlixProviderPlugin.kt | 19 ++ 5 files changed, 269 insertions(+) create mode 100644 WebFlix/build.gradle.kts create mode 100644 WebFlix/icon.png create mode 100644 WebFlix/src/main/AndroidManifest.xml create mode 100644 WebFlix/src/main/kotlin/com/lagradost/WebFlixProvider.kt create mode 100644 WebFlix/src/main/kotlin/com/lagradost/WebFlixProviderPlugin.kt diff --git a/WebFlix/build.gradle.kts b/WebFlix/build.gradle.kts new file mode 100644 index 0000000..956eb3b --- /dev/null +++ b/WebFlix/build.gradle.kts @@ -0,0 +1,26 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + // All of these properties are optional, you can safely remove them + + description = "Adds multiple sites using WebFlix. This includes sites in English, Polish, Portuguese and Arabic" + 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( + "Movies", + "TvSeries", + "Live" + ) + + iconUrl = "https://raw.githubusercontent.com/recloudstream/cloudstream-extensions-multilinugal/master/WebFlix/icon.png" +} diff --git a/WebFlix/icon.png b/WebFlix/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0bd09f707e4c0daed0e652d82b63c8f04749da85 GIT binary patch literal 4839 zcmY*dcQhMr_fM;+-ICTS-kPbjR&7O<#3pvBt#;H*)r@c3uMwNtL8wuyD5X|ZiCH7| zruJ5wq~e#pzrWr;p8K40&$*v_@8{n8+;g5Nn4UTVEe9GgqarX&k9opFW9Xdb6Yuo0kT!VYgJC-nv7Tz%>=&k#6TS5ac4W z&gi^{_FbRPQ1Ba2=GjXjDl9DQ6CC%lg9aKD0~4Set_6wzE7(6?gGmc$(32LHYyy`3 z{B4)A9i_72))d`iw1}#Wo2oMvNt!0J01}%m6rRSZ7%=-S89TuRx|PXRu~FNhs|Dq!uil` zEHOi9YP3sCGAuFXGU`WaWS5JVCCsmfLlx9zzm=)@Fg{kbp%7P?`KHA78-JA1494*MU zDMs5oNSvUplxteCK+z87~=3htC9utCFwYQ$CaL1Dd7f zedwFMq8sy7qS|IjvTrJQ{UkYOF_w`My#sEC)Hl|JKD_>VA+R(1TxKA2x+RRDRhB ztcM-Q^+N(EZfLe8mNY?~8ZlP?m|x*J`vb>4g1^gwXr$odlQP#&sREE8{e-&6Lk{V{ z?}jN{MpvK0dmmp>28z=3wowAD5Cg7{z5Q;s!#XsqO4>C-4aN_)X1wF*o@alzBvSOb z9JHvV>Rh^Ul!n1+%vk+paeS3yvW=!qu3Cs6u(OH#{jOQaq<2CrH|n;);ALzq zcP~tQDPVPr$WopL@;Y9r)?Ga{4ON(X0-wkA!)O?d#_B1$;^jMw1~PwM*x86zw0hwm6E=Xz5FgsO z&%eJK(H?IO%7*homtuCyN|}DGJm~x6k1uZhzJ*uWnyJ=aSMDO4K5Sfm9p2YyTnov8 z4hvB0TAsQ;SG~>W4eb%CPsmAz8C$2~Hb!#y(f6H4>qknJZ2HMY&GmA7W+L0ezh9V2 zp+v^SA^IXjsq(zYL|KQipBrgahcOh<-*&EE9~k&bBgP9>vS2;4?u0q6Jq(ts$&Hi+Km0jaulV+^vgR>2r(*vIZX8aSD9|eZ zHc)hhk=enhz6-lCtAaQ2`Qb^K75c9o$e#ZcD(|WrH2>?2P&EfX1bTYeozP%ySnnl<*XEs|18Ggf^Wes|5ip1 zQs{SI4;!0Y6TjGT7XJn=3Rh0gk8w6~_Rwjr-C?WX5j8*xT3M@CxHwSONbi5a;aN`4 zR|I4cw^IL>$eEZBgqOY0@(|8r*{T)q_MqRrNG7tjtW}j%ia6;`H~ce>%#T#+Hl?PE zLubSklu^#&qQJmm21rbYoIs1Gy>1zd-lYT=#q%dp(PPwqGLbwfWK$HUUPzHXm+(=3 zf>NR}rvmzTvuiTgmQ31!IlC{7XQ{vFYM<-@h3Vvw$Uh>=jllw0gf07jg%95miCoQ3gEK`bm?<~U8CoFS$ja+R_*Gm%}lR?{Q1wC*}sz1eEpv*|f`TpaD; zRQL#V88n@B7QVZ$;jYrCUTT%T)GO$jR|SXs=C$W%+z{m;RS;hPBu&Uqu4EnlB7iLZ zh}~XxQw4&F5MIUK9fg(;R2uz{cqC_8>ugero)XlAv1<;o!|^b4<^4uJac1gcVozWW zp>DrPb!gmk#CC6of>e9X&FlMt#3|?$UbU&uYEq*BHy|)gKUve94E+x^Gtf$=0{bz( znvOYEWz!4@fGl#rz<@2;H3Usq}=edMZF}`zrX!x4WB`CGN z6G1`~i0Wr&^Lj)Yj~gA2ep6^&wJmeGuw02eM86DAK1)#n=VEXS!29fzlgG8YWv^H7 z+wvXsJ30Q+4!&z~8~^%_(Ejv%&xOvx?AbAa72f*itdpuJRO>a7;XRiSr};K2U({;{ z1XZPkSLa#D@u##zAWGN+gX|^K5Hi4bk9O&J)-+=Zu51K7>4YXjH^+nvXgrWxC8GCw zN8>#Q)xmbe6rT&`qEMf9rf02^E~ULlDu@kySQ!H@CS_a+L<3HCIj!{~5)VILGdnJ6 zs%Ae~1H18QMkXGMd_oR5OjTXRI{PGfAEvHOJSI6ZqQ$p_;mVPKk#vG)VrUiMwI#xM z*bf-8NqXV3ecm7tIqx)VzOjUoywW%+xAY}MY991PnXLaeNPiWI`Kk=SjLpRo4j~%= z9&waOC*+Mse5|{)W~&1Vgs#MF4&5=|=RUg$l_Tk0h?hewN<}*FsYij4g(~IxbFuwD zTdpql>~cCZ2%MW?x{PonX_JMu`cMxrWH1MOH(%Fhv=B#|iC_@GcC{((e_vXK&qUOM zG^e`Sp6@Fp9XCYzY0gM|@>0kOr298u0<9dFE+n2rf)aX)@{?73cdw6~72C|e{rZFz z2M}}PFXw4>Y_}Y%H)2Ajce5ei`v@iPfy+!wVaH=Fe!C{Ni)~7>>}b7j6TGL7c|1yo^V(dHJS0uj(+GHuV5#C5GlEie=e^CN$43a> z5&DXQ{qh~(CQ4=}ax$sUke73~4 zrcxT&i|8{nI&RqJB(Vcj^euKk?i$G3xEfWNz64_G#<>4Ojhj+cw+7sS<*#Iop68%n zH|KWqhtmek*CrtPjoI^ZnT41=1%*0ya?do^cw5wg z^+>}sDf)RGCbD%RPN=Mg^FGsQZ_RvvJ8zDnm%NIy*p;&{YL~L{&Dh!tttrZd@sAw_ zioC`8b~56Eq81c#xqpFppIqNmW`q2T01tC8mNHW=gF(ysB1K5>rijG5EzWzd-sOvq zxsCV;DneSDPE?}!%09I^bQ660%h&z)wljsqnfHGh$=5rqdVYS-xQXX?q5i7lO|h+R z^K4iM@mUR@7PQ$$?5o8!_&>o?YoXWLDO>)g^3z|wy5w&v=n>-V)9Dzz-_fAMJO#rK<0CWID57({&MO*p4n7#l9Xoa-w`#a^$XnajeO?zBA{Eb% z5W{)MH2DJs2Ig9ut|Hv@+&L_S1CME7UgINTzA#p0QFag1_b%X=W_d?Uy<=^HVH; zSkH(nPEW_iD0L3Jkc^COJPNfII&=6oApn(NyVGZ(>b(V%XlErL5EtQegA%wB1h!Z9 z_F#q9jqJ06+1Z>npxN!oyYvbJ4hEje=;pWUvOD3p+w!V)@%oi6jI8(%~ko8 z&j@oAxg{}-N?-agd%*J@n!EG0v5W0QvEqFw9b2_isf?j@#v<5ZG#d<6A9Q?<%4F-Y zg=@4P2`%~5L5`w)&=Mw8A;_c> zvBLGn$J1m!B-p_B+UsjdC`2kls}n=x?>-3RhS=!l_FWnr_msP5D$G|c1Wf{`4bAdvHFp_!Kl}wc|wX^nM_dpdXBAAF*e(Xu%M%KVlt?Y(a#bSH{FRz zFK$sHj{_dL;x%2U^TDNPTHfV}SMxB7damd3kl@KK_@d10prq2}AmD{G0!Yg#v#K*{ z&126$zabOC=7Fl45Ye{2S^}Ha6)kNXEl9sTCE5kVxu1{vJ9NAvbVthKdGED!7R}oH z@M6-8>EKkHdxWWMqGtJ^1mDVwHe4?qlO+V}Ga>%jGP>SP54+4*B#qyhPukFi5R4@^?urky>atc!KBtk?fUpDoM%tMF!r zMqsidrO^yCUrv2W^w(h8L3B+e2jN*PKQ=-r*m@@EWI6gY zF!WEU0}j6qIi>{$L?#rKb86*Q53;;Kx$Aj^<=EKT@*(`)q=tFuuPx`%Jz>tI0&|IC zd;LOr5~%r7bZHt}_gGKTKpS6LG#~!yN=tNOPWBkxzU||4Z#nq>GdlzOWQCl~KT7{$ zf)0<{5oWSeT>7<-Kp4tgckQ9u_!%!mO<31tPq2Ghb~)bOM9xU@tNvnqRp@f?0284#<6H>Stc|5uetxjs-%0XX?{ zb5aBN?<{8T#(Iflo(0q`wD*NF=_@b;S#BT zAr9K(CR0NJm>g;rNoGhMTkj+^UxOaU5E4wK4ftNsO2ZkIU(Fx@b^i7AVQ6#mgR*ft zH>_dlWf2QtalC<(BPu_SUcisf0Eo()4LS}kNbcor$OxQ!Z~qB>Ey%xQ`tOK8_$G8FHZPsJZ^;U-X5O p(@t@f@sh(De6jy4`a!!$iTOxQOh0EM`R59t3DHyg{LJ?4{{a%Bo=*S( literal 0 HcmV?d00001 diff --git a/WebFlix/src/main/AndroidManifest.xml b/WebFlix/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/WebFlix/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/WebFlix/src/main/kotlin/com/lagradost/WebFlixProvider.kt b/WebFlix/src/main/kotlin/com/lagradost/WebFlixProvider.kt new file mode 100644 index 0000000..0eb0366 --- /dev/null +++ b/WebFlix/src/main/kotlin/com/lagradost/WebFlixProvider.kt @@ -0,0 +1,222 @@ +package com.lagradost + +import android.util.Base64 +import android.util.Log +import com.lagradost.WebFlixProvider.Companion.toHomePageList +import com.lagradost.cloudstream3.* +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.Qualities +import com.lagradost.cloudstream3.utils.loadExtractor +import java.net.URLEncoder + + +class WebFlixProvider(override var lang: String, override var mainUrl: String, override var name: String, override val supportedTypes: Set) : MainAPI() { + val magicPath = base64Decode("NEY1QTlDM0Q5QTg2RkE1NEVBQ0VEREQ2MzUxODUvZDUwNmFiZmQtOWZlMi00YjcxLWI5NzktZmVmZjIxYmNhZDEzLw==") + override val hasMainPage = true + override val hasChromecastSupport = true + + override suspend fun getMainPage( + page: Int, + request : MainPageRequest + ): HomePageResponse? { + val res = tryParseJson(app.get("$mainUrl/api/first/$magicPath").text) ?: return null + return HomePageResponse( + res.getHomePageLists(this), + false + ) + } + + override suspend fun search(query: String): List? { + val res = tryParseJson(app.get("$mainUrl/api/search/${query.encodeUri()}/$magicPath").text) ?: return null + return res.posters.map { it.toSearchResponse(this) } + } + + override suspend fun load(url: String): LoadResponse? { + val data = tryParseJson(app.get(url).text) ?: return null + return data.toLoadResponse(this) + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + val sources = tryParseJson>(data) ?: return false + sources.forEach { + it.load(subtitleCallback, callback) + } + return true + } + + private data class ApiSearchResponse( + val posters: List + ) + + private data class HomeResponse(val genres: List, val channels: List, val slides: List) { + fun getHomePageLists(provider: WebFlixProvider): List { + val lists = mutableListOf() + if (channels.isNotEmpty()) { + channels.forEach { + if (it.type == null) it.type = "channel" + } + lists.add(channels.toHomePageList("Channels", provider)) + } + if (slides.isNotEmpty()) lists.add(slides.toHomePageList("Slides", provider)) + lists.addAll(genres.map { it.toHomePageList(provider) }) + return lists + } + } + private data class HomeReponseGenre( + val title: String, + val posters: List + ) { + fun toHomePageList(provider: WebFlixProvider) = posters.toHomePageList(title, provider) + } + + data class Source( + val title: String?, + val url: String?, + val quality: String?, + ) { + suspend fun load(subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit) { + if (url == null) return; + when (url.split(".").last()) { + "mp4", "m3u8", "mov" -> callback.invoke( + ExtractorLink( + quality ?: "", + title ?: "", + url, + "", + Qualities.Unknown.value, + isM3u8 = (url.endsWith("m3u8")) + )) + else -> loadExtractor(url, subtitleCallback, callback) + } + } + } + + private data class ApiEpisode( + val id: Int, + val title: String?, + val description: String?, + val sources: List = emptyList() + ) { + fun toEpisode(season: Int, episode: Int) = Episode( + sources.toJson(), + title, + season, + episode, + null, + null, + description + ) + } + + private data class ApiSeason( + val title: String?, + val episodes: List = emptyList() + ) { + fun getEpisodes(season: Int): List = episodes.mapIndexed { idx, episode -> episode.toEpisode(season, idx + 1) } + } + + data class Entry( + val id: Int, + val title: String, + val label: String?, + val sublabel: String?, + val image: String?, + val description: String?, + var type: String?, + val year: String?, + val imdb: Double?, + val sources: List = emptyList() + ) { + fun getTvType() = when (type) { + "serie" -> TvType.TvSeries + "movie" -> TvType.Movie + "channel", "4" -> TvType.Live + else -> { + Log.d("WebFlix", "other: $type") + TvType.Others + } + } + + fun toSearchResponse(provider: WebFlixProvider): SearchResponse { + val entry = this + return when(getTvType()) { + TvType.Movie -> provider.newMovieSearchResponse( + title, + "${provider.mainUrl}/api/movie/by/$id/${provider.magicPath}", + getTvType(), + ) { + posterUrl = image + year = entry.year?.toIntOrNull() + } + TvType.TvSeries -> provider.newTvSeriesSearchResponse( + title, + "${provider.mainUrl}/api/series/by/$id/${provider.magicPath}", + TvType.TvSeries + ) { + posterUrl = image + //year = entry.year?.toIntOrNull() + } + TvType.Live -> provider.newMovieSearchResponse( + title, + "${provider.mainUrl}/api/channel/by/$id/${provider.magicPath}", + getTvType(), + ) { + posterUrl = image + year = entry.year?.toIntOrNull() + } + else -> provider.newMovieSearchResponse( + title, + "${provider.mainUrl}/api/$type/by/$id/${provider.magicPath}", + getTvType(), + ) { + posterUrl = image + year = entry.year?.toIntOrNull() + } + } + } + + suspend fun toLoadResponse(provider: WebFlixProvider): LoadResponse? { + val entry = this + return when(getTvType()) { + TvType.TvSeries -> { + val res = tryParseJson>(app.get("${provider.mainUrl}/api/season/by/serie/${id}/${provider.magicPath}").text) ?: return null + provider.newTvSeriesLoadResponse( + title, + "", + TvType.TvSeries, + res.mapIndexed { idx, season -> season.getEpisodes(idx + 1) } + .flatten() + ) { + this.posterUrl = entry.image + this.year = entry.year?.toIntOrNull() + this.plot = description + this.rating = if (entry.imdb != null) (entry.imdb*10).toInt() else null + } + } + else -> provider.newMovieLoadResponse( + title, + "", + getTvType(), + sources.toJson() + ) { + this.posterUrl = entry.image + this.year = entry.year?.toIntOrNull() + this.plot = description + this.rating = if (entry.imdb != null) (entry.imdb*10).toInt() else null + } + } + } + } + + companion object { + fun String.encodeUri() = URLEncoder.encode(this, "utf8") + fun List.toHomePageList(name: String, provider: WebFlixProvider) = HomePageList(name, this.map { it.toSearchResponse(provider) }) + } +} \ No newline at end of file diff --git a/WebFlix/src/main/kotlin/com/lagradost/WebFlixProviderPlugin.kt b/WebFlix/src/main/kotlin/com/lagradost/WebFlixProviderPlugin.kt new file mode 100644 index 0000000..586c54b --- /dev/null +++ b/WebFlix/src/main/kotlin/com/lagradost/WebFlixProviderPlugin.kt @@ -0,0 +1,19 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context +import com.lagradost.cloudstream3.TvType + +@CloudstreamPlugin +class WebFlixProviderPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(WebFlixProvider("en", "https://dhfilmtv.com", "DHFilmTv", setOf(TvType.Movie, TvType.TvSeries))) + registerMainAPI(WebFlixProvider("pl", "https://app.vodi.cc", "Vodi.cc", setOf(TvType.Movie, TvType.TvSeries))) + registerMainAPI(WebFlixProvider("fr", "http://www.vanflix.cm", "Vanflix", setOf(TvType.Movie, TvType.TvSeries, TvType.Live))) + registerMainAPI(WebFlixProvider("pt-pt", "https://www.brflix.xyz", "BrFlix", setOf(TvType.Movie, TvType.TvSeries, TvType.Live))) + registerMainAPI(WebFlixProvider("ar", "https://ifilm.live", "ifilm.live", setOf(TvType.Movie, TvType.TvSeries))) + } +} \ No newline at end of file From 4cda67d0a1d54ed6b8d02c7d654f42b937da7cdd Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Sun, 25 Sep 2022 15:36:00 +0200 Subject: [PATCH 14/17] fix vizjer and add one more webflix instance --- VizjerProvider/build.gradle.kts | 2 +- .../kotlin/com/lagradost/VizjerProvider.kt | 19 ++++++++++++------- WebFlix/build.gradle.kts | 3 +-- .../com/lagradost/WebFlixProviderPlugin.kt | 1 + 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/VizjerProvider/build.gradle.kts b/VizjerProvider/build.gradle.kts index db8c650..0f5ce8a 100644 --- a/VizjerProvider/build.gradle.kts +++ b/VizjerProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 3 +version = 4 cloudstream { diff --git a/VizjerProvider/src/main/kotlin/com/lagradost/VizjerProvider.kt b/VizjerProvider/src/main/kotlin/com/lagradost/VizjerProvider.kt index 9cbb90a..2c9ef0f 100644 --- a/VizjerProvider/src/main/kotlin/com/lagradost/VizjerProvider.kt +++ b/VizjerProvider/src/main/kotlin/com/lagradost/VizjerProvider.kt @@ -5,11 +5,12 @@ import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.loadExtractor +import com.lagradost.cloudstream3.network.CloudflareKiller import org.jsoup.Jsoup import org.jsoup.select.Elements class VizjerProvider : MainAPI() { - override var mainUrl = "http://93.185.166.160" + override var mainUrl = "https://vizjer.pl" override var name = "Vizjer.pl" override var lang = "pl" override val hasMainPage = true @@ -19,8 +20,10 @@ class VizjerProvider : MainAPI() { TvType.Movie ) + private val interceptor = CloudflareKiller() + override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val document = app.get(mainUrl).document + val document = app.get(mainUrl, interceptor = interceptor).document val lists = document.select(".item-list") val categories = ArrayList() for (l in lists) { @@ -37,7 +40,9 @@ class VizjerProvider : MainAPI() { this.name, TvType.Movie, properUrl(poster)!!, - year + year, + null, + posterHeaders = interceptor.getCookieHeaders(mainUrl).toMap() ) } categories.add(HomePageList(title, items)) @@ -47,7 +52,7 @@ class VizjerProvider : MainAPI() { override suspend fun search(query: String): List { val url = "$mainUrl/wyszukaj?phrase=$query" - val document = app.get(url).document + val document = app.get(url, interceptor = interceptor).document val lists = document.select("#advanced-search > div") val movies = lists[1].select("div:not(.clearfix)") val series = lists[3].select("div:not(.clearfix)") @@ -66,10 +71,10 @@ class VizjerProvider : MainAPI() { type, properUrl(img)!!, null, - null + posterHeaders = interceptor.getCookieHeaders(url).toMap() ) } else { - MovieSearchResponse(name, properUrl(href)!!, this.name, type, properUrl(img)!!, null) + MovieSearchResponse(name, properUrl(href)!!, this.name, type, properUrl(img)!!, null, posterHeaders = interceptor.getCookieHeaders(url).toMap()) } } } @@ -77,7 +82,7 @@ class VizjerProvider : MainAPI() { } override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document + val document = app.get(url, interceptor = interceptor).document val documentTitle = document.select("title").text().trim() if (documentTitle.startsWith("Logowanie")) { diff --git a/WebFlix/build.gradle.kts b/WebFlix/build.gradle.kts index 956eb3b..f413b2a 100644 --- a/WebFlix/build.gradle.kts +++ b/WebFlix/build.gradle.kts @@ -1,6 +1,5 @@ // use an integer for version numbers -version = 1 - +version = 2 cloudstream { // All of these properties are optional, you can safely remove them diff --git a/WebFlix/src/main/kotlin/com/lagradost/WebFlixProviderPlugin.kt b/WebFlix/src/main/kotlin/com/lagradost/WebFlixProviderPlugin.kt index 586c54b..2c33933 100644 --- a/WebFlix/src/main/kotlin/com/lagradost/WebFlixProviderPlugin.kt +++ b/WebFlix/src/main/kotlin/com/lagradost/WebFlixProviderPlugin.kt @@ -15,5 +15,6 @@ class WebFlixProviderPlugin: Plugin() { registerMainAPI(WebFlixProvider("fr", "http://www.vanflix.cm", "Vanflix", setOf(TvType.Movie, TvType.TvSeries, TvType.Live))) registerMainAPI(WebFlixProvider("pt-pt", "https://www.brflix.xyz", "BrFlix", setOf(TvType.Movie, TvType.TvSeries, TvType.Live))) registerMainAPI(WebFlixProvider("ar", "https://ifilm.live", "ifilm.live", setOf(TvType.Movie, TvType.TvSeries))) + registerMainAPI(WebFlixProvider("en", "https://karmadarna.com", "KarMaDarNa", setOf(TvType.NSFW))) } } \ No newline at end of file From 28605bded775cd5f580bc618655d345377c31f08 Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Sun, 25 Sep 2022 15:43:18 +0200 Subject: [PATCH 15/17] fix icon --- WebFlix/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebFlix/build.gradle.kts b/WebFlix/build.gradle.kts index f413b2a..d0e4c3b 100644 --- a/WebFlix/build.gradle.kts +++ b/WebFlix/build.gradle.kts @@ -21,5 +21,5 @@ cloudstream { "Live" ) - iconUrl = "https://raw.githubusercontent.com/recloudstream/cloudstream-extensions-multilinugal/master/WebFlix/icon.png" + iconUrl = "https://raw.githubusercontent.com/recloudstream/cloudstream-extensions-multilingual/master/WebFlix/icon.png" } From b3c10b308890702b42245901ed53df3d02a52f25 Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Sun, 25 Sep 2022 19:15:40 +0200 Subject: [PATCH 16/17] fix webflix --- WebFlix/build.gradle.kts | 2 +- .../src/main/kotlin/com/lagradost/WebFlixProvider.kt | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/WebFlix/build.gradle.kts b/WebFlix/build.gradle.kts index d0e4c3b..80a21f7 100644 --- a/WebFlix/build.gradle.kts +++ b/WebFlix/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 2 +version = 3 cloudstream { // All of these properties are optional, you can safely remove them diff --git a/WebFlix/src/main/kotlin/com/lagradost/WebFlixProvider.kt b/WebFlix/src/main/kotlin/com/lagradost/WebFlixProvider.kt index 0eb0366..79530bd 100644 --- a/WebFlix/src/main/kotlin/com/lagradost/WebFlixProvider.kt +++ b/WebFlix/src/main/kotlin/com/lagradost/WebFlixProvider.kt @@ -55,7 +55,11 @@ class WebFlixProvider(override var lang: String, override var mainUrl: String, o val posters: List ) - private data class HomeResponse(val genres: List, val channels: List, val slides: List) { + private data class HomeResponse( + val genres: List = emptyList(), + val channels: List = emptyList(), + // val slides: List = emptyList() + ) { fun getHomePageLists(provider: WebFlixProvider): List { val lists = mutableListOf() if (channels.isNotEmpty()) { @@ -64,7 +68,7 @@ class WebFlixProvider(override var lang: String, override var mainUrl: String, o } lists.add(channels.toHomePageList("Channels", provider)) } - if (slides.isNotEmpty()) lists.add(slides.toHomePageList("Slides", provider)) + //if (slides.isNotEmpty()) lists.add(slides.toHomePageList("Slides", provider)) lists.addAll(genres.map { it.toHomePageList(provider) }) return lists } @@ -157,7 +161,7 @@ class WebFlixProvider(override var lang: String, override var mainUrl: String, o } TvType.TvSeries -> provider.newTvSeriesSearchResponse( title, - "${provider.mainUrl}/api/series/by/$id/${provider.magicPath}", + "${provider.mainUrl}/api/movie/by/$id/${provider.magicPath}", TvType.TvSeries ) { posterUrl = image From b368fcce02897d40dac00f00c705e756a31a4b0b Mon Sep 17 00:00:00 2001 From: Eddy976 <47984460+Eddy976@users.noreply.github.com> Date: Mon, 26 Sep 2022 17:20:08 +0200 Subject: [PATCH 17/17] add VostfreeProvider (#16) * add VostfreeProvider * autoformat the code and add the changes requested * remove com.lagradost.cloudstream3.animeproviders line * optimized the getMainPage * Add Sarlay's provider * fix the research function and reshape the load * Add NekosamaProvider * add fuzzy for computing string distance * correct the name of class * fix and improve the search function * rename nekosama class * Remove no french provider * NekosamaProvider working version * Revert "Remove no french provider" This reverts commit b177de518dd429010b8b8ddc569940d9d11cc6d7. * use of apmap when necessary * Nekosama add more results items from search * Load function of vostfree optimized * "" * resolve conflict * Add WiflixProvider * Change title and use apmap * No vostfree reference * Add tags and complete season * precise dub ou sub * Update FrenchStream * not anime movie * Fix load movie for vostfree * Add vido extractor for frenchStream * fix reference vido * doodstream work for Wiflix * Udate vidoExtractor to take in account Wiflix ! Optimized FrenchStream to take directly the redirected link * In Frenchstream dood is in reality streamlare * Get MesFilmsProvider directly at Sarlay repository * Requested changes are done * Add showStatus to NekoSamaProvider * show news episodes for neko-sama * Requested changes done * Add year for episodes * French Stream change his mainUrl * Improve research for wiflix * add year of episodes and show the latest episode for vostfree * Add more provider for Wiflix * open the good extractor * Fix Extractor * change authors Co-authored-by: Eddy --- FrenchStreamProvider/build.gradle.kts | 7 +- .../com/lagradost/FrenchStreamProvider.kt | 220 +++++---- .../lagradost/FrenchStreamProviderPlugin.kt | 1 + .../kotlin/com/lagradost/VidoExtractor.kt | 47 ++ NekosamaProvider/build.gradle.kts | 26 + NekosamaProvider/src/main/AndroidManifest.xml | 2 + .../kotlin/com/lagradost/NekosamaProvider.kt | 443 ++++++++++++++++++ .../com/lagradost/NekosamaProviderPlugin.kt | 17 + .../kotlin/com/lagradost/PstreamExtractor.kt | 53 +++ README.md | 2 +- VostfreeProvider/build.gradle.kts | 26 + VostfreeProvider/src/main/AndroidManifest.xml | 2 + .../kotlin/com/lagradost/MytvExtractor.kt | 42 ++ .../kotlin/com/lagradost/SibnetExtractor.kt | 35 ++ .../kotlin/com/lagradost/VostfreeProvider.kt | 368 +++++++++++++++ .../com/lagradost/VostfreeProviderPlugin.kt | 18 + .../kotlin/com/lagradost/VudeoExtractor.kt | 34 ++ WiflixProvider/build.gradle.kts | 26 + WiflixProvider/src/main/AndroidManifest.xml | 2 + .../com/lagradost/DoodStreamExtractor.kt | 35 ++ .../com/lagradost/StreamSBPlusExtractor.kt | 81 ++++ .../kotlin/com/lagradost/WiflixProvider.kt | 307 ++++++++++++ .../com/lagradost/WiflixProviderPlugin.kt | 18 + build.gradle.kts | 2 + 24 files changed, 1714 insertions(+), 100 deletions(-) create mode 100644 FrenchStreamProvider/src/main/kotlin/com/lagradost/VidoExtractor.kt create mode 100644 NekosamaProvider/build.gradle.kts create mode 100644 NekosamaProvider/src/main/AndroidManifest.xml create mode 100644 NekosamaProvider/src/main/kotlin/com/lagradost/NekosamaProvider.kt create mode 100644 NekosamaProvider/src/main/kotlin/com/lagradost/NekosamaProviderPlugin.kt create mode 100644 NekosamaProvider/src/main/kotlin/com/lagradost/PstreamExtractor.kt create mode 100644 VostfreeProvider/build.gradle.kts create mode 100644 VostfreeProvider/src/main/AndroidManifest.xml create mode 100644 VostfreeProvider/src/main/kotlin/com/lagradost/MytvExtractor.kt create mode 100644 VostfreeProvider/src/main/kotlin/com/lagradost/SibnetExtractor.kt create mode 100644 VostfreeProvider/src/main/kotlin/com/lagradost/VostfreeProvider.kt create mode 100644 VostfreeProvider/src/main/kotlin/com/lagradost/VostfreeProviderPlugin.kt create mode 100644 VostfreeProvider/src/main/kotlin/com/lagradost/VudeoExtractor.kt create mode 100644 WiflixProvider/build.gradle.kts create mode 100644 WiflixProvider/src/main/AndroidManifest.xml create mode 100644 WiflixProvider/src/main/kotlin/com/lagradost/DoodStreamExtractor.kt create mode 100644 WiflixProvider/src/main/kotlin/com/lagradost/StreamSBPlusExtractor.kt create mode 100644 WiflixProvider/src/main/kotlin/com/lagradost/WiflixProvider.kt create mode 100644 WiflixProvider/src/main/kotlin/com/lagradost/WiflixProviderPlugin.kt diff --git a/FrenchStreamProvider/build.gradle.kts b/FrenchStreamProvider/build.gradle.kts index 83658ab..b86bc10 100644 --- a/FrenchStreamProvider/build.gradle.kts +++ b/FrenchStreamProvider/build.gradle.kts @@ -6,8 +6,8 @@ cloudstream { language = "fr" // All of these properties are optional, you can safely remove them - // description = "Lorem Ipsum" - // authors = listOf("Cloudburst") + description = "FRENCH STREAM en plus d'être un site efficace et plaisant dispose d'un contenu visuel diversifié" + authors = listOf("Sarlay", "Eddy976") /** * Status int as the following: @@ -18,10 +18,9 @@ cloudstream { * */ status = 1 // will be 3 if unspecified tvTypes = listOf( - "AnimeMovie", "TvSeries", "Movie", ) - iconUrl = "https://www.google.com/s2/favicons?domain=french-stream.re&sz=%size%" + iconUrl = "https://www.google.com/s2/favicons?domain=french-stream.ac&sz=%size%" } \ No newline at end of file diff --git a/FrenchStreamProvider/src/main/kotlin/com/lagradost/FrenchStreamProvider.kt b/FrenchStreamProvider/src/main/kotlin/com/lagradost/FrenchStreamProvider.kt index 4d6720c..05aef23 100644 --- a/FrenchStreamProvider/src/main/kotlin/com/lagradost/FrenchStreamProvider.kt +++ b/FrenchStreamProvider/src/main/kotlin/com/lagradost/FrenchStreamProvider.kt @@ -1,54 +1,31 @@ package com.lagradost + import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.LoadResponse.Companion.addRating import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.extractorApis +import org.jsoup.nodes.Element class FrenchStreamProvider : MainAPI() { - override var mainUrl = "https://french-stream.re" - override var name = "French Stream" + override var mainUrl = "https://french-stream.cx" //re ou ac ou city + override var name = "FrenchStream" override val hasQuickSearch = false override val hasMainPage = true override var lang = "fr" - override val supportedTypes = setOf(TvType.AnimeMovie, TvType.TvSeries, TvType.Movie) - + override val supportedTypes = setOf(TvType.Movie, TvType.TvSeries) override suspend fun search(query: String): List { - val link = "$mainUrl/?do=search&subaction=search&story=$query" - val soup = app.post(link).document + val link = "$mainUrl/?do=search&subaction=search&story=$query" // search' + val document = + app.post(link).document // app.get() permet de télécharger la page html avec une requete HTTP (get) + val results = document.select("div#dle-content > > div.short") - return soup.select("div.short-in.nl").map { li -> - val href = fixUrl(li.selectFirst("a.short-poster")!!.attr("href")) - val poster = li.selectFirst("img")?.attr("src") - val title = li.selectFirst("> a.short-poster")!!.text().toString().replace(". ", "") - val year = li.selectFirst(".date")?.text()?.split("-")?.get(0)?.toIntOrNull() - if (title.contains( - "saison", - ignoreCase = true - ) - ) { // if saison in title ==> it's a TV serie - TvSeriesSearchResponse( - title, - href, - this.name, - TvType.TvSeries, - poster, - year, - (title.split("Eps ", " ")[1]).split(" ")[0].toIntOrNull() - ) - } else { // it's a movie - MovieSearchResponse( - title, - href, - this.name, - TvType.Movie, - poster, - year, - ) + val allresultshome = + results.apmap { article -> // avec mapnotnull si un élément est null, il sera automatiquement enlevé de la liste + article.toSearchResponse() } - } + return allresultshome } override suspend fun load(url: String): LoadResponse { @@ -58,45 +35,45 @@ class FrenchStreamProvider : MainAPI() { val isMovie = !title.contains("saison", ignoreCase = true) val description = soup.selectFirst("div.fdesc")!!.text().toString() - .split("streaming", ignoreCase = true)[1].replace(" : ", "") - var poster = fixUrlNull(soup.selectFirst("div.fposter > img")?.attr("src")) + .split("streaming", ignoreCase = true)[1].replace(":", "") + var poster = soup.selectFirst("div.fposter > img")?.attr("src") val listEpisode = soup.select("div.elink") + val tags = soup.select("ul.flist-col > li").getOrNull(1) + //val rating = soup.select("span[id^=vote-num-id]")?.getOrNull(1)?.text()?.toInt() if (isMovie) { - val tags = soup.select("ul.flist-col > li").getOrNull(1) + val yearRegex = Regex("""ate de sortie\: (\d*)""") + val year = yearRegex.find(soup.text())?.groupValues?.get(1) val tagsList = tags?.select("a") ?.mapNotNull { // all the tags like action, thriller ...; unused variable it?.text() } return newMovieLoadResponse(title, url, TvType.Movie, url) { this.posterUrl = poster - addRating(soup.select("div.fr-count > div").text()) - this.year = soup.select("ul.flist-col > li").getOrNull(2)?.text()?.toIntOrNull() + this.year = year?.toIntOrNull() this.tags = tagsList this.plot = description - addTrailer(soup.selectFirst("div.fleft > span > a")?.attr("href")) + //this.rating = rating + addTrailer(soup.selectFirst("button#myBtn > a")?.attr("href")) } } else // a tv serie { - //println(listEpisode) - //println("listeEpisode:") + val episodeList = if (" val epNum = a.text().split("Episode")[1].trim().toIntOrNull() val epTitle = if (a.text().contains("Episode")) { val type = if ("honey" in a.attr("id")) { "VF" } else { - "VOSTFR" + "Vostfr" } - "Episode " + epNum?.toString() + " en " + type + "Episode " + type } else { a.text() } @@ -112,17 +89,24 @@ class FrenchStreamProvider : MainAPI() { null // episode date ) } - return TvSeriesLoadResponse( + + // val tagsList = tags?.text()?.replace("Genre :","") + val yearRegex = Regex("""Titre .* \/ (\d*)""") + val year = yearRegex.find(soup.text())?.groupValues?.get(1) + return newTvSeriesLoadResponse( title, url, - this.name, TvType.TvSeries, episodes, - poster, - null, - description, - ShowStatus.Ongoing, - ) + ) { + this.posterUrl = poster + this.plot = description + this.year = year?.toInt() + //this.rating = rating + //this.showStatus = ShowStatus.Ongoing + //this.tags = tagsList + addTrailer(soup.selectFirst("button#myBtn > a")?.attr("href")) + } } } @@ -219,10 +203,27 @@ class FrenchStreamProvider : MainAPI() { servers.apmap { for (extractor in extractorApis) { - if (it.first.contains(extractor.name, ignoreCase = true)) { - // val name = it.first - // print("true for $name") - extractor.getSafeUrl(it.second, it.second, subtitleCallback, callback) + var playerName = it.first + + if (playerName.contains("Stream.B")) { + playerName = it.first.replace("Stream.B", "StreamSB") + } + if (it.second.contains("streamlare")) { + playerName = "Streamlare" + } + if (playerName.contains(extractor.name, ignoreCase = true)) { + val header = app.get( + "https" + it.second.split("https").get(1), + allowRedirects = false + ).headers + val urlplayer = it.second + var playerUrl = when (!urlplayer.isNullOrEmpty()) { + urlplayer.contains("opsktp.com") -> header.get("location") + .toString() // case where there is redirection to opsktp + + else -> it.second + } + extractor.getSafeUrl(playerUrl, playerUrl, subtitleCallback, callback) break } } @@ -232,42 +233,71 @@ class FrenchStreamProvider : MainAPI() { } - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse? { - val document = app.get(mainUrl).document - val docs = document.select("div.sect") - val returnList = docs.mapNotNull { - val epList = it.selectFirst("> div.sect-c.floats.clearfix") ?: return@mapNotNull null - val title = - it.selectFirst("> div.sect-t.fx-row.icon-r > div.st-left > a.st-capt")!!.text() - val list = epList.select("> div.short") - val isMovieType = title.contains("Films") // if truen type is Movie - val currentList = list.map { head -> - val hrefItem = head.selectFirst("> div.short-in.nl > a") - val href = fixUrl(hrefItem!!.attr("href")) - val img = hrefItem.selectFirst("> img") - val posterUrl = img!!.attr("src") - val name = img.attr("> div.short-title").toString() - return@map if (isMovieType) MovieSearchResponse( - name, - href, - this.name, - TvType.Movie, - posterUrl, - null - ) else TvSeriesSearchResponse( - name, - href, - this.name, - TvType.TvSeries, - posterUrl, - null, null - ) - } - if (currentList.isNotEmpty()) { - HomePageList(title, currentList) - } else null + private fun Element.toSearchResponse(): SearchResponse { + + val posterUrl = fixUrl(select("a.short-poster > img").attr("src")) + val qualityExtracted = select("span.film-ripz > a").text() + val type = select("span.mli-eps").text() + val title = select("div.short-title").text() + val link = select("a.short-poster").attr("href").replace("wvw.", "") //wvw is an issue + var quality = when (!qualityExtracted.isNullOrBlank()) { + qualityExtracted.contains("HDLight") -> getQualityFromString("HD") + qualityExtracted.contains("Bdrip") -> getQualityFromString("BlueRay") + qualityExtracted.contains("DVD") -> getQualityFromString("DVD") + qualityExtracted.contains("CAM") -> getQualityFromString("Cam") + + else -> null + } + + if (type.contains("Eps", false)) { + return MovieSearchResponse( + name = title, + url = link, + apiName = title, + type = TvType.Movie, + posterUrl = posterUrl, + quality = quality + + ) + + + } else // an Serie + { + + return TvSeriesSearchResponse( + name = title, + url = link, + apiName = title, + type = TvType.TvSeries, + posterUrl = posterUrl, + quality = quality, + // + ) + } - if (returnList.isEmpty()) return null - return HomePageResponse(returnList) } + + override val mainPage = mainPageOf( + Pair("$mainUrl/xfsearch/version-film/page/", "Derniers films"), + Pair("$mainUrl/xfsearch/version-serie/page/", "Derniers séries"), + Pair("$mainUrl/film/arts-martiaux/page/", "Films za m'ringué (Arts martiaux)"), + Pair("$mainUrl/film/action/page/", "Films Actions"), + Pair("$mainUrl/film/romance/page/", "Films za malomo (Romance)"), + Pair("$mainUrl/serie/aventure-serie/page/", "Série aventure"), + Pair("$mainUrl/film/documentaire/page/", "Documentaire") + + ) + + override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { + val url = request.data + page + val document = app.get(url).document + val movies = document.select("div#dle-content > div.short") + + val home = + movies.map { article -> // avec mapnotnull si un élément est null, il sera automatiquement enlevé de la liste + article.toSearchResponse() + } + return newHomePageResponse(request.name, home) + } + } diff --git a/FrenchStreamProvider/src/main/kotlin/com/lagradost/FrenchStreamProviderPlugin.kt b/FrenchStreamProvider/src/main/kotlin/com/lagradost/FrenchStreamProviderPlugin.kt index f111fc9..ac71bb4 100644 --- a/FrenchStreamProvider/src/main/kotlin/com/lagradost/FrenchStreamProviderPlugin.kt +++ b/FrenchStreamProvider/src/main/kotlin/com/lagradost/FrenchStreamProviderPlugin.kt @@ -10,5 +10,6 @@ class FrenchStreamProviderPlugin: Plugin() { override fun load(context: Context) { // All providers should be added in this manner. Please don't edit the providers list directly. registerMainAPI(FrenchStreamProvider()) + registerExtractorAPI(VidoExtractor()) } } \ No newline at end of file diff --git a/FrenchStreamProvider/src/main/kotlin/com/lagradost/VidoExtractor.kt b/FrenchStreamProvider/src/main/kotlin/com/lagradost/VidoExtractor.kt new file mode 100644 index 0000000..32f5517 --- /dev/null +++ b/FrenchStreamProvider/src/main/kotlin/com/lagradost/VidoExtractor.kt @@ -0,0 +1,47 @@ +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.getAndUnpack + +class VidoExtractor : ExtractorApi() { + override var name = "Vido" + override var mainUrl = "https://vido.lol" + private val srcRegex = Regex("""layer\(\{sources\:\["(.*)"\]""") + override val requiresReferer = true + + override suspend fun getUrl(url: String, referer: String?): List? { + val methode = if (url.contains("embed")) { + app.get(url) // french stream + } else { + val code = url.substringAfterLast("/") + val data = mapOf( + "op" to "embed", + "file_code" to code, + "&auto" to "1" + + ) + app.post("https://vido.lol/dl", referer = url, data = data) // wiflix + } + with(methode) { + getAndUnpack(this.text).let { unpackedText -> + //val quality = unpackedText.lowercase().substringAfter(" height=").substringBefore(" ").toIntOrNull() + srcRegex.find(unpackedText)?.groupValues?.get(1)?.let { link -> + return listOf( + ExtractorLink( + name, + name, + link, + url, + Qualities.Unknown.value, + true, + ) + ) + } + } + } + return null + } +} \ No newline at end of file diff --git a/NekosamaProvider/build.gradle.kts b/NekosamaProvider/build.gradle.kts new file mode 100644 index 0000000..1e0cc3f --- /dev/null +++ b/NekosamaProvider/build.gradle.kts @@ -0,0 +1,26 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "fr" + // All of these properties are optional, you can safely remove them + + description = " Ce site fait son entrée dans la catégorie des meilleurs sites animes Français. Il est très fiable car quasiment tous ses liens vidéos marchent. Il propose des animes en « VF » version française et en « VOSTFR » version originale Sous-titrée en Français." + authors = listOf("Eddy") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "Anime", + "AnimeMovie", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=neko-sama.fr&sz=%size%" +} \ No newline at end of file diff --git a/NekosamaProvider/src/main/AndroidManifest.xml b/NekosamaProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/NekosamaProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/NekosamaProvider/src/main/kotlin/com/lagradost/NekosamaProvider.kt b/NekosamaProvider/src/main/kotlin/com/lagradost/NekosamaProvider.kt new file mode 100644 index 0000000..8905e3e --- /dev/null +++ b/NekosamaProvider/src/main/kotlin/com/lagradost/NekosamaProvider.kt @@ -0,0 +1,443 @@ +package com.lagradost + + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.utils.AppUtils.toJson + +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import org.jsoup.nodes.Element + +import me.xdrop.fuzzywuzzy.FuzzySearch +import java.util.* +import kotlin.collections.ArrayList + +class NekosamaProvider : MainAPI() { + override var mainUrl = "https://neko-sama.fr" + override var name = "Neko-sama" + override val hasQuickSearch = false // recherche rapide (optionel, pas vraimet utile) + override val hasMainPage = true // page d'accueil (optionel mais encoragé) + override var lang = "fr" // fournisseur est en francais + override val supportedTypes = + setOf(TvType.Anime, TvType.AnimeMovie, TvType.OVA) // animes, animesfilms + + private val nCharQuery = 10 // take the lenght of the query + nCharQuery + private val resultsSearchNbr = 50 // take only n results from search function + + + data class EpisodeData( + @JsonProperty("id") val id: Int, + @JsonProperty("title") val title: String?, + @JsonProperty("title_english") val title_english: String?, + @JsonProperty("title_romanji") val title_romanji: String?, + @JsonProperty("title_french") val title_french: String?, + @JsonProperty("others") val others: String?, + @JsonProperty("type") val type: String?, + @JsonProperty("status") val status: String?, + @JsonProperty("popularity") val popularity: Int?, + @JsonProperty("url") val url: String, + @JsonProperty("genre") val genre: Genre?, + @JsonProperty("url_image") val url_image: String?, + @JsonProperty("score") val score: String?, + @JsonProperty("start_date_year") val start_date_year: String?, + @JsonProperty("nb_eps") val nb_eps: String?, + + ) + + data class Genre( + @JsonProperty("0") val action: String?, + @JsonProperty("1") val adventure: String?, + @JsonProperty("2") val drama: String?, + @JsonProperty("3") val fantasy: String?, + @JsonProperty("4") val military: String?, + @JsonProperty("5") val shounen: String?, + ) + + // Looking for the best title matching from parsed Episode data + private fun EpisodeData.titleObtainedBysortByQuery(query: String?): String? { + + if (query == null) { + // No shorting so return the first title + var title = this.title + + return title + } else { + + + val titles = listOf(title, title_french, title_english, title_romanji).filterNotNull() + // Sorted by the best title matching + val titlesSorted = titles.sortedBy { it -> + -FuzzySearch.ratio( + it?.take(query.length + nCharQuery), + query + ) + } + return titlesSorted.elementAt(0) + + + } + } + + private fun List.sortByQuery(query: String?): List { + return if (query == null) { + // Return list to base state if no query + this.sortedBy { it.title } + } else { + + this.sortedBy { + val bestTitleMatching = it.titleObtainedBysortByQuery(query) + -FuzzySearch.ratio( + bestTitleMatching?.take(query.length + nCharQuery) ?: bestTitleMatching, + query + ) + } + } + } + + /** This function is done because there is two database (vf and vostfr). So it allows to sort the combined database **/ + private fun List.sortByname(query: String?): List { + return if (query == null) { + // Return list to base state if no query + this.sortedBy { it.name } + } else { + + this.sortedBy { + val name = it.name + -FuzzySearch.ratio(name.take(query.length + nCharQuery), query) + } + } + } + + /** + Cherche le site pour un titre spécifique + + La recherche retourne une SearchResponse, qui peut être des classes suivants: AnimeSearchResponse, MovieSearchResponse, TorrentSearchResponse, TvSeriesSearchResponse + Chaque classes nécessite des données différentes, mais a en commun le nom, le poster et l'url + **/ + override suspend fun search(query: String): List { + + var listofResults = ArrayList() + + listOf( + "$mainUrl/animes-search-vf.json" to "(VF) ", + "$mainUrl/animes-search-vostfr.json" to "(Vostfr) " + ).apmap {(url, version) -> + val dubStatus = when (!version.isNullOrBlank()) { + version.contains("VF") -> DubStatus.Dubbed + version.contains("Vostfr") -> DubStatus.Subbed + else -> null + } + val reponse = app.get(url).text + val ParsedData = tryParseJson>(reponse) + + ParsedData?.sortByQuery(query)?.take(resultsSearchNbr)?.forEach { it -> + val type = it.type + val mediaPoster = it.url_image + val href = fixUrl(it.url) + val bestTitleMatching = it.titleObtainedBysortByQuery(query) + val title = version + bestTitleMatching + + when (type) { + "m0v1e", "special" -> ( + listofResults.add(newMovieSearchResponse( // réponse du film qui sera ajoutée à la liste apmap qui sera ensuite return + title, + href, + TvType.AnimeMovie, + false + ) { + this.posterUrl = mediaPoster + } + )) + null, "tv", "ova", "" -> ( + listofResults.add(newAnimeSearchResponse( + title, + href, + TvType.Anime, + false + ) { + this.posterUrl = mediaPoster + this.dubStatus = EnumSet.of(dubStatus) + } + + )) + else -> { + + throw ErrorLoadingException("invalid media type") // le type n'est pas reconnu ==> affiche une erreur + } + } + } ?: throw ErrorLoadingException("ParsedData failed") + } + return listofResults.sortByname(query) + .take(resultsSearchNbr) // Do that to short the vf and vostfr anime together + + } + + /** + * charge la page d'informations, il ya toutes les donées, les épisodes, le résumé etc ... + * Il faut retourner soit: AnimeLoadResponse, MovieLoadResponse, TorrentLoadResponse, TvSeriesLoadResponse. + */ + override suspend fun load(url: String): LoadResponse { + val document = app.get(url).document // + // url est le lien retourné par la fonction search (la variable href) ou la fonction getMainPage + + val episodes = ArrayList() + var mediaType = TvType.Anime + val script = + document.select("div#main > script:first-of-type") + + val srcAllInfoEpisode = + Regex("""min\"\,\"([^\}]*)\}""") + val results = srcAllInfoEpisode.findAll(script.toString()) + //srcAllInfoEpisode.find(script.toString())?.groupValues?.get(1)? + ////////////////////////////////////// + var title = "" //document.select("div.offset-md-4 >:not(small)").text() + var dataUrl = "" + var link_video = "" + ///////////////////////////////////// + results.forEach { infoEpisode -> + val episodeScript = infoEpisode.groupValues[1] + val srcScriptEpisode = + Regex("""episode\"\:\"Ep\. ([0-9]*)\"""") + val episodeNum = srcScriptEpisode.find(episodeScript)?.groupValues?.get(1)?.toInt() + val srcScriptTitle = Regex("""title\"\:\"([^\"]*)\"\,\"url\"\:\"\\\/anime""") + var titleE = srcScriptTitle.find(episodeScript)?.groupValues?.get(1) + if (titleE != null) title = titleE + val srcScriptlink = + Regex("""\"url\"\:\"([^\"]*)\"""") // remove\ + val link = srcScriptlink.find(episodeScript)?.groupValues?.get(1) + + if (link != null) link_video = fixUrl(link.replace("\\", "")) + + val srcScriptposter = + Regex("""\"url_image\"\:\"([^\"]*)\"""") // remove\ + val poster = srcScriptposter.find(episodeScript)?.groupValues?.get(1) + var link_poster = "" + if (poster != null) link_poster = poster.replace("\\", "") + dataUrl = link_video + + + episodes.add( + Episode( + link_video, + episode = episodeNum, + name = title, + posterUrl = link_poster + + ) + ) + + } + val regexYear = Regex("""Diffusion [a-zA-Z]* (\d*)""") + val infosList = + document.selectFirst("div#anime-info-list")?.text() + val isinfosList = !infosList.isNullOrBlank() + var year:Int?=null + if (isinfosList) { + if (infosList!!.contains("movie")) mediaType = TvType.AnimeMovie + year =regexYear.find(infosList)!!.groupValues.get(1).toInt() + } + + val description = document.selectFirst("div.synopsis > p")?.text() + val poster = document.select("div.cover > img").attr("src") + + if (mediaType == TvType.AnimeMovie) { + return newMovieLoadResponse( + title, + url, + mediaType, + dataUrl + ) { // retourne les informations du film + this.posterUrl = poster + this.plot = description + this.year = year + } + } else // an anime + { + val status = when (isinfosList) { + infosList!!.contains("En cours") -> ShowStatus.Ongoing // En cours + infosList!!.contains("Terminé") -> ShowStatus.Completed + else -> null + } + return newAnimeLoadResponse( + title, + url, + mediaType, + ) { + this.posterUrl = poster + this.plot = description + addEpisodes( + DubStatus.Dubbed, + episodes + ) + this.showStatus = status + this.year = year + + } + } + } + + + /** récupere les liens .mp4 ou m3u8 directement à partir du paramètre data généré avec la fonction load()**/ + override suspend fun loadLinks( + data: String, // fournit par load() + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit, + ): Boolean { + val url = data + val document = app.get(url).document + val script = document.select("""[type^="text"]""")[1] + val srcAllvideolinks = + Regex("""\'(https:\/\/[^']*)""") + + val results = srcAllvideolinks.findAll(script.toString()) + + results.forEach { infoEpisode -> + + var playerUrl = infoEpisode.groupValues[1] + + if (!playerUrl.isNullOrBlank()) + loadExtractor( + httpsify(playerUrl), + playerUrl, + subtitleCallback + ) { link -> + callback.invoke( + ExtractorLink( + link.source, + link.name + "", + link.url, + link.referer, + getQualityFromName("HD"), + link.isM3u8, + link.headers, + link.extractorData + ) + ) + } + } + + return true + } + + private fun Element.toSearchResponse(): SearchResponse { + val poster = select("div.cover > a > div.ma-lazy-wrapper") + var posterUrl = poster.select("img:last-child").attr("src") + if (posterUrl == "#") posterUrl = poster.select("img:last-child").attr("data-src") + val type = select("div.info > p.year").text() + val title = select("div.info > a.title > div.limit").text() + val link = fixUrl(select("div.cover > a").attr("href")) + if (type.contains("Film")) { + return newMovieSearchResponse( + title, + link, + TvType.AnimeMovie, + false, + ) { + this.posterUrl = posterUrl + } + + } else // an Anime + { + return newAnimeSearchResponse( + title, + link, + TvType.Anime, + false, + ) { + this.posterUrl = posterUrl + } + } + } + + data class LastEpisodeData( + @JsonProperty("time") val time: String?, + @JsonProperty("timestamp") val timestamp: Int?, + @JsonProperty("episode") val episode: String?, + @JsonProperty("icons") val icons: String?, + @JsonProperty("title") val title: String?, + @JsonProperty("lang") val lang: String?, + @JsonProperty("url") val url: String?, + @JsonProperty("anime_url") val anime_url: String?, + @JsonProperty("url_image") val url_image: String?, + @JsonProperty("url_bg") val url_bg: String, + ) + + private fun LastEpisodeData.tomainHome(): SearchResponse { + + var posterUrl = this.url_image?.replace("""\""", "") + val link = this.anime_url?.replace("""\""", "")?.let { fixUrl(it) } + ?: throw error("Error parsing") + val title = this.title ?: throw error("Error parsing") + val type = this.episode ?: "" + var lang = this.lang + val dubStatus = if (lang?.contains("vf") == true) { + DubStatus.Dubbed + } else { + DubStatus.Subbed + } + + if (type.contains("Ep")) { + return newAnimeSearchResponse( + title.take(15).replace("\n", "") + "\n" + type.replace("Ep", "Episode"), + link, + TvType.Anime, + false, + ) { + this.posterUrl = posterUrl + this.dubStatus = EnumSet.of(dubStatus) + + } + + } else // a movie + { + return newMovieSearchResponse( + title, + link, + TvType.AnimeMovie, + false, + ) { + this.posterUrl = posterUrl + } + } + } + + override val mainPage = mainPageOf( + Pair("$mainUrl", "Nouveaux épisodes"), + Pair("$mainUrl/anime-vf/", "Animes et Films en version français"), + Pair("$mainUrl/anime/", "Animes et Films sous-titrés en français"), + ) + + override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { + val categoryName = request.name + var cssSelector = "" + if (categoryName.contains("Nouveaux") && page <= 1) { + cssSelector = "div#main >script"//"div.js-last-episode-container > div.col-lg-3" + } + val url: String + url = if (page == 1) { + request.data + } else { + request.data + page + } + val document = app.get(url).document + + val regexLastEpisode = Regex("""lastEpisodes = (.*)\;""") + val home = when (!categoryName.isNullOrBlank()) { + request.name.contains("Animes") -> document.select("div#regular-list-animes > div.anime") + .mapNotNull { article -> article.toSearchResponse() } + else -> + tryParseJson>( + document.selectFirst( + cssSelector + )?.let { + regexLastEpisode.find( + it.toString() + )?.groupValues?.get(1) + } + )!!.map { episode -> episode.tomainHome() } + } + return newHomePageResponse(request.name, home) + } +} \ No newline at end of file diff --git a/NekosamaProvider/src/main/kotlin/com/lagradost/NekosamaProviderPlugin.kt b/NekosamaProvider/src/main/kotlin/com/lagradost/NekosamaProviderPlugin.kt new file mode 100644 index 0000000..a94d27a --- /dev/null +++ b/NekosamaProvider/src/main/kotlin/com/lagradost/NekosamaProviderPlugin.kt @@ -0,0 +1,17 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class NekosamaPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(NekosamaProvider()) + registerExtractorAPI(PstreamExtractor()) + + + } +} \ No newline at end of file diff --git a/NekosamaProvider/src/main/kotlin/com/lagradost/PstreamExtractor.kt b/NekosamaProvider/src/main/kotlin/com/lagradost/PstreamExtractor.kt new file mode 100644 index 0000000..ad93b03 --- /dev/null +++ b/NekosamaProvider/src/main/kotlin/com/lagradost/PstreamExtractor.kt @@ -0,0 +1,53 @@ +package com.lagradost + +import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.app + +import okio.ByteString.Companion.decodeBase64 + +open class PstreamExtractor : ExtractorApi() { + override val name: String = "Pstream" + override val mainUrl: String = "https://www.pstream.net" + override val requiresReferer = true + + override suspend fun getUrl(url: String, referer: String?): List? { + val refer = url + val headers = mapOf( + "Accept" to "*/*", + "Accept-Language" to "en-US,en;q=0.5", + ) + val document = app.get(url, headers = headers).document + + val scriptsourceUrl = + document.select("""script[src^="https://www.pstream.net/u/player-script?"]""") + .attr("src")//** Get the url where the scritp function is **/ + + val Scripdocument = + app.get(scriptsourceUrl, headers = headers).document//** Open the scritp function **/ + + val base64CodeRegex = + Regex("""e\.parseJSON\(atob\(t\)\.slice\(2\)\)\}\(\"(.*)\=\="\)\,n\=\"""") //** Search the code64 **/ + val code64 = base64CodeRegex.find(Scripdocument.toString())?.groupValues?.get(1) + + val decoded = code64?.decodeBase64()?.utf8() //** decode the code64 **/ + + val regexLink = Regex("""\"(https:\\\/\\\/[^"]*)""") //** Extract the m3u8 link **/ + val m3u8found = regexLink.find(decoded.toString())?.groupValues?.get(1) + var m3u8 = m3u8found.toString().replace("""\""", "") + + return listOf( + ExtractorLink( + name, + name, + m3u8, + refer, // voir si site demande le referer à mettre ici + Qualities.Unknown.value, + true, + headers = headers + + ) + ) + + } +} + diff --git a/README.md b/README.md index a377ccc..f207cae 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Cloudstream Non-English Plugin Repository +# Cloudstream Non-English Plugin Repository All available repositories: https://recloudstream.github.io/repos/ diff --git a/VostfreeProvider/build.gradle.kts b/VostfreeProvider/build.gradle.kts new file mode 100644 index 0000000..9e936a5 --- /dev/null +++ b/VostfreeProvider/build.gradle.kts @@ -0,0 +1,26 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "fr" + // All of these properties are optional, you can safely remove them + + description = " Ce site est certainement l’un des meilleurs sites permettant de regarder des animes en ligne et gratuitement. Il vous propose la version « VF » version française et la « VOSTFR » version originale Sous-titrée en Français." + authors = listOf("Eddy") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "Anime", + "AnimeMovie", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=vostfree.cx&sz=%size%" +} \ No newline at end of file diff --git a/VostfreeProvider/src/main/AndroidManifest.xml b/VostfreeProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/VostfreeProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/VostfreeProvider/src/main/kotlin/com/lagradost/MytvExtractor.kt b/VostfreeProvider/src/main/kotlin/com/lagradost/MytvExtractor.kt new file mode 100644 index 0000000..66c5d21 --- /dev/null +++ b/VostfreeProvider/src/main/kotlin/com/lagradost/MytvExtractor.kt @@ -0,0 +1,42 @@ +package com.lagradost +import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.app +import org.jsoup.Jsoup + + +open class MytvExtractor : ExtractorApi() { + override val name: String = "Mytv" + override val mainUrl: String = "https://www.myvi.tv/" + private val srcRegex = + Regex("""PlayerLoader\.CreatePlayer\(\"v\=(.*)\\u0026tp""") // would be possible to use the parse and find src attribute + override val requiresReferer = false + + + override suspend fun getUrl(url: String, referer: String?): List? { + val cleaned_url = url + val html = app.get(cleaned_url) + with(html) { // raised error ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED (3003) is due to the response: "error_nofile" + srcRegex.find(this.text)?.groupValues?.get(1)?.let { link -> + var lien = link + lien = lien.replace("%2f", "/").replace("%3a", ":").replace("%3f", "?") + .replace("%3d", "=").replace("%26", "&") + + //val html = app.get(url).text + //val document = Jsoup.parse(html) + //val link1 = document.select("script") + return listOf( + ExtractorLink( + name, + name, + lien, + cleaned_url, // voir si site demande le referer à mettre ici + Qualities.Unknown.value, + ) + ) + } + } + + return null + + } +} diff --git a/VostfreeProvider/src/main/kotlin/com/lagradost/SibnetExtractor.kt b/VostfreeProvider/src/main/kotlin/com/lagradost/SibnetExtractor.kt new file mode 100644 index 0000000..46bdccb --- /dev/null +++ b/VostfreeProvider/src/main/kotlin/com/lagradost/SibnetExtractor.kt @@ -0,0 +1,35 @@ + +package com.lagradost +import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.app +import org.jsoup.Jsoup + + +open class SibnetExtractor : ExtractorApi() { + override val name: String = "Sibnet" + override val mainUrl: String = "https://video.sibnet.ru" + private val srcRegex = + Regex("""player\.src\(\[\{src: \"(.*?)\"""") // would be possible to use the parse and find src attribute + override val requiresReferer = true + + + override suspend fun getUrl(url: String, referer: String?): List? { + val cleaned_url = url + val html = app.get(cleaned_url) + with(html) { // raised error ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED (3003) is due to the response: "error_nofile" + srcRegex.find(this.text)?.groupValues?.get(1)?.let { link -> + return listOf( + ExtractorLink( + name, + name, + mainUrl + link, + cleaned_url, // voir si site demande le referer à mettre ici + Qualities.Unknown.value, + ) + ) + } + } + + return null + } +} diff --git a/VostfreeProvider/src/main/kotlin/com/lagradost/VostfreeProvider.kt b/VostfreeProvider/src/main/kotlin/com/lagradost/VostfreeProvider.kt new file mode 100644 index 0000000..b41e261 --- /dev/null +++ b/VostfreeProvider/src/main/kotlin/com/lagradost/VostfreeProvider.kt @@ -0,0 +1,368 @@ +package com.lagradost + + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.utils.AppUtils.toJson +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import org.jsoup.nodes.Element +import java.util.* +import kotlin.collections.ArrayList + + +class VostfreeProvider : MainAPI() { + // VostFreeProvider() est ajouté à la liste allProviders dans MainAPI.kt + override var mainUrl = "https://vostfree.cx" + override var name = "Vostfree" + override val hasQuickSearch = false // recherche rapide (optionel, pas vraimet utile) + override val hasMainPage = true // page d'accueil (optionel mais encoragé) + override var lang = "fr" // fournisseur est en francais + override val supportedTypes = + setOf(TvType.Anime, TvType.AnimeMovie, TvType.OVA) // animes, animesfilms + // liste des types: https://recloudstream.github.io/dokka/app/com.lagradost.cloudstream3/-tv-type/index.html + + /** + Cherche le site pour un titre spécifique + + La recherche retourne une SearchResponse, qui peut être des classes suivants: AnimeSearchResponse, MovieSearchResponse, TorrentSearchResponse, TvSeriesSearchResponse + Chaque classes nécessite des données différentes, mais a en commun le nom, le poster et l'url + **/ + override suspend fun search(query: String): List { + val link = + "$mainUrl/index.php?do=search&subaction=search&story=$query&submit=Submit+Query" // L'url pour chercher un anime de dragon sera donc: 'https://vostfree.cx/index.php?story=dragon&do=search&subaction=search' + var mediaType = TvType.Anime + val document = + app.post(link).document // app.get() permet de télécharger la page html avec une requete HTTP (get) + return document.select("div.search-result") // on séléctione tous les éléments 'enfant' du type articles + .mapNotNull { div -> // map crée une liste des éléments (ici newMovieSearchResponse et newAnimeSearchResponse) + val type = + div?.selectFirst("div.genre") + ?.text() // replace enlève tous les '\t' et '\n' du titre + val mediaPoster = + div?.selectFirst("span.image > img")?.attr("src") + ?.let { fixUrl(it) } // récupère le texte de l'attribut src de l'élément + val href = div?.selectFirst("div.info > div.title > a")?.attr("href") + ?: throw ErrorLoadingException("invalid link") // renvoie une erreur si il n'y a pas de lien vers le média + val title = div.selectFirst("> div.info > div.title > a")?.text().toString() + val version = div.selectFirst("> div.info > ul > li")?.text().toString() + if (type == "OAV") mediaType = TvType.OVA + when (type) { + "FILM" -> ( + newMovieSearchResponse( // réponse du film qui sera ajoutée à la liste map qui sera ensuite return + title, + href, + TvType.AnimeMovie, + false + ) { + this.posterUrl = mediaPoster + // this.rating = rating + } + ) + null, "OAV" -> ( + newAnimeSearchResponse( + title, + href, + mediaType, + false + ) { + this.posterUrl = mediaPoster + this.dubStatus = + if (version.contains("VF")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of( + DubStatus.Subbed + ) + // this.rating = rating + } + + + ) + else -> { + throw ErrorLoadingException("invalid media type") // le type n'est pas reconnu ==> affiche une erreur + } + } + } + } + + /** + * charge la page d'informations, il ya toutes les donées, les épisodes, le résumé etc ... + * Il faut retourner soit: AnimeLoadResponse, MovieLoadResponse, TorrentLoadResponse, TvSeriesLoadResponse. + */ + data class EpisodeData( + @JsonProperty("url") val url: String, + @JsonProperty("episodeNumber") val episodeNumber: String, + ) + + + override suspend fun load(url: String): LoadResponse { + val document = app.get(url).document // récupere le texte sur la page (requète http) + // url est le lien retourné par la fonction search (la variable href) ou la fonction getMainPage + var mediaType = TvType.Anime + val episodes = ArrayList() + val urlSaison = ArrayList() + val meta = + document.selectFirst("div#dle-content > div.watch-top > div.image-bg > div.image-bg-content > div.slide-block ") + val description = meta?.select("div.slide-middle > div.slide-desc")?.first() + ?.text() // first() selectione le premier élément de la liste + var title = meta?.select("div.slide-middle > h1")?.text() + ?: "Invalid title" + title = title.replace("Saison", "").replace("saison", "").replace("SAISON", "") + .replace("Season", "").replace("season", "").replace("SEASON", "") + val poster = fixUrl( + meta?.select(" div.slide-poster > img") + ?.attr("src")!! + )// récupere le texte de l'attribut 'data-src' + var year = document.select("div.slide-info > p > b > a")?.text()?.toInt() + + urlSaison.add(url) + + + var seasonNumber: Int? = null + val otherSaisonFound = document.select("div.new_player_series_count > a") + otherSaisonFound.forEach { + urlSaison.add(it.attr("href")) + } + + urlSaison.apmap { urlseason -> + val document = + app.get(urlseason).document // récupere le texte sur la page (requète http) + + val meta = + document.selectFirst("div#dle-content > div.watch-top > div.image-bg > div.image-bg-content > div.slide-block ") + val poster_saison = mainUrl + meta?.select(" div.slide-poster > img") + ?.attr("src") + val seasontext = meta?.select("ul.slide-top > li:last-child > b:last-child")?.text() + var indication: String? = null + + if (!seasontext.isNullOrBlank() && !seasontext.contains("""([a-zA-Z])""".toRegex())) { + seasonNumber = seasontext.toInt() + + if (seasonNumber!! < 1) { // seem a an OVA has 0 as season number + seasonNumber = 1000 + indication = "Vous regardez un OVA" + } + } + + document.select(" select.new_player_selector > option").forEach { + val typeOftheAnime = it.text() + + if (typeOftheAnime != "Film") { + mediaType = TvType.Anime + val link = + EpisodeData( + urlseason, + typeOftheAnime.replace("Episode ", ""), + ).toJson() + episodes.add( + Episode( + link, + episode = typeOftheAnime.replace("Episode ", "").toInt(), + season = seasonNumber, + name = typeOftheAnime, + description = indication, + posterUrl = poster_saison + ) + ) + } else { + + mediaType = TvType.AnimeMovie + } + } + } + + if (mediaType == TvType.AnimeMovie) { + return newMovieLoadResponse( + title, + url, + mediaType, + url + ) { // retourne les informations du film + this.posterUrl = poster + this.plot = description + this.year = year + } + } else // an anime + { + return newAnimeLoadResponse( + title, + url, + mediaType, + ) { + this.posterUrl = poster + this.plot = description + this.year = year + addEpisodes( + if (title.contains("VF")) DubStatus.Dubbed else DubStatus.Subbed, + episodes + ) + + } + } + } + + + // récupere les liens .mp4 ou m3u8 directement à partir du paramètre data généré avec la fonction load() + override suspend fun loadLinks( + data: String, // fournit par load() + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit, + ): Boolean { + val parsedInfo = tryParseJson(data) + val url = parsedInfo?.url ?: data + + val noMovie = "1" + val numeroEpisode = parsedInfo?.episodeNumber + ?: noMovie // if is not a movie then take the episode number else for movie it is 1 + + val document = app.get(url).document + document.select("div.new_player_bottom") + .forEach { player_bottom -> // séléctione tous les players + + // supprimer les zéro de 0015 pour obtenir l'episode 15 + var index = numeroEpisode.indexOf('0') + var numero = numeroEpisode + while (index == 0) { + numero = numeroEpisode.drop(1) + index = numero.indexOf('0') + } + + val cssQuery = " div#buttons_$numero" // numero épisode + val buttonsNepisode = player_bottom?.select(cssQuery) + ?: throw ErrorLoadingException("Non player") //séléctione tous les players pour l'episode NoEpisode + buttonsNepisode.select("> div").apmap { + val player = it.attr("id") + .toString() //prend tous les players resultat : "player_2140" et "player_6521" + val playerName = it.select("div#$player") + .text() // prend le nom du player ex : "Uqload" et "Sibnet" + val codePlayload = + document.selectFirst("div#content_$player")?.text() + .toString() // result : "325544" ou "https:..." + var playerUrl = when (playerName) { + "VIP", "Upvid", "Dstream", "Streamsb", "Vudeo", "NinjaS", "Upstream" -> codePlayload // case https + "Uqload" -> "https://uqload.com/embed-$codePlayload.html" + "Mytv" -> "https://www.myvi.tv/embed/$codePlayload" + "Sibnet" -> "https://video.sibnet.ru/shell.php?videoid=$codePlayload" + "Stream" -> "https://myvi.ru/player/embed/html/$codePlayload" + else -> return@apmap + } + + + loadExtractor( + httpsify(playerUrl), + playerUrl, + subtitleCallback + ) { link -> // charge un extracteur d'extraire le lien direct .mp4 + callback.invoke( + ExtractorLink( // ici je modifie le callback pour ajouter des informations, normalement ce n'est pas nécessaire + link.source, + link.name + "", + link.url, + link.referer, + getQualityFromName("HD"), + link.isM3u8, + link.headers, + link.extractorData + ) + ) + } + // } + + } + + } + return true + } + + private fun Element.toSearchResponse(): SearchResponse { + val poster = select("span.image") + val posterUrl = fixUrl(poster.select("> img").attr("src")) + val subdub = select("div.quality").text() + val genre = select("div.genre").text() + val title = select("div.info > div.title").text() + val link = select("div.play > a").attr("href") + if (genre == "FILM") { + return newMovieSearchResponse( + title, + link, + TvType.AnimeMovie, + false, + ) { + this.posterUrl = posterUrl +//this.quality = quality + } + + } else // an Anime + { + return newAnimeSearchResponse( + title, + link, + TvType.Anime, + false, + ) { + this.posterUrl = posterUrl + this.dubStatus = + if (subdub == "VF") EnumSet.of(DubStatus.Dubbed) else EnumSet.of(DubStatus.Subbed) + } + } + } + + private fun Element.toSearchResponse1(): SearchResponse { + val poster = select("span.image") + val posterUrl = fixUrl(poster.select("> img").attr("src")) + val subdub = select("div.quality").text() + //val genre = select("div.info > ul.additional > li").text() + val title = select("div.info > div.title").text() + val link = select(" div.info > div.title > a").attr("href") + + return newAnimeSearchResponse( + title, + link, + TvType.Anime, + false, + ) { + this.posterUrl = posterUrl + this.dubStatus = + if (subdub == "VF") EnumSet.of(DubStatus.Dubbed) else EnumSet.of(DubStatus.Subbed) + } + + } + + override val mainPage = mainPageOf( + Pair("$mainUrl/last-episode.html/page/", "Nouveaux épisodes en Vostfr"), + Pair( + "$mainUrl/animes-vostfr-recement-ajoutees.html/page/", + "Animes Vostfr récemment ajoutés" + ), + Pair("$mainUrl/last-episode-vf.html/page/", "Nouveaux épisodes en français"), + Pair("$mainUrl/last-anime-vf.html/page/", "Animes VF récemment ajoutés"), + Pair("$mainUrl/animes-vf/page/", "Animes en version français"), + Pair("$mainUrl/animes-vostfr/page/", "Animes sous-titrés en français"), + Pair("$mainUrl/films-vf-vostfr/page/", "Films en Fr et Vostfr") + ) + + override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { + val categoryName = request.name + var cssSelector = "" + if (categoryName.contains("récemment")) { + cssSelector = "div#content > div.movie-poster" + } else { + cssSelector = "div#content > div#dle-content > div.movie-poster" + } + val url = request.data + page + val document = app.get(url).document + + val home = + when (!categoryName.isNullOrBlank()) { + request.name.contains("Nouveaux") -> document.select("div#content > div.last-episode") + .mapNotNull { article -> article.toSearchResponse1() } + else -> + document.select(cssSelector) + .mapNotNull { article -> // avec mapnotnull si un élément est null, il sera automatiquement enlevé de la liste + article.toSearchResponse() + } + } + return newHomePageResponse(request.name, home) + } + + +} \ No newline at end of file diff --git a/VostfreeProvider/src/main/kotlin/com/lagradost/VostfreeProviderPlugin.kt b/VostfreeProvider/src/main/kotlin/com/lagradost/VostfreeProviderPlugin.kt new file mode 100644 index 0000000..8083b33 --- /dev/null +++ b/VostfreeProvider/src/main/kotlin/com/lagradost/VostfreeProviderPlugin.kt @@ -0,0 +1,18 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class VostfreePlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(VostfreeProvider()) + registerExtractorAPI(VudeoExtractor()) + registerExtractorAPI(SibnetExtractor()) + registerExtractorAPI(MytvExtractor()) + + } +} \ No newline at end of file diff --git a/VostfreeProvider/src/main/kotlin/com/lagradost/VudeoExtractor.kt b/VostfreeProvider/src/main/kotlin/com/lagradost/VudeoExtractor.kt new file mode 100644 index 0000000..a0d4e54 --- /dev/null +++ b/VostfreeProvider/src/main/kotlin/com/lagradost/VudeoExtractor.kt @@ -0,0 +1,34 @@ + +package com.lagradost +import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.app + + +open class VudeoExtractor : ExtractorApi() { + override val name: String = "Vudeo" + override val mainUrl: String = "https://vudeo.io/" + private val srcRegex = + Regex("""sources\: \[\"(.*)\"""") // would be possible to use the parse and find src attribute + override val requiresReferer = false + + + override suspend fun getUrl(url: String, referer: String?): List? { + val cleaned_url = url + with(app.get(cleaned_url)) { // raised error ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED (3003) is due to the response: "error_nofile" + srcRegex.find(this.text)?.groupValues?.get(1)?.let { link -> + return listOf( + ExtractorLink( + name, + name, + link, + cleaned_url, // voir si site demande le referer à mettre ici + Qualities.Unknown.value, + ) + ) + } + } + return null + } +} + + diff --git a/WiflixProvider/build.gradle.kts b/WiflixProvider/build.gradle.kts new file mode 100644 index 0000000..096b9ae --- /dev/null +++ b/WiflixProvider/build.gradle.kts @@ -0,0 +1,26 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "fr" + // All of these properties are optional, you can safely remove them + + description = "WIFLIX, le site grâce auquel vous allez pouvoir regarder vos films et séries préférées" + authors = listOf("Eddy") + + /** + * 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=wiflix.zone&sz=%size%" +} \ No newline at end of file diff --git a/WiflixProvider/src/main/AndroidManifest.xml b/WiflixProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/WiflixProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/WiflixProvider/src/main/kotlin/com/lagradost/DoodStreamExtractor.kt b/WiflixProvider/src/main/kotlin/com/lagradost/DoodStreamExtractor.kt new file mode 100644 index 0000000..06b0277 --- /dev/null +++ b/WiflixProvider/src/main/kotlin/com/lagradost/DoodStreamExtractor.kt @@ -0,0 +1,35 @@ +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.getQualityFromName + + + +open class DoodStreamExtractor : ExtractorApi() { + override var name = "DoodStream" + override var mainUrl = "https://doodstream.com" + override val requiresReferer = false + + override fun getExtractorUrl(id: String): String { + return "$mainUrl/d/$id" + } + + override suspend fun getUrl(url: String, referer: String?): List? { + val response0 = app.get(url).text // html of DoodStream page to look for /pass_md5/... + val md5 =mainUrl+(Regex("/pass_md5/[^']*").find(response0)?.value ?: return null) // get https://dood.ws/pass_md5/... + val trueUrl = app.get(md5, referer = url).text + "zUEJeL3mUN?token=" + md5.substringAfterLast("/") //direct link to extract (zUEJeL3mUN is random) + val quality = Regex("\\d{3,4}p").find(response0.substringAfter("").substringBefore(""))?.groupValues?.get(0) + return listOf( + ExtractorLink( + trueUrl, + this.name, + trueUrl, + mainUrl, + getQualityFromName(quality), + false + ) + ) // links are valid in 8h + + } +} \ No newline at end of file diff --git a/WiflixProvider/src/main/kotlin/com/lagradost/StreamSBPlusExtractor.kt b/WiflixProvider/src/main/kotlin/com/lagradost/StreamSBPlusExtractor.kt new file mode 100644 index 0000000..6db5221 --- /dev/null +++ b/WiflixProvider/src/main/kotlin/com/lagradost/StreamSBPlusExtractor.kt @@ -0,0 +1,81 @@ +package com.lagradost + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.SubtitleFile +import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.utils.ExtractorApi +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.M3u8Helper + + +// This is a modified version of https://github.com/jmir1/aniyomi-extensions/blob/master/src/en/genoanime/src/eu/kanade/tachiyomi/animeextension/en/genoanime/extractors/StreamSBExtractor.kt +// The following code is under the Apache License 2.0 https://github.com/jmir1/aniyomi-extensions/blob/master/LICENSE +open class StreamSBPlusExtractor : ExtractorApi() { + override var name = "StreamSB" + override var mainUrl = "https://sbspeed.com" + override val requiresReferer = false + + private val hexArray = "0123456789ABCDEF".toCharArray() + + private fun bytesToHex(bytes: ByteArray): String { + val hexChars = CharArray(bytes.size * 2) + for (j in bytes.indices) { + val v = bytes[j].toInt() and 0xFF + + hexChars[j * 2] = hexArray[v ushr 4] + hexChars[j * 2 + 1] = hexArray[v and 0x0F] + } + return String(hexChars) + } + + data class Subs ( + @JsonProperty("file") val file: String, + @JsonProperty("label") val label: String, + ) + + data class StreamData ( + @JsonProperty("file") val file: String, + @JsonProperty("cdn_img") val cdnImg: String, + @JsonProperty("hash") val hash: String, + @JsonProperty("subs") val subs: List?, + @JsonProperty("length") val length: String, + @JsonProperty("id") val id: String, + @JsonProperty("title") val title: String, + @JsonProperty("backup") val backup: String, + ) + + data class Main ( + @JsonProperty("stream_data") val streamData: StreamData, + @JsonProperty("status_code") val statusCode: Int, + ) + + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val regexID = + Regex("(embed-[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+|/e/[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)") + val id = regexID.findAll(url).map { + it.value.replace(Regex("(embed-|/e/)"), "") + }.first() +// val master = "$mainUrl/sources48/6d6144797752744a454267617c7c${bytesToHex.lowercase()}7c7c4e61755a56456f34385243727c7c73747265616d7362/6b4a33767968506e4e71374f7c7c343837323439333133333462353935333633373836643638376337633462333634663539343137373761333635313533333835333763376333393636363133393635366136323733343435323332376137633763373337343732363536313664373336327c7c504d754478413835306633797c7c73747265616d7362" + val master = "$mainUrl/sources48/" + bytesToHex("||$id||||streamsb".toByteArray()) + "/" + val headers = mapOf( + "watchsb" to "sbstream", + ) + val mapped = app.get( + master.lowercase(), + headers = headers, + referer = url, + ).parsedSafe
() + // val urlmain = mapped.streamData.file.substringBefore("/hls/") + M3u8Helper.generateM3u8( + name, + mapped?.streamData?.file ?: return, + url, + headers = headers + ).forEach(callback) + } +} \ No newline at end of file diff --git a/WiflixProvider/src/main/kotlin/com/lagradost/WiflixProvider.kt b/WiflixProvider/src/main/kotlin/com/lagradost/WiflixProvider.kt new file mode 100644 index 0000000..77a55d7 --- /dev/null +++ b/WiflixProvider/src/main/kotlin/com/lagradost/WiflixProvider.kt @@ -0,0 +1,307 @@ +package com.lagradost + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.utils.AppUtils.toJson +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import org.jsoup.nodes.Element +import org.jsoup.select.Elements +import kotlin.collections.ArrayList + +class WiflixProvider : MainAPI() { + + + override var mainUrl = "https://wiflix.zone" + override var name = "Wiflix" + override val hasQuickSearch = false // recherche rapide (optionel, pas vraimet utile) + override val hasMainPage = true // page d'accueil (optionel mais encoragé) + override var lang = "fr" // fournisseur est en francais + override val supportedTypes = + setOf(TvType.Movie, TvType.TvSeries) // series, films + // liste des types: https://recloudstream.github.io/dokka/app/com.lagradost.cloudstream3/-tv-type/index.html + + /** + Cherche le site pour un titre spécifique + + La recherche retourne une SearchResponse, qui peut être des classes suivants: AnimeSearchResponse, MovieSearchResponse, TorrentSearchResponse, TvSeriesSearchResponse + Chaque classes nécessite des données différentes, mais a en commun le nom, le poster et l'url + **/ + override suspend fun search(query: String): List { + val link = + "$mainUrl/index.php?do=search&subaction=search&search_start=0&full_search=1&result_from=1&story=$query&titleonly=3&searchuser=&replyless=0&replylimit=0&searchdate=0&beforeafter=after&sortby=date&resorder=desc&showposts=0&catlist%5B%5D=0" // search' + val document = + app.post(link).document // app.get() permet de télécharger la page html avec une requete HTTP (get) + val results = document.select("div#dle-content > div.clearfix") + + val allresultshome = + results.mapNotNull { article -> // avec mapnotnull si un élément est null, il sera automatiquement enlevé de la liste + article.toSearchResponse() + } + return allresultshome + } + + /** + * charge la page d'informations, il ya toutes les donées, les épisodes, le résumé etc ... + * Il faut retourner soit: AnimeLoadResponse, MovieLoadResponse, TorrentLoadResponse, TvSeriesLoadResponse. + */ + data class EpisodeData( + @JsonProperty("url") val url: String, + @JsonProperty("episodeNumber") val episodeNumber: String, + ) + + private fun Elements.takeEpisode(url: String, duborSub: String?): ArrayList { + + val episodes = ArrayList() + this.select("ul.eplist > li").forEach { + + val strEpisode = it.text() + val strEpisodeN = strEpisode.replace("Episode ", "") + val link = + EpisodeData( + url, + strEpisodeN, + ).toJson() + + + episodes.add( + Episode( + link, + name = duborSub, + episode = strEpisodeN.toInt(), + ) + ) + } + + return episodes + } + + override suspend fun load(url: String): LoadResponse { + val document = app.get(url).document // + // url est le lien retourné par la fonction search (la variable href) ou la fonction getMainPage + + var episodes = ArrayList() + var mediaType: TvType + val episodeFrfound = + document.select("div.blocfr") + + val episodeVostfrfound = + document.select("div.blocvostfr") + val title = + document.select("h1[itemprop]").text() + val posterUrl = + document.select("img#posterimg").attr("src") + val yearRegex = Regex("""ate de sortie\: (\d*)""") + val year = yearRegex.find(document.text())?.groupValues?.get(1) + + + val tags = document.select("[itemprop=genre] > a") + .map { it.text() } // séléctione tous les tags et les ajoutes à une liste + + if (episodeFrfound.text().contains("Episode")) { + mediaType = TvType.TvSeries + val duborSub = "Episode en VF" + episodes = episodeFrfound.takeEpisode(url, duborSub) + } else if (episodeVostfrfound.text().contains("Episode")) { + mediaType = TvType.TvSeries + val duborSub = "Episode sous-titré" + episodes = episodeVostfrfound.takeEpisode(url, duborSub) + } else { + + mediaType = TvType.Movie + } + /////////////////////////////////////////// + /////////////////////////////////////////// + var type_rec: TvType + val recommendations = + document.select("div.clearfixme > div > div")?.mapNotNull { element -> + val recTitle = + element.select("a").text() ?: return@mapNotNull null + val image = element.select("a >img")?.attr("src") + val recUrl = element.select("a").attr("href") + type_rec = TvType.TvSeries + if (recUrl.contains("film")) type_rec = TvType.Movie + + if (type_rec == TvType.TvSeries) { + TvSeriesSearchResponse( + recTitle, + recUrl, + this.name, + TvType.TvSeries, + image?.let { fixUrl(it) }, + + ) + } else + MovieSearchResponse( + recTitle, + recUrl, + this.name, + TvType.Movie, + image?.let { fixUrl(it) }, + + ) + + } + + var comingSoon = url.contains("films-prochainement") + + + if (mediaType == TvType.Movie) { + val description = document.selectFirst("div.screenshots-full")?.text() + ?.replace("(.* .ynopsis)".toRegex(), "") + return newMovieLoadResponse( + name = title, + url = url, + type = TvType.Movie, + dataUrl = url + + ) { + this.posterUrl = fixUrl(posterUrl) + this.plot = description + this.recommendations = recommendations + this.year = year?.toIntOrNull() + this.comingSoon = comingSoon + this.tags = tags + } + } else { + val description = document.selectFirst("span[itemprop=description]")?.text() + return newTvSeriesLoadResponse( + title, + url, + mediaType, + episodes + ) { + this.posterUrl = fixUrl(posterUrl) + this.plot = description + this.recommendations = recommendations + this.year = year?.toIntOrNull() + this.comingSoon = comingSoon + this.tags = tags + + } + } + } + + + // récupere les liens .mp4 ou m3u8 directement à partir du paramètre data généré avec la fonction load() + override suspend fun loadLinks( + data: String, // fournit par load() + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit, + ): Boolean { + val parsedInfo = + tryParseJson(data) + val url = parsedInfo?.url ?: data + + val numeroEpisode = parsedInfo?.episodeNumber ?: null + + val document = app.get(url).document + val episodeFrfound = + document.select("div.blocfr") + val episodeVostfrfound = + document.select("div.blocvostfr") + + val cssCodeForPlayer = if (episodeFrfound.text().contains("Episode")) { + "div.ep${numeroEpisode}vf > a" + } else if (episodeVostfrfound.text().contains("Episode")) { + "div.ep${numeroEpisode}vs > a" + } else { + "div.linkstab > a" + } + + + document.select("$cssCodeForPlayer").apmap { player -> // séléctione tous les players + var playerUrl = "https"+player.attr("href").replace("(.*)https".toRegex(), "") + if (!playerUrl.isNullOrBlank()) + if (playerUrl.contains("dood")) { + playerUrl = playerUrl.replace("doodstream.com", "dood.wf") + } + loadExtractor( + httpsify(playerUrl), + playerUrl, + subtitleCallback + ) { link -> + callback.invoke( + ExtractorLink( // ici je modifie le callback pour ajouter des informations, normalement ce n'est pas nécessaire + link.source, + link.name + "", + link.url, + link.referer, + getQualityFromName("HD"), + link.isM3u8, + link.headers, + link.extractorData + ) + ) + } + } + + + return true + } + + private fun Element.toSearchResponse(): SearchResponse { + + val posterUrl = fixUrl(select("div.img-box > img").attr("src")) + val qualityExtracted = select("div.nbloc1-2 >span").text() + val type = select("div.nbloc3").text() + val title = select("a.nowrap").text() + val link = select("a.nowrap").attr("href") + var quality = when (!qualityExtracted.isNullOrBlank()) { + qualityExtracted.contains("HDLight") -> getQualityFromString("HD") + qualityExtracted.contains("Bdrip") -> getQualityFromString("BlueRay") + qualityExtracted.contains("DVD") -> getQualityFromString("DVD") + qualityExtracted.contains("CAM") -> getQualityFromString("Cam") + + else -> null + } + if (type.contains("Film")) { + return MovieSearchResponse( + name = title, + url = link, + apiName = title, + type = TvType.Movie, + posterUrl = posterUrl, + quality = quality + + ) + + + } else // an Serie + { + + return TvSeriesSearchResponse( + name = title, + url = link, + apiName = title, + type = TvType.TvSeries, + posterUrl = posterUrl, + quality = quality, + // + ) + + } + } + + override val mainPage = mainPageOf( + Pair("$mainUrl/films-prochainement/page/", "Film Prochainement en Streaming"), + Pair("$mainUrl/film-en-streaming/page/", "Top Films cette année"), + Pair("$mainUrl/serie-en-streaming/page/", "Top Séries cette année"), + Pair("$mainUrl/saison-complete/page/", "Les saisons complètes"), + Pair("$mainUrl/film-ancien/page/", "Film zahalé (ancien)") + ) + + override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { + val url = request.data + page + val document = app.get(url).document + val movies = document.select("div#dle-content > div.clearfix") + + val home = + movies.mapNotNull { article -> // avec mapnotnull si un élément est null, il sera automatiquement enlevé de la liste + article.toSearchResponse() + } + return newHomePageResponse(request.name, home) + } + +} diff --git a/WiflixProvider/src/main/kotlin/com/lagradost/WiflixProviderPlugin.kt b/WiflixProvider/src/main/kotlin/com/lagradost/WiflixProviderPlugin.kt new file mode 100644 index 0000000..7e90a52 --- /dev/null +++ b/WiflixProvider/src/main/kotlin/com/lagradost/WiflixProviderPlugin.kt @@ -0,0 +1,18 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class WiflixPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(WiflixProvider()) + registerExtractorAPI(DoodStreamExtractor()) + registerExtractorAPI(StreamSBPlusExtractor()) + + + } +} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 7d6e19e..122837f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -84,6 +84,8 @@ subprojects { //run JS implementation("org.mozilla:rhino:1.7.14") + // Library/extensions searching with Levenshtein distance + implementation ("me.xdrop:fuzzywuzzy:1.4.0") } }