mirror of
https://github.com/recloudstream/cloudstream-extensions-multilingual.git
synced 2024-08-15 03:15:14 +00:00
clone site feature don't work with witflix, add alternative for wiflix and french stream
in mainpage see episode number nekosama
This commit is contained in:
parent
50b934a0cf
commit
9bd6623187
3 changed files with 267 additions and 146 deletions
|
@ -1,10 +1,13 @@
|
||||||
package com.lagradost
|
package com.lagradost
|
||||||
|
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||||
|
import com.lagradost.cloudstream3.utils.AppUtils
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
import com.lagradost.cloudstream3.utils.extractorApis
|
import com.lagradost.cloudstream3.utils.extractorApis
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.jsoup.nodes.Element
|
import org.jsoup.nodes.Element
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,6 +19,35 @@ class FrenchStreamProvider : MainAPI() {
|
||||||
override var lang = "fr"
|
override var lang = "fr"
|
||||||
override val supportedTypes = setOf(TvType.Movie, TvType.TvSeries)
|
override val supportedTypes = setOf(TvType.Movie, TvType.TvSeries)
|
||||||
|
|
||||||
|
init {
|
||||||
|
runBlocking {
|
||||||
|
try {
|
||||||
|
val document = app.get(mainUrl).document
|
||||||
|
val newMainUrl = document.select("link[rel*=\"canonical\"]").attr("href")
|
||||||
|
if (!newMainUrl.isNullOrBlank() && newMainUrl.contains("french-stream")) {
|
||||||
|
mainUrl = newMainUrl
|
||||||
|
} else {
|
||||||
|
val data =
|
||||||
|
AppUtils.tryParseJson<ArrayList<mediaData>>(app.get("https://raw.githubusercontent.com/Eddy976/cloudstream-extensions-eddy/ressources/fetchwebsite.json").text)!!
|
||||||
|
data.forEach {
|
||||||
|
if (it.title.lowercase().contains("french-stream")) {
|
||||||
|
mainUrl = it.url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) { // url changed
|
||||||
|
val data =
|
||||||
|
AppUtils.tryParseJson<ArrayList<mediaData>>(app.get("https://raw.githubusercontent.com/Eddy976/cloudstream-extensions-eddy/ressources/fetchwebsite.json").text)!!
|
||||||
|
data.forEach {
|
||||||
|
if (it.title.lowercase().contains("french-stream")) {
|
||||||
|
mainUrl = it.url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun search(query: String): List<SearchResponse> {
|
override suspend fun search(query: String): List<SearchResponse> {
|
||||||
val link = "$mainUrl/?do=search&subaction=search&story=$query" // search'
|
val link = "$mainUrl/?do=search&subaction=search&story=$query" // search'
|
||||||
val document =
|
val document =
|
||||||
|
@ -29,20 +61,57 @@ class FrenchStreamProvider : MainAPI() {
|
||||||
return allresultshome
|
return allresultshome
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Element.takeEpisode(
|
||||||
|
url: String,
|
||||||
|
): MutableList<Episode> {
|
||||||
|
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(
|
||||||
|
fixUrl(url).plus("-episodenumber:$epNum") + if (epTitle.contains("Vostfr")) {
|
||||||
|
"*vostfr*"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
},
|
||||||
|
epTitle,
|
||||||
|
null,
|
||||||
|
epNum,
|
||||||
|
a.selectFirst("div.fposter > img")?.attr("src"),
|
||||||
|
)
|
||||||
|
}.toMutableList()
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun load(url: String): LoadResponse {
|
override suspend fun load(url: String): LoadResponse {
|
||||||
val soup = app.get(url).document
|
val soup = app.get(url).document
|
||||||
|
var subEpisodes = mutableListOf<Episode>()
|
||||||
|
var dubEpisodes = mutableListOf<Episode>()
|
||||||
val title = soup.selectFirst("h1#s-title")!!.text().toString()
|
val title = soup.selectFirst("h1#s-title")!!.text().toString()
|
||||||
val isMovie = !title.contains("saison", ignoreCase = true)
|
|
||||||
val description =
|
val description =
|
||||||
soup.selectFirst("div.fdesc")!!.text().toString()
|
soup.selectFirst("div.fdesc")!!.text().toString()
|
||||||
.split("streaming", ignoreCase = true)[1].replace(":", "")
|
.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 listEpisode = soup.select("div.elink")
|
||||||
val tags = soup.select("ul.flist-col > li").getOrNull(1)
|
val tags = soup.select("ul.flist-col > li").getOrNull(1)
|
||||||
//val rating = soup.select("span[id^=vote-num-id]")?.getOrNull(1)?.text()?.toInt()
|
//val rating = soup.select("span[id^=vote-num-id]")?.getOrNull(1)?.text()?.toInt()
|
||||||
|
if ("<a" in (listEpisode[1]).toString()) { // check if VF is empty
|
||||||
if (isMovie) {
|
subEpisodes = listEpisode[1].takeEpisode(url) // return vostfr
|
||||||
|
}
|
||||||
|
if ("<a" in (listEpisode[0]).toString()) {
|
||||||
|
dubEpisodes = listEpisode[0].takeEpisode(url)// return vf
|
||||||
|
}
|
||||||
|
if (subEpisodes.isEmpty() && dubEpisodes.isEmpty()) {
|
||||||
val yearRegex = Regex("""ate de sortie\: (\d*)""")
|
val yearRegex = Regex("""ate de sortie\: (\d*)""")
|
||||||
val year = yearRegex.find(soup.text())?.groupValues?.get(1)
|
val year = yearRegex.find(soup.text())?.groupValues?.get(1)
|
||||||
val tagsList = tags?.select("a")
|
val tagsList = tags?.select("a")
|
||||||
|
@ -57,56 +126,21 @@ class FrenchStreamProvider : MainAPI() {
|
||||||
//this.rating = rating
|
//this.rating = rating
|
||||||
addTrailer(soup.selectFirst("button#myBtn > a")?.attr("href"))
|
addTrailer(soup.selectFirst("button#myBtn > a")?.attr("href"))
|
||||||
}
|
}
|
||||||
} else // a tv serie
|
} else {
|
||||||
{
|
|
||||||
|
|
||||||
val episodeList = if ("<a" !in (listEpisode[0]).toString()) { // check if VF is empty
|
|
||||||
listEpisode[1] // no vf, return vostfr
|
|
||||||
} else {
|
|
||||||
listEpisode[0] // no vostfr, return vf
|
|
||||||
}
|
|
||||||
|
|
||||||
val episodes = episodeList.select("a").map { a ->
|
|
||||||
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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// val tagsList = tags?.text()?.replace("Genre :","")
|
|
||||||
val yearRegex = Regex("""Titre .* \/ (\d*)""")
|
val yearRegex = Regex("""Titre .* \/ (\d*)""")
|
||||||
val year = yearRegex.find(soup.text())?.groupValues?.get(1)
|
val year = yearRegex.find(soup.text())?.groupValues?.get(1)
|
||||||
return newTvSeriesLoadResponse(
|
return newAnimeLoadResponse(
|
||||||
title,
|
title,
|
||||||
url,
|
url,
|
||||||
TvType.TvSeries,
|
TvType.TvSeries,
|
||||||
episodes,
|
|
||||||
) {
|
) {
|
||||||
this.posterUrl = poster
|
this.posterUrl = poster
|
||||||
this.plot = description
|
this.plot = description
|
||||||
this.year = year?.toInt()
|
this.year = year?.toInt()
|
||||||
//this.rating = rating
|
|
||||||
//this.showStatus = ShowStatus.Ongoing
|
|
||||||
//this.tags = tagsList
|
|
||||||
addTrailer(soup.selectFirst("button#myBtn > a")?.attr("href"))
|
addTrailer(soup.selectFirst("button#myBtn > a")?.attr("href"))
|
||||||
|
if (subEpisodes.isNotEmpty()) addEpisodes(DubStatus.Subbed, subEpisodes)
|
||||||
|
if (dubEpisodes.isNotEmpty()) addEpisodes(DubStatus.Dubbed, dubEpisodes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,6 +161,7 @@ class FrenchStreamProvider : MainAPI() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override suspend fun loadLinks(
|
override suspend fun loadLinks(
|
||||||
data: String,
|
data: String,
|
||||||
isCasting: Boolean,
|
isCasting: Boolean,
|
||||||
|
@ -136,8 +171,14 @@ class FrenchStreamProvider : MainAPI() {
|
||||||
val servers =
|
val servers =
|
||||||
if (data.contains("-episodenumber:"))// It's a serie:
|
if (data.contains("-episodenumber:"))// It's a serie:
|
||||||
{
|
{
|
||||||
|
val isvostfr = data.takeLast(8) == "*vostfr*"
|
||||||
|
|
||||||
val split =
|
val split =
|
||||||
data.split("-episodenumber:") // the data contains the url and the wanted episode number (a temporary dirty fix that will last forever)
|
if (isvostfr) {
|
||||||
|
data.dropLast(8).split("-episodenumber:")
|
||||||
|
} else {
|
||||||
|
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 url = split[0]
|
||||||
val wantedEpisode =
|
val wantedEpisode =
|
||||||
if (split[1] == "2") { // the episode number 2 has id of ABCDE, don't ask any question
|
if (split[1] == "2") { // the episode number 2 has id of ABCDE, don't ask any question
|
||||||
|
@ -185,7 +226,11 @@ class FrenchStreamProvider : MainAPI() {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
serversvf + serversvo
|
if (isvostfr) {
|
||||||
|
serversvo
|
||||||
|
} else {
|
||||||
|
serversvf
|
||||||
|
}
|
||||||
} else { // it's a movie
|
} else { // it's a movie
|
||||||
val movieServers =
|
val movieServers =
|
||||||
app.get(fixUrl(data)).document.select("nav#primary_nav_wrap > ul > li > ul > li > a")
|
app.get(fixUrl(data)).document.select("nav#primary_nav_wrap > ul > li > ul > li > a")
|
||||||
|
@ -218,7 +263,7 @@ class FrenchStreamProvider : MainAPI() {
|
||||||
allowRedirects = false
|
allowRedirects = false
|
||||||
).headers
|
).headers
|
||||||
val urlplayer = it.second
|
val urlplayer = it.second
|
||||||
var playerUrl = when (!urlplayer.isNullOrEmpty()) {
|
val playerUrl = when (!urlplayer.isNullOrEmpty()) {
|
||||||
urlplayer.contains("opsktp.com") -> header.get("location")
|
urlplayer.contains("opsktp.com") -> header.get("location")
|
||||||
.toString() // case where there is redirection to opsktp
|
.toString() // case where there is redirection to opsktp
|
||||||
|
|
||||||
|
@ -238,19 +283,20 @@ class FrenchStreamProvider : MainAPI() {
|
||||||
|
|
||||||
val posterUrl = fixUrl(select("a.short-poster > img").attr("src"))
|
val posterUrl = fixUrl(select("a.short-poster > img").attr("src"))
|
||||||
val qualityExtracted = select("span.film-ripz > a").text()
|
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 title = select("div.short-title").text()
|
||||||
val link = select("a.short-poster").attr("href").replace("wvw.", "") //wvw is an issue
|
val link = select("a.short-poster").attr("href").replace("wvw.", "") //wvw is an issue
|
||||||
var quality = when (!qualityExtracted.isNullOrBlank()) {
|
val quality = getQualityFromString(
|
||||||
qualityExtracted.contains("HDLight") -> getQualityFromString("HD")
|
when (!qualityExtracted.isNullOrBlank()) {
|
||||||
qualityExtracted.contains("Bdrip") -> getQualityFromString("BlueRay")
|
qualityExtracted.contains("HDLight") -> "HD"
|
||||||
qualityExtracted.contains("DVD") -> getQualityFromString("DVD")
|
qualityExtracted.contains("Bdrip") -> "BlueRay"
|
||||||
qualityExtracted.contains("CAM") -> getQualityFromString("Cam")
|
qualityExtracted.contains("DVD") -> "DVD"
|
||||||
|
qualityExtracted.contains("CAM") -> "Cam"
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
else -> null
|
if (!type.contains("eps")) {
|
||||||
}
|
|
||||||
|
|
||||||
if (type.contains("Eps", false)) {
|
|
||||||
return MovieSearchResponse(
|
return MovieSearchResponse(
|
||||||
name = title,
|
name = title,
|
||||||
url = link,
|
url = link,
|
||||||
|
@ -264,20 +310,28 @@ class FrenchStreamProvider : MainAPI() {
|
||||||
|
|
||||||
} else // an Serie
|
} else // an Serie
|
||||||
{
|
{
|
||||||
|
return newAnimeSearchResponse(
|
||||||
return TvSeriesSearchResponse(
|
|
||||||
name = title,
|
name = title,
|
||||||
url = link,
|
url = link,
|
||||||
apiName = title,
|
|
||||||
type = TvType.TvSeries,
|
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(
|
override val mainPage = mainPageOf(
|
||||||
Pair("$mainUrl/xfsearch/version-film/page/", "Derniers films"),
|
Pair("$mainUrl/xfsearch/version-film/page/", "Derniers films"),
|
||||||
Pair("$mainUrl/xfsearch/version-serie/page/", "Derniers séries"),
|
Pair("$mainUrl/xfsearch/version-serie/page/", "Derniers séries"),
|
||||||
|
@ -288,17 +342,9 @@ class FrenchStreamProvider : MainAPI() {
|
||||||
Pair("$mainUrl/film/documentaire/page/", "Documentaire")
|
Pair("$mainUrl/film/documentaire/page/", "Documentaire")
|
||||||
|
|
||||||
)
|
)
|
||||||
private var ismainUrlChecked = false
|
|
||||||
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
|
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
|
||||||
val url = request.data + page
|
val url = request.data + page
|
||||||
if (!ismainUrlChecked) {
|
|
||||||
ismainUrlChecked = true
|
|
||||||
val document = app.get(mainUrl).document
|
|
||||||
val newMainUrl = document.select("link[rel*=\"canonical\"]").attr("href")
|
|
||||||
if (!newMainUrl.isNullOrBlank() && newMainUrl.contains("french-stream")) {
|
|
||||||
mainUrl = newMainUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val document = app.get(url).document
|
val document = app.get(url).document
|
||||||
val movies = document.select("div#dle-content > div.short")
|
val movies = document.select("div#dle-content > div.short")
|
||||||
|
|
||||||
|
@ -308,5 +354,6 @@ class FrenchStreamProvider : MainAPI() {
|
||||||
}
|
}
|
||||||
return newHomePageResponse(request.name, home)
|
return newHomePageResponse(request.name, home)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -4,8 +4,6 @@ package com.lagradost
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.utils.*
|
import com.lagradost.cloudstream3.utils.*
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.toJson
|
|
||||||
|
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
||||||
import org.jsoup.nodes.Element
|
import org.jsoup.nodes.Element
|
||||||
|
|
||||||
|
@ -59,7 +57,7 @@ class NekosamaProvider : MainAPI() {
|
||||||
|
|
||||||
if (query == null) {
|
if (query == null) {
|
||||||
// No shorting so return the first title
|
// No shorting so return the first title
|
||||||
var title = this.title
|
val title = this.title
|
||||||
|
|
||||||
return title
|
return title
|
||||||
} else {
|
} else {
|
||||||
|
@ -333,7 +331,7 @@ class NekosamaProvider : MainAPI() {
|
||||||
val type = select("div.info > p.year").text()
|
val type = select("div.info > p.year").text()
|
||||||
val title = select("div.info > a.title > div.limit").text()
|
val title = select("div.info > a.title > div.limit").text()
|
||||||
val link = fixUrl(select("div.cover > a").attr("href"))
|
val link = fixUrl(select("div.cover > a").attr("href"))
|
||||||
if (type.contains("Film")) {
|
if (type.uppercase().contains("FILM")) {
|
||||||
return newMovieSearchResponse(
|
return newMovieSearchResponse(
|
||||||
title,
|
title,
|
||||||
link,
|
link,
|
||||||
|
@ -341,6 +339,7 @@ class NekosamaProvider : MainAPI() {
|
||||||
false,
|
false,
|
||||||
) {
|
) {
|
||||||
this.posterUrl = posterUrl
|
this.posterUrl = posterUrl
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else // an Anime
|
} else // an Anime
|
||||||
|
@ -352,6 +351,11 @@ class NekosamaProvider : MainAPI() {
|
||||||
false,
|
false,
|
||||||
) {
|
) {
|
||||||
this.posterUrl = posterUrl
|
this.posterUrl = posterUrl
|
||||||
|
addDubStatus(
|
||||||
|
isDub = link.contains("-vf"),
|
||||||
|
episodes = Regex("""(\d*) Eps""").find(type)?.groupValues?.get(1)
|
||||||
|
?.toIntOrNull()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -371,17 +375,13 @@ class NekosamaProvider : MainAPI() {
|
||||||
|
|
||||||
private fun LastEpisodeData.tomainHome(): SearchResponse {
|
private fun LastEpisodeData.tomainHome(): SearchResponse {
|
||||||
|
|
||||||
var posterUrl = this.url_image?.replace("""\""", "")
|
val posterUrl = this.url_image?.replace("""\""", "")
|
||||||
val link = this.anime_url?.replace("""\""", "")?.let { fixUrl(it) }
|
val link = this.anime_url?.replace("""\""", "")?.let { fixUrl(it) }
|
||||||
?: throw error("Error parsing")
|
?: throw error("Error parsing")
|
||||||
val title = this.title ?: throw error("Error parsing")
|
val title = this.title ?: throw error("Error parsing")
|
||||||
val type = this.episode ?: ""
|
val type = this.episode ?: ""
|
||||||
var lang = this.lang
|
val lang = this.lang
|
||||||
val dubStatus = if (lang?.contains("vf") == true) {
|
|
||||||
DubStatus.Dubbed
|
|
||||||
} else {
|
|
||||||
DubStatus.Subbed
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type.contains("Ep")) {
|
if (type.contains("Ep")) {
|
||||||
return newAnimeSearchResponse(
|
return newAnimeSearchResponse(
|
||||||
|
@ -391,8 +391,11 @@ class NekosamaProvider : MainAPI() {
|
||||||
false,
|
false,
|
||||||
) {
|
) {
|
||||||
this.posterUrl = posterUrl
|
this.posterUrl = posterUrl
|
||||||
this.dubStatus = EnumSet.of(dubStatus)
|
addDubStatus(
|
||||||
|
isDub = lang?.contains("vf")==true,
|
||||||
|
episodes = Regex("""Ep[\.][\s]+(\d*)""").find(type)?.groupValues?.get(1)
|
||||||
|
?.toIntOrNull()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else // a movie
|
} else // a movie
|
||||||
|
@ -404,8 +407,11 @@ class NekosamaProvider : MainAPI() {
|
||||||
false,
|
false,
|
||||||
) {
|
) {
|
||||||
this.posterUrl = posterUrl
|
this.posterUrl = posterUrl
|
||||||
this.dubStatus = EnumSet.of(dubStatus)
|
addDubStatus(
|
||||||
}
|
isDub = lang?.contains("vf")==true,
|
||||||
|
episodes = Regex("""(\d*) Eps""").find(type)?.groupValues?.get(1)
|
||||||
|
?.toIntOrNull()
|
||||||
|
) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,18 +8,39 @@ import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
||||||
import org.jsoup.nodes.Element
|
import org.jsoup.nodes.Element
|
||||||
import org.jsoup.select.Elements
|
import org.jsoup.select.Elements
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
|
import com.lagradost.cloudstream3.network.CloudflareKiller
|
||||||
|
import com.lagradost.nicehttp.NiceResponse
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
|
|
||||||
class WiflixProvider : MainAPI() {
|
class WiflixProvider : MainAPI() {
|
||||||
|
|
||||||
|
|
||||||
override var mainUrl = "https://wiflix.zone"
|
override var mainUrl = "https://wiflix.cafe"
|
||||||
override var name = "Wiflix"
|
override var name = "Wiflix"
|
||||||
override val hasQuickSearch = false // recherche rapide (optionel, pas vraimet utile)
|
override val hasQuickSearch = false // recherche rapide (optionel, pas vraimet utile)
|
||||||
override val hasMainPage = true // page d'accueil (optionel mais encoragé)
|
override val hasMainPage = true // page d'accueil (optionel mais encoragé)
|
||||||
override var lang = "fr" // fournisseur est en francais
|
override var lang = "fr" // fournisseur est en francais
|
||||||
override val supportedTypes =
|
override val supportedTypes =
|
||||||
setOf(TvType.Movie, TvType.TvSeries) // series, films
|
setOf(TvType.Movie, TvType.TvSeries) // series, films
|
||||||
// liste des types: https://recloudstream.github.io/dokka/app/com.lagradost.cloudstream3/-tv-type/index.html
|
private val interceptor = CloudflareKiller()
|
||||||
|
|
||||||
|
init {
|
||||||
|
runBlocking {
|
||||||
|
try {
|
||||||
|
app.get(mainUrl)
|
||||||
|
} catch (e: Exception) { // url changed
|
||||||
|
val data =
|
||||||
|
tryParseJson<ArrayList<mediaData>>(app.get("https://raw.githubusercontent.com/Eddy976/cloudstream-extensions-eddy/ressources/fetchwebsite.json").text)!!
|
||||||
|
data.forEach {
|
||||||
|
if (it.title.lowercase().contains("wiflix")) {
|
||||||
|
mainUrl = it.url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Cherche le site pour un titre spécifique
|
Cherche le site pour un titre spécifique
|
||||||
|
@ -29,7 +50,7 @@ class WiflixProvider : MainAPI() {
|
||||||
**/
|
**/
|
||||||
override suspend fun search(query: String): List<SearchResponse> {
|
override suspend fun search(query: String): List<SearchResponse> {
|
||||||
val link =
|
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'
|
"$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 =
|
val document =
|
||||||
app.post(link).document // app.get() permet de télécharger la page html avec une requete HTTP (get)
|
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 results = document.select("div#dle-content > div.clearfix")
|
||||||
|
@ -50,13 +71,17 @@ class WiflixProvider : MainAPI() {
|
||||||
@JsonProperty("episodeNumber") val episodeNumber: String,
|
@JsonProperty("episodeNumber") val episodeNumber: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun Elements.takeEpisode(url: String, duborSub: String?): ArrayList<Episode> {
|
private fun Elements.takeEpisode(
|
||||||
|
url: String,
|
||||||
|
posterUrl: String?,
|
||||||
|
duborSub: String?
|
||||||
|
): ArrayList<Episode> {
|
||||||
|
|
||||||
val episodes = ArrayList<Episode>()
|
val episodes = ArrayList<Episode>()
|
||||||
this.select("ul.eplist > li").forEach {
|
this.select("ul.eplist > li").forEach {
|
||||||
|
|
||||||
val strEpisode = it.text()
|
val strEpisodeN =
|
||||||
val strEpisodeN = strEpisode.replace("Episode ", "")
|
Regex("""pisode[\s]+(\d+)""").find(it.text())?.groupValues?.get(1).toString()
|
||||||
val link =
|
val link =
|
||||||
EpisodeData(
|
EpisodeData(
|
||||||
url,
|
url,
|
||||||
|
@ -66,9 +91,14 @@ class WiflixProvider : MainAPI() {
|
||||||
|
|
||||||
episodes.add(
|
episodes.add(
|
||||||
Episode(
|
Episode(
|
||||||
link,
|
link + if (duborSub=="vostfr") {
|
||||||
name = duborSub,
|
"*$duborSub*"
|
||||||
episode = strEpisodeN.toInt(),
|
} else {
|
||||||
|
""
|
||||||
|
},
|
||||||
|
name = "Episode en " + duborSub,
|
||||||
|
episode = strEpisodeN.toIntOrNull(),
|
||||||
|
posterUrl = posterUrl
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -77,11 +107,11 @@ class WiflixProvider : MainAPI() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun load(url: String): LoadResponse {
|
override suspend fun load(url: String): LoadResponse {
|
||||||
val document = app.get(url).document //
|
val document = avoidCloudflare(url).document //
|
||||||
// url est le lien retourné par la fonction search (la variable href) ou la fonction getMainPage
|
// url est le lien retourné par la fonction search (la variable href) ou la fonction getMainPage
|
||||||
|
var subEpisodes = ArrayList<Episode>()
|
||||||
var episodes = ArrayList<Episode>()
|
var dubEpisodes = ArrayList<Episode>()
|
||||||
var mediaType: TvType
|
val mediaType: TvType
|
||||||
val episodeFrfound =
|
val episodeFrfound =
|
||||||
document.select("div.blocfr")
|
document.select("div.blocfr")
|
||||||
|
|
||||||
|
@ -97,27 +127,23 @@ class WiflixProvider : MainAPI() {
|
||||||
|
|
||||||
val tags = document.select("[itemprop=genre] > a")
|
val tags = document.select("[itemprop=genre] > a")
|
||||||
.map { it.text() } // séléctione tous les tags et les ajoutes à une liste
|
.map { it.text() } // séléctione tous les tags et les ajoutes à une liste
|
||||||
|
mediaType = TvType.TvSeries
|
||||||
if (episodeFrfound.text().contains("Episode")) {
|
if (episodeFrfound.text().lowercase().contains("episode")) {
|
||||||
mediaType = TvType.TvSeries
|
val duborSub = "\uD83C\uDDE8\uD83C\uDDF5"
|
||||||
val duborSub = "Episode en VF"
|
dubEpisodes = episodeFrfound.takeEpisode(url, fixUrl(posterUrl), duborSub)
|
||||||
episodes = episodeFrfound.takeEpisode(url, duborSub)
|
}
|
||||||
} else if (episodeVostfrfound.text().contains("Episode")) {
|
if (episodeVostfrfound.text().lowercase().contains("episode")) {
|
||||||
mediaType = TvType.TvSeries
|
val duborSub = "vostfr"
|
||||||
val duborSub = "Episode sous-titré"
|
subEpisodes = episodeVostfrfound.takeEpisode(url, fixUrl(posterUrl), duborSub)
|
||||||
episodes = episodeVostfrfound.takeEpisode(url, duborSub)
|
|
||||||
} else {
|
|
||||||
|
|
||||||
mediaType = TvType.Movie
|
|
||||||
}
|
}
|
||||||
///////////////////////////////////////////
|
///////////////////////////////////////////
|
||||||
///////////////////////////////////////////
|
///////////////////////////////////////////
|
||||||
var type_rec: TvType
|
var type_rec: TvType
|
||||||
val recommendations =
|
val recommendations =
|
||||||
document.select("div.clearfixme > div > div")?.mapNotNull { element ->
|
document.select("div.clearfixme > div > div").mapNotNull { element ->
|
||||||
val recTitle =
|
val recTitle =
|
||||||
element.select("a").text() ?: return@mapNotNull null
|
element.select("a").text() ?: return@mapNotNull null
|
||||||
val image = element.select("a >img")?.attr("src")
|
val image = element.select("a >img").attr("src")
|
||||||
val recUrl = element.select("a").attr("href")
|
val recUrl = element.select("a").attr("href")
|
||||||
type_rec = TvType.TvSeries
|
type_rec = TvType.TvSeries
|
||||||
if (recUrl.contains("film")) type_rec = TvType.Movie
|
if (recUrl.contains("film")) type_rec = TvType.Movie
|
||||||
|
@ -138,15 +164,14 @@ class WiflixProvider : MainAPI() {
|
||||||
this.name,
|
this.name,
|
||||||
TvType.Movie,
|
TvType.Movie,
|
||||||
image?.let { fixUrl(it) },
|
image?.let { fixUrl(it) },
|
||||||
|
)
|
||||||
)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var comingSoon = url.contains("films-prochainement")
|
val comingSoon = url.contains("films-prochainement")
|
||||||
|
|
||||||
|
|
||||||
if (mediaType == TvType.Movie) {
|
if (subEpisodes.isEmpty() && dubEpisodes.isEmpty()) {
|
||||||
val description = document.selectFirst("div.screenshots-full")?.text()
|
val description = document.selectFirst("div.screenshots-full")?.text()
|
||||||
?.replace("(.* .ynopsis)".toRegex(), "")
|
?.replace("(.* .ynopsis)".toRegex(), "")
|
||||||
return newMovieLoadResponse(
|
return newMovieLoadResponse(
|
||||||
|
@ -165,11 +190,10 @@ class WiflixProvider : MainAPI() {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val description = document.selectFirst("span[itemprop=description]")?.text()
|
val description = document.selectFirst("span[itemprop=description]")?.text()
|
||||||
return newTvSeriesLoadResponse(
|
return newAnimeLoadResponse(
|
||||||
title,
|
title,
|
||||||
url,
|
url,
|
||||||
mediaType,
|
mediaType,
|
||||||
episodes
|
|
||||||
) {
|
) {
|
||||||
this.posterUrl = fixUrl(posterUrl)
|
this.posterUrl = fixUrl(posterUrl)
|
||||||
this.plot = description
|
this.plot = description
|
||||||
|
@ -177,6 +201,8 @@ class WiflixProvider : MainAPI() {
|
||||||
this.year = year?.toIntOrNull()
|
this.year = year?.toIntOrNull()
|
||||||
this.comingSoon = comingSoon
|
this.comingSoon = comingSoon
|
||||||
this.tags = tags
|
this.tags = tags
|
||||||
|
if (subEpisodes.isNotEmpty()) addEpisodes(DubStatus.Subbed, subEpisodes)
|
||||||
|
if (dubEpisodes.isNotEmpty()) addEpisodes(DubStatus.Dubbed, dubEpisodes)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,30 +216,43 @@ class WiflixProvider : MainAPI() {
|
||||||
subtitleCallback: (SubtitleFile) -> Unit,
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
callback: (ExtractorLink) -> Unit,
|
callback: (ExtractorLink) -> Unit,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val parsedInfo =
|
var isvostfr = false
|
||||||
|
val parsedInfo = if (data.takeLast(8) == "*vostfr*") {
|
||||||
|
isvostfr = true
|
||||||
|
tryParseJson<EpisodeData>(data.dropLast(6))
|
||||||
|
} else {
|
||||||
tryParseJson<EpisodeData>(data)
|
tryParseJson<EpisodeData>(data)
|
||||||
|
}
|
||||||
|
|
||||||
val url = parsedInfo?.url ?: data
|
val url = parsedInfo?.url ?: data
|
||||||
|
|
||||||
val numeroEpisode = parsedInfo?.episodeNumber ?: null
|
val numeroEpisode = parsedInfo?.episodeNumber
|
||||||
|
|
||||||
val document = app.get(url).document
|
val document = avoidCloudflare(url).document
|
||||||
val episodeFrfound =
|
val episodeFrfound =
|
||||||
document.select("div.blocfr")
|
document.select("div.blocfr")
|
||||||
val episodeVostfrfound =
|
val episodeVostfrfound =
|
||||||
document.select("div.blocvostfr")
|
document.select("div.blocvostfr")
|
||||||
|
var flag = "\uD83C\uDDE8\uD83C\uDDF5"
|
||||||
|
|
||||||
val cssCodeForPlayer = if (episodeFrfound.text().contains("Episode")) {
|
val cssCodeForPlayer = if (episodeFrfound.text().contains("Episode") && !isvostfr) {
|
||||||
"div.ep${numeroEpisode}vf > a"
|
"div.ep${numeroEpisode}vf > a"
|
||||||
|
|
||||||
} else if (episodeVostfrfound.text().contains("Episode")) {
|
} else if (episodeVostfrfound.text().contains("Episode")) {
|
||||||
"div.ep${numeroEpisode}vs > a"
|
"div.ep${numeroEpisode}vs > a"
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
"div.linkstab > a"
|
"div.linkstab > a"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cssCodeForPlayer.contains("vs")) {
|
||||||
|
flag = " \uD83D\uDCDC \uD83C\uDDEC\uD83C\uDDE7"
|
||||||
|
}
|
||||||
|
|
||||||
document.select("$cssCodeForPlayer").apmap { player -> // séléctione tous les players
|
|
||||||
var playerUrl = "https"+player.attr("href").replace("(.*)https".toRegex(), "")
|
document.select(cssCodeForPlayer).apmap { player -> // séléctione tous les players
|
||||||
if (!playerUrl.isNullOrBlank())
|
var playerUrl = "https" + player.attr("href").replace("(.*)https".toRegex(), "")
|
||||||
|
if (!playerUrl.isBlank())
|
||||||
if (playerUrl.contains("dood")) {
|
if (playerUrl.contains("dood")) {
|
||||||
playerUrl = playerUrl.replace("doodstream.com", "dood.wf")
|
playerUrl = playerUrl.replace("doodstream.com", "dood.wf")
|
||||||
}
|
}
|
||||||
|
@ -225,7 +264,7 @@ class WiflixProvider : MainAPI() {
|
||||||
callback.invoke(
|
callback.invoke(
|
||||||
ExtractorLink( // ici je modifie le callback pour ajouter des informations, normalement ce n'est pas nécessaire
|
ExtractorLink( // ici je modifie le callback pour ajouter des informations, normalement ce n'est pas nécessaire
|
||||||
link.source,
|
link.source,
|
||||||
link.name + "",
|
link.name + flag,
|
||||||
link.url,
|
link.url,
|
||||||
link.referer,
|
link.referer,
|
||||||
getQualityFromName("HD"),
|
getQualityFromName("HD"),
|
||||||
|
@ -245,18 +284,20 @@ class WiflixProvider : MainAPI() {
|
||||||
|
|
||||||
val posterUrl = fixUrl(select("div.img-box > img").attr("src"))
|
val posterUrl = fixUrl(select("div.img-box > img").attr("src"))
|
||||||
val qualityExtracted = select("div.nbloc1-2 >span").text()
|
val qualityExtracted = select("div.nbloc1-2 >span").text()
|
||||||
val type = select("div.nbloc3").text()
|
val type = select("div.nbloc3").text().lowercase()
|
||||||
val title = select("a.nowrap").text()
|
val title = select("a.nowrap").text()
|
||||||
val link = select("a.nowrap").attr("href")
|
val link = select("a.nowrap").attr("href")
|
||||||
var quality = when (!qualityExtracted.isNullOrBlank()) {
|
val quality = getQualityFromString(
|
||||||
qualityExtracted.contains("HDLight") -> getQualityFromString("HD")
|
when (!qualityExtracted.isNullOrBlank()) {
|
||||||
qualityExtracted.contains("Bdrip") -> getQualityFromString("BlueRay")
|
qualityExtracted.contains("HDLight") -> "HD"
|
||||||
qualityExtracted.contains("DVD") -> getQualityFromString("DVD")
|
qualityExtracted.contains("Bdrip") -> "BlueRay"
|
||||||
qualityExtracted.contains("CAM") -> getQualityFromString("Cam")
|
qualityExtracted.contains("DVD") -> "DVD"
|
||||||
|
qualityExtracted.contains("CAM") -> "Cam"
|
||||||
|
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
if (type.contains("Film")) {
|
)
|
||||||
|
if (type.contains("film")) {
|
||||||
return MovieSearchResponse(
|
return MovieSearchResponse(
|
||||||
name = title,
|
name = title,
|
||||||
url = link,
|
url = link,
|
||||||
|
@ -271,19 +312,40 @@ class WiflixProvider : MainAPI() {
|
||||||
} else // an Serie
|
} else // an Serie
|
||||||
{
|
{
|
||||||
|
|
||||||
return TvSeriesSearchResponse(
|
return newAnimeSearchResponse(
|
||||||
name = title,
|
name = title,
|
||||||
url = link,
|
url = link,
|
||||||
apiName = title,
|
|
||||||
type = TvType.TvSeries,
|
type = TvType.TvSeries,
|
||||||
posterUrl = posterUrl,
|
|
||||||
quality = quality,
|
) {
|
||||||
//
|
this.posterUrl = posterUrl
|
||||||
)
|
this.quality = quality
|
||||||
|
addDubStatus(
|
||||||
|
isDub = !select("span.block-sai").text().uppercase().contains("VOSTFR"),
|
||||||
|
episodes = Regex("""pisode[\s]+(\d+)""").find(select("div.block-ep").text())?.groupValues?.get(
|
||||||
|
1
|
||||||
|
)?.toIntOrNull()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
suspend fun avoidCloudflare(url: String): NiceResponse {
|
||||||
|
if (!app.get(url).isSuccessful) {
|
||||||
|
return app.get(url, interceptor = interceptor)
|
||||||
|
} else {
|
||||||
|
return app.get(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class mediaData(
|
||||||
|
@JsonProperty("title") var title: String,
|
||||||
|
@JsonProperty("url") val url: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
override val mainPage = mainPageOf(
|
override val mainPage = mainPageOf(
|
||||||
Pair("$mainUrl/films-prochainement/page/", "Film Prochainement en Streaming"),
|
Pair("$mainUrl/films-prochainement/page/", "Film Prochainement en Streaming"),
|
||||||
Pair("$mainUrl/film-en-streaming/page/", "Top Films cette année"),
|
Pair("$mainUrl/film-en-streaming/page/", "Top Films cette année"),
|
||||||
|
@ -294,7 +356,11 @@ class WiflixProvider : MainAPI() {
|
||||||
|
|
||||||
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
|
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
|
||||||
val url = request.data + page
|
val url = request.data + page
|
||||||
val document = app.get(url).document
|
val document =
|
||||||
|
avoidCloudflare(url).document
|
||||||
|
|
||||||
|
//posterHeaders = interceptor.getCookieHeaders(url).toMap()
|
||||||
|
|
||||||
val movies = document.select("div#dle-content > div.clearfix")
|
val movies = document.select("div#dle-content > div.clearfix")
|
||||||
|
|
||||||
val home =
|
val home =
|
||||||
|
@ -305,3 +371,5 @@ class WiflixProvider : MainAPI() {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue