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) } - } +