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/WiflixProvider.kt b/WiflixProvider/src/main/kotlin/com/lagradost/WiflixProvider.kt new file mode 100644 index 0000000..7df5817 --- /dev/null +++ b/WiflixProvider/src/main/kotlin/com/lagradost/WiflixProvider.kt @@ -0,0 +1,309 @@ +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&story=$query&submit=Submit+Query" // 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) + val results = document.select("div#dle-content > div.clearfix") + + val Allresultshome = + results.apmap { 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): 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, + 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) + + if (episodeFrfound.text().contains("Episode")) { + mediaType = TvType.TvSeries + episodes = episodeFrfound.takeEpisode(url) + } else if (episodeVostfrfound.text().contains("Episode")) { + mediaType = TvType.TvSeries + + episodes = episodeVostfrfound.takeEpisode(url) + } 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?.toInt() + this.comingSoon =comingSoon + } + } 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?.toInt() + this.comingSoon =comingSoon + + } + } + } + + + // 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 = if (parsedInfo?.url != null) { + parsedInfo.url + } else { + data + } + val numeroEpisode = if (parsedInfo?.episodeNumber != null) { + parsedInfo.episodeNumber + } else { + null + } + + var cssCodeForPlayer = "" + + val document = app.get(url).document + val episodeFrfound = + document.select("div.blocfr") + val episodeVostfrfound = + document.select("div.blocvostfr") + + if (episodeFrfound.text().contains("Episode")) { + cssCodeForPlayer = "div.ep$numeroEpisode" + "vf > a" + } else if (episodeVostfrfound.text().contains("Episode")) { + cssCodeForPlayer = "div.ep$numeroEpisode" + "vs > a" + } else { + cssCodeForPlayer = "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 != "" || playerUrl != null) + 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 subOrdub = 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 = getQualityFromString("") + if (subOrdub.contains("HDLight")) { + quality = getQualityFromString("HD") + } else if (subOrdub.contains("Bdrip")) { + quality = getQualityFromString("BlueRay") + } else if (subOrdub.contains("DVDSCR")) { + quality = getQualityFromString("DVD") + } else if (subOrdub.contains("CAM")) { + quality = getQualityFromString("Cam") + } else { + quality = 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.Movie, + posterUrl = posterUrl, + quality = quality, + // + ) + + } + } + + override val mainPage = mainPageOf( + Pair("$mainUrl/films-prochainement/page/", "Film Prochainement en Streaming"), + Pair("$mainUrl/film-en-streaming/page/", "Film en streaming"), + Pair("$mainUrl/serie-en-streaming/page/", "Serie en streaming"), + Pair("$mainUrl/film-ancien/page/", "Film zavira") + ) + + 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.apmap { 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/WiflixProvider/src/main/kotlin/com/lagradost/WiflixProviderPlugin.kt b/WiflixProvider/src/main/kotlin/com/lagradost/WiflixProviderPlugin.kt new file mode 100644 index 0000000..d4dd31a --- /dev/null +++ b/WiflixProvider/src/main/kotlin/com/lagradost/WiflixProviderPlugin.kt @@ -0,0 +1,16 @@ + +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()) + + + } +} \ No newline at end of file