diff --git a/FrenchStreamProvider/src/main/kotlin/com/lagradost/FrenchStreamProvider.kt b/FrenchStreamProvider/src/main/kotlin/com/lagradost/FrenchStreamProvider.kt index 05aef23..0028a3f 100644 --- a/FrenchStreamProvider/src/main/kotlin/com/lagradost/FrenchStreamProvider.kt +++ b/FrenchStreamProvider/src/main/kotlin/com/lagradost/FrenchStreamProvider.kt @@ -1,20 +1,25 @@ package com.lagradost +import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer +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.extractorApis +import com.lagradost.cloudstream3.utils.loadExtractor import org.jsoup.nodes.Element class FrenchStreamProvider : MainAPI() { - override var mainUrl = "https://french-stream.cx" //re ou ac ou city + override var mainUrl = "https://streem.re" //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.Movie, TvType.TvSeries) + override suspend fun search(query: String): List { val link = "$mainUrl/?do=search&subaction=search&story=$query" // search' val document = @@ -22,21 +27,60 @@ class FrenchStreamProvider : MainAPI() { val results = document.select("div#dle-content > > div.short") val allresultshome = - results.apmap { article -> // avec mapnotnull si un élément est null, il sera automatiquement enlevé de la liste + results.mapNotNull { article -> // avec mapnotnull si un élément est null, il sera automatiquement enlevé de la liste article.toSearchResponse() } return allresultshome } + private fun Element.takeEpisode( + url: String, + ): List { + return this.select("a").map { a -> + val epNum = + Regex("""pisode[\s]+(\d+)""").find(a.text().lowercase())?.groupValues?.get(1) + ?.toIntOrNull() + val epTitle = if (a.text().contains("Episode")) { + val type = if ("honey" in a.attr("id")) { + "VF" + } else { + "Vostfr" + } + "Episode $type" + } else { + a.text() + } + + Episode( + loadLinkData( + fixUrl(url), + epTitle.contains("Vostfr"), + epNum, + ).toJson(), + epTitle, + null, + epNum, + a.selectFirst("div.fposter > img")?.attr("src"), + ) + } + } + + data class loadLinkData( + val embedUrl: String, + val isVostFr: Boolean? = null, + val episodenumber: Int? = null, + ) + override suspend fun load(url: String): LoadResponse { val soup = app.get(url).document - + var subEpisodes = listOf() + var dubEpisodes = listOf() val title = soup.selectFirst("h1#s-title")!!.text().toString() - val isMovie = !title.contains("saison", ignoreCase = true) + val isMovie = !url.contains("/serie/", ignoreCase = true) val description = soup.selectFirst("div.fdesc")!!.text().toString() .split("streaming", ignoreCase = true)[1].replace(":", "") - var poster = soup.selectFirst("div.fposter > img")?.attr("src") + val 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() @@ -48,7 +92,7 @@ class FrenchStreamProvider : MainAPI() { ?.mapNotNull { // all the tags like action, thriller ...; unused variable it?.text() } - return newMovieLoadResponse(title, url, TvType.Movie, url) { + return newMovieLoadResponse(title, url, TvType.Movie, loadLinkData(url)) { this.posterUrl = poster this.year = year?.toIntOrNull() this.tags = tagsList @@ -56,56 +100,26 @@ class FrenchStreamProvider : MainAPI() { //this.rating = rating addTrailer(soup.selectFirst("button#myBtn > a")?.attr("href")) } - } else // a tv serie - { - - 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" - } - "Episode " + type - } else { - a.text() - } - if (poster == null) { - poster = a.selectFirst("div.fposter > img")?.attr("src") - } - Episode( - fixUrl(url).plus("-episodenumber:$epNum"), - epTitle, - null, - epNum, - null, // episode Thumbnail - null // episode date - ) + if (" a")?.attr("href")) + if (subEpisodes.isNotEmpty()) addEpisodes(DubStatus.Subbed, subEpisodes) + if (dubEpisodes.isNotEmpty()) addEpisodes(DubStatus.Dubbed, dubEpisodes) } } } @@ -126,23 +140,26 @@ class FrenchStreamProvider : MainAPI() { } } - override suspend fun loadLinks( + + override suspend fun loadLinks( // TODO FIX *Garbage* data transmission betwenn function data: String, isCasting: Boolean, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit, ): Boolean { + val parsedData = tryParseJson(data) + val url = parsedData?.embedUrl ?: return false val servers = - if (data.contains("-episodenumber:"))// It's a serie: + if (parsedData.episodenumber != null)// It's a serie: { - val split = - data.split("-episodenumber:") // the data contains the url and the wanted episode number (a temporary dirty fix that will last forever) - val url = split[0] + val isvostfr = parsedData.isVostFr == true + + val wantedEpisode = - if (split[1] == "2") { // the episode number 2 has id of ABCDE, don't ask any question + if (parsedData.episodenumber.toString() == "2") { // the episode number 2 has id of ABCDE, don't ask any question "ABCDE" } else { - "episode" + split[1] + "episode" + parsedData.episodenumber.toString() } @@ -160,7 +177,7 @@ class FrenchStreamProvider : MainAPI() { // val litext = li.text() if (serverUrl.isNotBlank()) { if (li.text().replace(" ", "").replace(" ", "").isNotBlank()) { - Pair(li.text().replace(" ", ""), "vf" + fixUrl(serverUrl)) + Pair(li.text().replace(" ", ""), fixUrl(serverUrl)) } else { null } @@ -169,14 +186,14 @@ class FrenchStreamProvider : MainAPI() { } } - val translated = translate(split[1], serversvf.isNotEmpty()) + val translated = translate(parsedData.episodenumber.toString(), serversvf.isNotEmpty()) val serversvo = // Original version servers soup.select("div#$translated > div.selink > ul.btnss $div> li") .mapNotNull { li -> val serverUrl = fixUrlNull(li.selectFirst("a")?.attr("href")) if (!serverUrl.isNullOrEmpty()) { if (li.text().replace(" ", "").isNotBlank()) { - Pair(li.text().replace(" ", ""), "vo" + fixUrl(serverUrl)) + Pair(li.text().replace(" ", ""), fixUrl(serverUrl)) } else { null } @@ -184,10 +201,14 @@ class FrenchStreamProvider : MainAPI() { null } } - serversvf + serversvo + if (isvostfr) { + serversvo + } else { + serversvf + } } else { // it's a movie val movieServers = - app.get(fixUrl(data)).document.select("nav#primary_nav_wrap > ul > li > ul > li > a") + app.get(fixUrl(url)).document.select("nav#primary_nav_wrap > ul > li > ul > li > a") .mapNotNull { a -> val serverurl = fixUrlNull(a.attr("href")) ?: return@mapNotNull null val parent = a.parents()[2] @@ -200,33 +221,19 @@ class FrenchStreamProvider : MainAPI() { } movieServers } - servers.apmap { - for (extractor in extractorApis) { - var playerName = it.first + val urlplayer = it.second - 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 - } - } + val playerUrl = if (urlplayer.contains("opsktp.com") || urlplayer.contains("flixeo.xyz")) { + val header = app.get( + "https" + it.second.split("https")[1], + allowRedirects = false + ).headers + header["location"].toString() + } else { + urlplayer + }.replace("https://doodstream.com", "https://dood.yt") + loadExtractor(playerUrl, mainUrl, subtitleCallback, callback) } return true @@ -237,19 +244,20 @@ class FrenchStreamProvider : MainAPI() { 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 type = select("span.mli-eps").text().lowercase() 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") + val quality = getQualityFromString( + when (!qualityExtracted.isNullOrBlank()) { + qualityExtracted.contains("HDLight") -> "HD" + qualityExtracted.contains("Bdrip") -> "BlueRay" + qualityExtracted.contains("DVD") -> "DVD" + qualityExtracted.contains("CAM") -> "Cam" + else -> null + } + ) - else -> null - } - - if (type.contains("Eps", false)) { + if (!type.contains("eps")) { return MovieSearchResponse( name = title, url = link, @@ -261,35 +269,43 @@ class FrenchStreamProvider : MainAPI() { ) - } else // an Serie + } else // a Serie { - - return TvSeriesSearchResponse( + return newAnimeSearchResponse( name = title, url = link, - apiName = title, type = TvType.TvSeries, - posterUrl = posterUrl, - quality = quality, - // - ) + + ) { + this.posterUrl = posterUrl + addDubStatus( + isDub = select("span.film-verz").text().uppercase().contains("VF"), + episodes = select("span.mli-eps>i").text().toIntOrNull() + ) + } + } } + data class mediaData( + @JsonProperty("title") var title: String, + @JsonProperty("url") val url: String, + ) + 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") + Pair("/xfsearch/version-film/page/", "Derniers Films"), + Pair("/xfsearch/version-serie/page/", "Dernieres Séries"), + Pair("/film/arts-martiaux/page/", "Films Arts martiaux"), + Pair("/film/action/page/", "Films Action"), + Pair("/film/romance/page/", "Films Romance"), + Pair("/serie/aventure-serie/page/", "Séries aventure"), + Pair("/film/documentaire/page/", "Documentaires") ) override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { - val url = request.data + page + val url = mainUrl + request.data + page val document = app.get(url).document val movies = document.select("div#dle-content > div.short") @@ -299,5 +315,5 @@ class FrenchStreamProvider : MainAPI() { } 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 ac71bb4..4dcc3ad 100644 --- a/FrenchStreamProvider/src/main/kotlin/com/lagradost/FrenchStreamProviderPlugin.kt +++ b/FrenchStreamProvider/src/main/kotlin/com/lagradost/FrenchStreamProviderPlugin.kt @@ -10,6 +10,5 @@ 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 deleted file mode 100644 index 32f5517..0000000 --- a/FrenchStreamProvider/src/main/kotlin/com/lagradost/VidoExtractor.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.lagradost - -import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.utils.ExtractorApi -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.cloudstream3.utils.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/MesFilmsProvider/build.gradle.kts b/MesFilmsProvider/build.gradle.kts new file mode 100644 index 0000000..a92aae8 --- /dev/null +++ b/MesFilmsProvider/build.gradle.kts @@ -0,0 +1,25 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "fr" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + authors = listOf("Sarlay") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "Movie", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=mesfilms.lol&sz=%size%" +} diff --git a/MesFilmsProvider/src/main/AndroidManifest.xml b/MesFilmsProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/MesFilmsProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/MesFilmsProvider/src/main/kotlin/com/lagradost/MesFilmsProvider.kt b/MesFilmsProvider/src/main/kotlin/com/lagradost/MesFilmsProvider.kt new file mode 100644 index 0000000..9f2e304 --- /dev/null +++ b/MesFilmsProvider/src/main/kotlin/com/lagradost/MesFilmsProvider.kt @@ -0,0 +1,301 @@ +package com.lagradost + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.LoadResponse.Companion.addRating +import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer +import com.lagradost.cloudstream3.metaproviders.CrossTmdbProvider +import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.utils.AppUtils.parseJson +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import org.jsoup.Jsoup + + +class MesFilmsProvider : MainAPI() { + override var mainUrl = "https://mesfilms.lol" + override var name = "Mes Films" + 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) // ici on ne supporte que les films + // override val supportedTypes = setOf(TvType.Movie, TvType.TvSeries) // films et series + // 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/?s=$query" + val document = app.get(link).document // on convertit le html en un document + return document.select("div.search-page > div.result-item > article") // on séléctione tous les éléments 'enfant' du type articles + .filter { article -> // on filtre la liste obtenue de tous les articles + val type = article?.selectFirst("> div.image > div.thumbnail > a > span")?.text() + ?.replace("\t", "")?.replace("\n", "") + + type != "Épisode" // enlève tous les éléments qui sont des épisodes de série + }.mapNotNull { div -> // apmap crée une liste des éléments (ici newMovieSearchResponse et newTvSeriesSearchResponse) + val posterContainer = div.selectFirst("> div.image > div.thumbnail > a") // selectione le premier élément correspondant à ces critères + val type = posterContainer?.selectFirst("> span")?.text()?.replace("\t", "")?.replace("\n", "") + // replace enlève tous les '\t' et '\n' du titre + val mediaPoster = posterContainer?.selectFirst("> img")?.attr("src") // récupère le texte de l'attribut src de l'élément + + val href = posterContainer?.attr("href") ?: return@mapNotNull null // renvoie une erreur si il n'y a pas de lien vers le média + // val test1 = stringVariable ?: "valeur par défault" + // val test2 = stringVariable ?: doSomething() // si stringVariable est null, on appelle la fonction doSomething + // '?:' est appelé Elvis Operator, si la variable stringVariable est null, alors on utilise la "valeur par défault" + // https://stackoverflow.com/questions/48253107/what-does-do-in-kotlin-elvis-operator + // val details = div.select("> div.details > div.meta") + //val rating = details.select("> span.rating").text() + // val year = details.select("> span.year").text().toIntOrNull() + + val title = div.selectFirst("> div.details > div.title > a")?.text().toString() + + when (type) { + "FILM" -> ( + newMovieSearchResponse( // réponse du film qui sera ajoutée à la liste map qui sera ensuite return + title, + href, + TvType.Movie, + false + ) { + this.posterUrl = mediaPoster + // this.year = year + // this.rating = rating + } + ) + else -> { + return@mapNotNull null // le type n'est pas reconnu ==> enlever + } + } + } + } + + private data class EmbedUrlClass( + @JsonProperty("embed_url") val url: String?, + ) + + /** + * 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 { + // url est le lien retourné par la fonction search (la variable href) ou la fonction getMainPage + val document = app.get(url).document // convertit en document + + val meta = document.selectFirst("div.sheader") + val poster = meta?.select("div.poster > img")?.attr("data-src") // récupere le texte de l'attribut 'data-src' + + val title = meta?.select("div.data > h1")?.text() ?: throw ErrorLoadingException("Invalid title") + + val data = meta.select("div.data") + val extra = data.select("div.extra") + + val description = extra.select("span.tagline").first()?.text() // first() selectione le premier élément de la liste + + val rating1 = document.select("div.custom_fields > span.valor > b#repimdb > strong").text() + val rating2 = document.select("div.custom_fields > span.valor > strong").text() + + val rating = when { + !rating1.isNullOrBlank() -> rating1 + !rating2.isNullOrBlank() -> rating2 + else -> null + } + + + val date = extra.select("span.date").first()?.text()?.takeLast(4) // prends les 4 dernier chiffres + + val tags = data.select("div.sgeneros > a").apmap {it.text()} // séléctione tous les tags et les ajoutes à une liste + + val postId = document.select("#report-video-button-field > input[name=postID]").first()?.attr("value") // élémennt spécifique à ce site + + val mediaType = if(url.contains("/film/")) { + "movie" + } else { + "TV" + } + // un api est disponible sur ce site pour récupérer le trailer (lien vers youtube) + val trailerUrl = postId?.let{ + try { + val payloadRequest = mapOf( + "action" to "doo_player_ajax", + "post" to postId, + "nume" to "trailer", + "type" to mediaType + ) // on crée les donées de la requetes ici + val getTrailer = + app.post( + "$mainUrl/wp-admin/admin-ajax.php", + headers = mapOf("Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8"), + data = payloadRequest + ).text + parseJson(getTrailer).url + // parseJson lit le contenu de la réponse (la variable getTrailer) et cherche la valeur d'embed_url dans cette réponse + + } catch (e: Exception){ + null + } + } + + + if (mediaType == "movie") { + return newMovieLoadResponse( + title, + url, + TvType.Movie, + url + ) { // retourne les informations du film + this.posterUrl = poster + addRating(rating) + this.year = date?.toIntOrNull() + this.tags = tags + this.plot = description + trailerUrl?.let{addTrailer(trailerUrl)} + } + } else // a tv serie + { + throw ErrorLoadingException("Nothing besides movies are implemented for this provider") + } + } + + + // 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 document = app.get(data).document + + document.select("ul#playeroptionsul > li:not(#player-option-trailer)").apmap { li -> // séléctione tous les players sauf celui avec l'id player-option-trailer + // https://jsoup.org/cookbook/extracting-data/selector-syntax + val quality = li.selectFirst("span.title")?.text() + val server = li.selectFirst("> span.server")?.text() + val languageInfo = + + li.selectFirst("span.flag > img")?.attr("data-src") + ?.substringAfterLast("/") // séléctione la partie de la chaine de caractère apr_s le dernier / + ?.replace(".png", "") ?: "" + + val postId = li.attr("data-post") + + val indexOfPlayer = li.attr("data-nume") + + // un api est disponible sur le site pour récupéré les liens vers des embed (iframe) type uqload + val payloadRequest = mapOf( + "action" to "doo_player_ajax", + "post" to postId, + "nume" to indexOfPlayer, + "type" to "movie" + ) + + + val getPlayerEmbed = + app.post( + "$mainUrl/wp-admin/admin-ajax.php", + headers = mapOf("Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8"), + data = payloadRequest + ).text + val playerUrl = parseJson(getPlayerEmbed).url // récupère l'url de l'embed en lisant le json + + if (playerUrl != null) + 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 + " $languageInfo", + link.url, + link.referer, + getQualityFromName(quality), + link.isM3u8, + link.headers, + link.extractorData + )) + } + } + + + return true + } + + + override val mainPage = mainPageOf( + Pair("$mainUrl/film/", "Récemment ajouté"), + Pair("$mainUrl/tendance/?get=movies", "Tendance"), + Pair("$mainUrl/evaluations/?get=movies", "Les plus notés"), + Pair("$mainUrl/films-classiques/", "Quelques classiques"), + ) + + override suspend fun getMainPage( + page: Int, + request: MainPageRequest + ): HomePageResponse { + val document = app.get(request.data).document + val movies = document.select("div.items > article.movies") + val categoryTitle = request.name + val returnList = movies.mapNotNull { article -> + // ici si un élément est null, il sera automatiquement enlevé de la liste + val poster = article.select("div.poster") + val posterUrl = poster.select("> img").attr("data-src") + val qualityExtracted = poster.select("> div.mepo > span.quality").text().uppercase() + + val quality = getQualityFromString( + when (!qualityExtracted.isNullOrBlank()) { + qualityExtracted.contains("WEBRIP") -> { + "WebRip" + } + qualityExtracted.contains("HDRIP") -> { + "HD" + } + qualityExtracted.contains("HDLIGHT") -> { + "HD" + } + qualityExtracted.contains("BDRIP") -> { + "BlueRay" + } + qualityExtracted.contains("DVD") -> { + "DVD" + } + qualityExtracted.contains("CAM") -> { + "Cam" + } + + else -> { + null + } + } + ) + + val data = article.select("div.data") + val title = data.select("> h3 > a").text() + val link = data.select("> h3 > a").attr("href") + newMovieSearchResponse( + title, + link, + TvType.Movie, + false, + ) { + this.posterUrl = posterUrl + this.quality = quality + } + } + if (returnList.isEmpty()) throw ErrorLoadingException("No movies") + return HomePageResponse( + listOf( + HomePageList(categoryTitle, returnList) + ) + ) + } +} diff --git a/MesFilmsProvider/src/main/kotlin/com/lagradost/MesFilmsProviderPlugin.kt b/MesFilmsProvider/src/main/kotlin/com/lagradost/MesFilmsProviderPlugin.kt new file mode 100644 index 0000000..7fb680c --- /dev/null +++ b/MesFilmsProvider/src/main/kotlin/com/lagradost/MesFilmsProviderPlugin.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 MesFilmsProviderPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(MesFilmsProvider()) + } +} \ No newline at end of file