improve the plugin, changes requested and add comments

This commit is contained in:
Eddy 2022-11-15 21:09:07 +01:00
parent 024bdd6342
commit fbe6ad3fa2
3 changed files with 250 additions and 217 deletions

View file

@ -1,53 +1,20 @@
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
class FrenchStreamProvider : MainAPI() { class FrenchStreamProvider : MainAPI() {
override var mainUrl = "https://french-stream.ac" //re ou ac ou city override var mainUrl = "https://french-stream.cx" //re ou ac ou city
override var name = "FrenchStream" override var name = "FrenchStream"
override val hasQuickSearch = false override val hasQuickSearch = false
override val hasMainPage = true override val hasMainPage = true
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 =
@ -61,48 +28,15 @@ 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 = !url.contains("/serie/", ignoreCase = true) 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(":", "")
val poster = soup.selectFirst("div.fposter > img")?.attr("src") var 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()
@ -122,26 +56,56 @@ 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 { } else // a tv serie
if ("<a" in listEpisode[1].toString()) { // check if VF is empty {
subEpisodes = listEpisode[1].takeEpisode(url) // return vostfr
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
} }
if ("<a" in listEpisode[0].toString()) {
dubEpisodes = listEpisode[0].takeEpisode(url)// 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 newAnimeLoadResponse( return newTvSeriesLoadResponse(
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)
} }
} }
} }
@ -162,7 +126,6 @@ class FrenchStreamProvider : MainAPI() {
} }
} }
override suspend fun loadLinks( override suspend fun loadLinks(
data: String, data: String,
isCasting: Boolean, isCasting: Boolean,
@ -172,14 +135,8 @@ 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 =
if (isvostfr) { data.split("-episodenumber:") // the data contains the url and the wanted episode number (a temporary dirty fix that will last forever)
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
@ -227,11 +184,7 @@ class FrenchStreamProvider : MainAPI() {
null null
} }
} }
if (isvostfr) { serversvf + serversvo
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")
@ -264,7 +217,7 @@ class FrenchStreamProvider : MainAPI() {
allowRedirects = false allowRedirects = false
).headers ).headers
val urlplayer = it.second val urlplayer = it.second
val playerUrl = when (!urlplayer.isNullOrEmpty()) { var 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
@ -284,20 +237,19 @@ 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().lowercase() val type = select("span.mli-eps").text()
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
val quality = getQualityFromString( var quality = when (!qualityExtracted.isNullOrBlank()) {
when (!qualityExtracted.isNullOrBlank()) { qualityExtracted.contains("HDLight") -> getQualityFromString("HD")
qualityExtracted.contains("HDLight") -> "HD" qualityExtracted.contains("Bdrip") -> getQualityFromString("BlueRay")
qualityExtracted.contains("Bdrip") -> "BlueRay" qualityExtracted.contains("DVD") -> getQualityFromString("DVD")
qualityExtracted.contains("DVD") -> "DVD" qualityExtracted.contains("CAM") -> getQualityFromString("Cam")
qualityExtracted.contains("CAM") -> "Cam"
else -> null
}
)
if (!type.contains("eps")) { else -> null
}
if (type.contains("Eps", false)) {
return MovieSearchResponse( return MovieSearchResponse(
name = title, name = title,
url = link, url = link,
@ -311,28 +263,20 @@ 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"),
@ -355,6 +299,5 @@ class FrenchStreamProvider : MainAPI() {
} }
return newHomePageResponse(request.name, home) return newHomePageResponse(request.name, home)
} }
} }

View file

@ -12,10 +12,8 @@ import kotlinx.coroutines.runBlocking
import me.xdrop.fuzzywuzzy.FuzzySearch import me.xdrop.fuzzywuzzy.FuzzySearch
class MacIPTVProvider(override var lang: String) : MainAPI() { class MacIPTVProvider(override var lang: String) : MainAPI() {
private val defaulmac_adresse = private var defaulmac_adresse = "mac=00:1A:79:aa:53:65"
"mac=00:1A:79:aa:53:65" private val defaultmainUrl = "http://ky-iptv.com:25461/portalstb"
private val defaultmainUrl =
"http://ky-iptv.com:25461/portalstb"
var defaultname = "ky-iptv |${lang.uppercase()}|" var defaultname = "ky-iptv |${lang.uppercase()}|"
var Basename = "Box Iptv" var Basename = "Box Iptv"
override val hasQuickSearch = false override val hasQuickSearch = false
@ -23,12 +21,20 @@ class MacIPTVProvider(override var lang: String) : MainAPI() {
override val supportedTypes = override val supportedTypes =
setOf(TvType.Live) // live setOf(TvType.Live) // live
private var key: String? = "" private var key: String? = "" // key used in the header
private val arraymediaPlaylist =
ArrayList<Channel>() // store all channels for the search function
var allCategory =
arrayListOf<String>() // even if we don't display all countries or categories, we need to know those avalaible
var isNotInit = true
init { init {
name = Basename + " |${lang.uppercase()}|" name = Basename + " |${lang.uppercase()}|"
} }
/**
check if the data are incorrect
**/
private fun accountInfoNotGood(url: String, mac: String?): Boolean { private fun accountInfoNotGood(url: String, mac: String?): Boolean {
return url.uppercase().trim() == "NONE" || url.isBlank() || mac?.uppercase() return url.uppercase().trim() == "NONE" || url.isBlank() || mac?.uppercase()
?.trim() == "NONE" || mac.isNullOrBlank() ?.trim() == "NONE" || mac.isNullOrBlank()
@ -44,18 +50,23 @@ class MacIPTVProvider(override var lang: String) : MainAPI() {
mac mac
} }
val url_key = val url_key =
"$mainUrl/portal.php?type=stb&action=handshake&JsHttpRequest=1-xml" "$mainUrl/portal.php?type=stb&action=handshake&token=&JsHttpRequest=1-xml"
val reponseGetkey = app.get( val reponseGetkey = app.get(
url_key, headers = mapOf( url_key, headers = mapOf(
"Cookie" to adresseMac, "Cookie" to adresseMac,
"User-Agent" to "Mozilla/5.0 (QtEmbedded; U; Linux; C) AppleWebKit/533.3 (KHTML, like Gecko) MAG200 stbapp ver: 2 rev: 250 Safari/533.3", "User-Agent" to "Mozilla/5.0 (QtEmbedded; U; Linux; C) AppleWebKit/533.3 (KHTML, like Gecko) MAG200 stbapp ver: 2 rev: 250 Safari/533.3",
) "X-User-Agent" to "Model: MAG250; Link: WiFi",
)
) )
val keyJson = reponseGetkey.parsed<Getkey>() key = tryParseJson<Getkey>(
key = keyJson.js?.token Regex("""\{\"js\"(.*[\r\n]*)+\}""").find(reponseGetkey.text)?.groupValues?.get(0)
)?.js?.token ?: ""
} }
/**
From user account, get the tags (to select categories and countries), url , mac address ... if there are avalaible
**/
private suspend fun getAuthHeader() { private suspend fun getAuthHeader() {
tags = tags ?: "" // tags will allow to select more contains tags = tags ?: "" // tags will allow to select more contains
if (tags?.uppercase()?.trim() == "NONE" || tags?.isBlank() == true) tags = lang if (tags?.uppercase()?.trim() == "NONE" || tags?.isBlank() == true) tags = lang
@ -66,22 +77,48 @@ class MacIPTVProvider(override var lang: String) : MainAPI() {
mainUrl = defaultmainUrl mainUrl = defaultmainUrl
name = defaultname name = defaultname
getkey(defaulmac_adresse) getkey(defaulmac_adresse)
mutableMapOf( if (key?.isNotBlank() == true) {
"Cookie" to defaulmac_adresse, mutableMapOf(
"User-Agent" to "Mozilla/5.0 (QtEmbedded; U; Linux; C) AppleWebKit/533.3 (KHTML, like Gecko) MAG200 stbapp ver: 2 rev: 250 Safari/533.3", "Cookie" to defaulmac_adresse,
"Authorization" to "Bearer $key", "User-Agent" to "Mozilla/5.0 (QtEmbedded; U; Linux; C) AppleWebKit/533.3 (KHTML, like Gecko) MAG200 stbapp ver: 2 rev: 250 Safari/533.3",
) "Authorization" to "Bearer $key",
)
} else {
mutableMapOf(
"Cookie" to defaulmac_adresse,
"User-Agent" to "Mozilla/5.0 (QtEmbedded; U; Linux; C) AppleWebKit/533.3 (KHTML, like Gecko) MAG200 stbapp ver: 2 rev: 250 Safari/533.3",
)
}
} }
else -> { else -> {
name = (companionName ?: Basename) + " |${lang.uppercase()}|" name = (companionName ?: Basename) + " |${lang.uppercase()}|"
getkey(loginMac.toString()) getkey(loginMac.toString())
mutableMapOf( if (key?.isNotBlank() == true) {
"Cookie" to "mac=$loginMac", defaulmac_adresse = "mac=$loginMac"
"User-Agent" to "Mozilla/5.0 (QtEmbedded; U; Linux; C) AppleWebKit/533.3 (KHTML, like Gecko) MAG200 stbapp ver: 2 rev: 250 Safari/533.3", mutableMapOf(
"Authorization" to "Bearer $key", "Cookie" to "mac=$loginMac",
) "User-Agent" to "Mozilla/5.0 (QtEmbedded; U; Linux; C) AppleWebKit/533.3 (KHTML, like Gecko) MAG200 stbapp ver: 2 rev: 250 Safari/533.3",
"Authorization" to "Bearer $key",
)
} else {
defaulmac_adresse = "mac=$loginMac"
mutableMapOf(
"Cookie" to "mac=$loginMac",
"User-Agent" to "Mozilla/5.0 (QtEmbedded; U; Linux; C) AppleWebKit/533.3 (KHTML, like Gecko) MAG200 stbapp ver: 2 rev: 250 Safari/533.3",
)
}
} }
} }
mainUrl = when (true) { // the "c" is not needed and some provider doesn't work with it
mainUrl.endsWith("/c/") -> mainUrl.dropLast(3)
mainUrl.endsWith("/c") -> mainUrl.dropLast(2)
mainUrl.endsWith("/") -> mainUrl.dropLast(1)
else -> mainUrl
}
app.get(
"$mainUrl/portal.php?type=stb&action=get_modules",
headers = headerMac
) // some providers need this request to work
isNotInit = false isNotInit = false
} }
@ -104,24 +141,24 @@ class MacIPTVProvider(override var lang: String) : MainAPI() {
} else { } else {
this.sortedBy { this.sortedBy {
val name = cleanTitle(it.title).replace(rgxcodeCountry, "").trim() val name = cleanTitle(it.title).replace(findKeyWord(lang), "").trim()
-FuzzySearch.ratio(name.lowercase(), query.lowercase()) -FuzzySearch.ratio(name.lowercase(), query.lowercase())
} }
} }
} }
private val resultsSearchNbr = 50 // show only the 50 results
override suspend fun search(query: String): List<SearchResponse> { override suspend fun search(query: String): List<SearchResponse> {
return arraymediaPlaylist.sortByname(query).take(resultsSearchNbr).map { media -> return arraymediaPlaylist.sortByname(query).take(50)
LiveSearchResponse( .map { media -> // display only the 50 results
media.title, LiveSearchResponse(
media.toJson(), media.title,
name, media.toJson(),
TvType.Live, name,
media.url_image, TvType.Live,
) media.url_image,
} )
}
} }
data class Root_epg( data class Root_epg(
@ -154,25 +191,41 @@ class MacIPTVProvider(override var lang: String) : MainAPI() {
override suspend fun load(url: String): LoadResponse { override suspend fun load(url: String): LoadResponse {
if (url.contains("I_Need_Help")) { when {
return LiveStreamLoadResponse( url.contains("I_Need_Help") -> { // go to see all the avalaible tags
name = "GO TO CREATE YOUR \uD83C\uDDF9\u200B\u200B\u200B\u200B\u200B\uD83C\uDDE6\u200B\u200B\u200B\u200B\u200B\uD83C\uDDEC\u200B\u200B\u200B\u200B\u200B ACCOUNT => italia|sport|crime|uk ", return LiveStreamLoadResponse(
url = url, name = "GO TO CREATE YOUR \uD83C\uDDF9\u200B\u200B\u200B\u200B\u200B\uD83C\uDDE6\u200B\u200B\u200B\u200B\u200B\uD83C\uDDEC\u200B\u200B\u200B\u200B\u200B ACCOUNT eg. italia|sport|crime|uk ",
apiName = this.name, url = url,
dataUrl = url, apiName = this.name,
posterUrl = "https://www.toutestpossible.be/wp-content/uploads/2017/05/comment-faire-des-choix-eclaires-en-10-etapes-01-300x167.jpg", dataUrl = url,
plot = "ALL TAGS \uD83D\uDD0E ${allCategory.sortedBy { it }}", posterUrl = "https://www.toutestpossible.be/wp-content/uploads/2017/05/comment-faire-des-choix-eclaires-en-10-etapes-01-300x167.jpg",
comingSoon = true plot = "ALL TAGS \uD83D\uDD0E ${allCategory.sortedBy { it }}",
) comingSoon = true
)
}
url.contains("There_is_an_error") -> { // case where the provider don't work
return LiveStreamLoadResponse(
name = "\uD83C\uDDF5\u200B\u200B\u200B\u200B\u200B\uD83C\uDDF7\u200B\u200B\u200B\u200B\u200B\uD83C\uDDF4\u200B\u200B\u200B\u200B\u200B\uD83C\uDDE7\u200B\u200B\u200B\u200B\u200B\uD83C\uDDF1\u200B\u200B\u200B\u200B\u200B\uD83C\uDDEA\u200B\u200B\u200B\u200B\u200B\uD83C\uDDF2\u200B\u200B\u200B\u200B\u200B",
url = url,
apiName = this.name,
dataUrl = url,
posterUrl = "https://www.toutestpossible.be/wp-content/uploads/2017/05/comment-faire-des-choix-eclaires-en-10-etapes-01-300x167.jpg",
plot = "There is a issue with this account. Please check your credentials or try another account",
comingSoon = true
)
}
else -> ""
} }
val media = parseJson<Channel>(url.replace(mainUrl, "")) val media = parseJson<Channel>(url.replace(mainUrl, ""))
val epg_url = val epg_url =
"$mainUrl/portal.php?type=itv&action=get_short_epg&ch_id=${media.ch_id}&size=10&JsHttpRequest=1-xml" // plot "$mainUrl/portal.php?type=itv&action=get_short_epg&ch_id=${media.ch_id}&size=10&JsHttpRequest=1-xml" // the plot when it's avalaible
val response = app.get(epg_url, headers = headerMac) val response = app.get(epg_url, headers = headerMac)
val description = getEpg(response.text) val description = getEpg(response.text)
val link = media.url val link = media.url
val title = media.title val title = media.title
val a = cleanTitle(title).replace(rgxcodeCountry, "").trim() val a = cleanTitle(title).replace(findKeyWord(lang), "").trim()
val posterUrl = media.url_image.toString() val posterUrl = media.url_image.toString()
var b_new: String var b_new: String
@ -184,7 +237,7 @@ class MacIPTVProvider(override var lang: String) : MainAPI() {
posterUrl = posterUrl, posterUrl = posterUrl,
plot = description, plot = description,
recommendations = arraymediaPlaylist.mapNotNull { channel -> recommendations = arraymediaPlaylist.mapNotNull { channel ->
val b = cleanTitle(channel.title).replace(rgxcodeCountry, "").trim() val b = cleanTitle(channel.title).replace(findKeyWord(lang), "").trim()
b_new = b.take(6) b_new = b.take(6)
if (channel.id != media.id && a.take(6) if (channel.id != media.id && a.take(6)
.contains(b_new) && media.tv_genre_id == channel.tv_genre_id .contains(b_new) && media.tv_genre_id == channel.tv_genre_id
@ -218,7 +271,7 @@ class MacIPTVProvider(override var lang: String) : MainAPI() {
) )
LiveSearchResponse( LiveSearchResponse(
name = cleanTitleButKeepNumber(channelname).replace( name = cleanTitleButKeepNumber(channelname).replace(
rgxcodeCountry, findKeyWord(lang),
"" ""
).trim(), ).trim(),
url = streamurl, url = streamurl,
@ -235,8 +288,6 @@ class MacIPTVProvider(override var lang: String) : MainAPI() {
} }
/** get the channel id */
val regexCode_ch = Regex("""\/(\d*)\?""")
/** /**
* Use new token. * Use new token.
@ -249,7 +300,8 @@ class MacIPTVProvider(override var lang: String) : MainAPI() {
if (request.url.toString().contains("token") if (request.url.toString().contains("token")
) { ) {
val chID = regexCode_ch.find(request.url.toString())!!.groupValues[1] + "_" val chID =
Regex("""\/(\d*)\?""").find(request.url.toString())!!.groupValues[1] + "_"
val TokenLink = val TokenLink =
"$mainUrl/portal.php?type=itv&action=create_link&cmd=ffmpeg%20http://localhost/ch/$chID&series=&forced_storage=0&disable_ad=0&download=0&force_ch_link_check=0&JsHttpRequest=1-xml" "$mainUrl/portal.php?type=itv&action=create_link&cmd=ffmpeg%20http://localhost/ch/$chID&series=&forced_storage=0&disable_ad=0&download=0&force_ch_link_check=0&JsHttpRequest=1-xml"
@ -287,9 +339,8 @@ class MacIPTVProvider(override var lang: String) : MainAPI() {
} }
} }
/** récupere les liens .mp4 ou m3u8 directement à partir du paramètre data généré avec la fonction load()**/
override suspend fun loadLinks( override suspend fun loadLinks(
data: String, // fournit par load() data: String,
isCasting: Boolean, isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit, subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit, callback: (ExtractorLink) -> Unit,
@ -342,9 +393,6 @@ class MacIPTVProvider(override var lang: String) : MainAPI() {
} }
private val arraymediaPlaylist = ArrayList<Channel>()
data class Cmds( data class Cmds(
@JsonProperty("ch_id") var chId: String? = null, @JsonProperty("ch_id") var chId: String? = null,
) )
@ -394,29 +442,42 @@ class MacIPTVProvider(override var lang: String) : MainAPI() {
@JsonProperty("js") var js: Js? = Js() @JsonProperty("js") var js: Js? = Js()
) )
private var codeCountry = lang
var allCategory = mutableListOf<String>()
val rgxcodeCountry = findKeyWord(codeCountry)
var isNotInit = true
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
if (isNotInit || arraymediaPlaylist.isEmpty()) { if (isNotInit || arraymediaPlaylist.isEmpty()) {
if (isNotInit) getAuthHeader() if (isNotInit) getAuthHeader()
val rgxFindJson =
Regex("""\{\"js\"(.*[\r\n]*)+\}""") // select only the json part, some account give a bad json response
var responseAllchannels: NiceResponse? = null var responseAllchannels: NiceResponse? = null
listOf( listOf(
"$mainUrl/portal.php?type=account_info&action=get_main_info&JsHttpRequest=1-xml", "$mainUrl/portal.php?type=account_info&action=get_main_info&JsHttpRequest=1-xml&$defaulmac_adresse", // GET EXPIRATION
"$mainUrl/portal.php?type=itv&action=get_genres&JsHttpRequest=1-xml", "$mainUrl/portal.php?type=itv&action=get_genres", // GET ALL GENRES (Countries or categories)
"$mainUrl/portal.php?type=itv&action=get_all_channels&JsHttpRequest=1-xml", "$mainUrl/portal.php?type=itv&action=get_all_channels", // GET ALL LIVE CAHNNELS
).apmap { url -> ).apmap { url ->
when { when {
url.contains("action=get_main_info") -> { url.contains("action=get_main_info") -> {
expiration = app.get(url, headers = headerMac) val textJson = app.get(
.parsed<GetExpiration>().js?.phone.toString() // GET EXPIRATION url,
headers = headerMac
).text
expiration = tryParseJson<GetExpiration>(
rgxFindJson.find(
textJson
)?.groupValues?.get(0)
)?.js?.phone.toString()
} }
url.contains("action=get_genre") -> { url.contains("action=get_genre") -> {
responseGetGenretoJSON = app.get(url, headers = headerMac) responseGetGenretoJSON =
.parsed<JsonGetGenre>().js // GET ALL GENRES tryParseJson<JsonGetGenre>(
rgxFindJson.find(
app.get(
url,
headers = headerMac
).text
)?.groupValues?.get(0)
)?.js
?: arrayListOf(Js_category("0", ".*"))
} }
url.contains("action=get_all_channels") -> { url.contains("action=get_all_channels") -> {
responseAllchannels = app.get(url, headers = headerMac) responseAllchannels = app.get(url, headers = headerMac)
@ -426,12 +487,38 @@ class MacIPTVProvider(override var lang: String) : MainAPI() {
} }
} }
} }
val allDataJson =
tryParseJson<Root>(
rgxFindJson.find(responseAllchannels?.text.toString())?.groupValues?.get(
0
)
)?.js?.data?.sortByTitleNumber() //use of tryPaseJson to be able to select the json part because sometime the json response is not good
if (!allDataJson.isNullOrEmpty()) {
return HomePageResponse(
HomeResponse(
allDataJson,
).getHomePageListsInit(this), false
)
} else {
return HomePageResponse(
listOf(
HomePageList(
"\uD83C\uDDF5\u200B\u200B\u200B\u200B\u200B\uD83C\uDDF7\u200B\u200B\u200B\u200B\u200B\uD83C\uDDF4\u200B\u200B\u200B\u200B\u200B\uD83C\uDDE7\u200B\u200B\u200B\u200B\u200B\uD83C\uDDF1\u200B\u200B\u200B\u200B\u200B\uD83C\uDDEA\u200B\u200B\u200B\u200B\u200B\uD83C\uDDF2\u200B\u200B\u200B\u200B\u200B",
listOf(
LiveSearchResponse(
"Contact the provider",
"${mainUrl}There_is_an_error", // trick to load the help page
name,
TvType.Live,
"https://bodhisattva4you.files.wordpress.com/2014/11/esprit-probleme-00.jpg",
)
),
isHorizontalImages = true
)
), false
)
}
return HomePageResponse(
HomeResponse(
responseAllchannels!!.parsed<Root>().js!!.data.sortByTitleNumber(),// GET ALL CHANNELS and Sort byTitle
).getHomePageListsInit(this), false
)
} else { } else {
return HomePageResponse( return HomePageResponse(
HomeResponse().getHomePageListsFromArrayChannel(this), false HomeResponse().getHomePageListsFromArrayChannel(this), false
@ -464,7 +551,7 @@ class MacIPTVProvider(override var lang: String) : MainAPI() {
fun getHomePageListsInit(provider: MacIPTVProvider): List<HomePageList> { fun getHomePageListsInit(provider: MacIPTVProvider): List<HomePageList> {
val rgxcodeCountry = provider.rgxcodeCountry val rgxcodeCountry = findKeyWord(provider.lang)
var firstCat = true var firstCat = true
provider.allCategory.clear() provider.allCategory.clear()
@ -482,7 +569,8 @@ class MacIPTVProvider(override var lang: String) : MainAPI() {
} }
} }
val arraychannel = mutableListOf<Channel>() val arraychannel =
mutableListOf<Channel>() // toMutable because HomePageList need to remove element from the list
if (channels.isNotEmpty() && idGenre.contains("""\d""".toRegex()) && (categoryTitle.uppercase() if (channels.isNotEmpty() && idGenre.contains("""\d""".toRegex()) && (categoryTitle.uppercase()
.contains(rgxcodeCountry) || .contains(rgxcodeCountry) ||
categoryTitle.isContainsTargetCountry(provider) categoryTitle.isContainsTargetCountry(provider)
@ -490,7 +578,7 @@ class MacIPTVProvider(override var lang: String) : MainAPI() {
.contains(findKeyWord(tags.toString())) .contains(findKeyWord(tags.toString()))
) { ) {
val itr = channels.iterator() val itr = channels.iterator()
while (itr.hasNext()) { while (itr.hasNext()) { // Remove elements from a list while iterating
val data = itr.next() val data = itr.next()
val genre = data.tvGenreId val genre = data.tvGenreId
if (genre != null) { if (genre != null) {
@ -548,9 +636,10 @@ class MacIPTVProvider(override var lang: String) : MainAPI() {
} }
fun getHomePageListsFromArrayChannel(provider: MacIPTVProvider): List<HomePageList> { fun getHomePageListsFromArrayChannel(provider: MacIPTVProvider): List<HomePageList> {
val rgxcodeCountry = provider.rgxcodeCountry val rgxcodeCountry = findKeyWord(provider.lang)
var firstCat = true var firstCat = true
val arrayChannel = provider.arraymediaPlaylist.toMutableList() val arrayChannel =
provider.arraymediaPlaylist.toMutableList() // toMutable because HomePageList need to remove element from the list
return getHelpHomePage(provider) + responseGetGenretoJSON.mapNotNull { js -> return getHelpHomePage(provider) + responseGetGenretoJSON.mapNotNull { js ->
val idGenre = js.id.toString() val idGenre = js.id.toString()
val categoryTitle = js.title.toString() val categoryTitle = js.title.toString()
@ -583,23 +672,24 @@ class MacIPTVProvider(override var lang: String) : MainAPI() {
} }
} }
/** Since I don't select all the content because it is too big, I want to at least display the countries and categories available.
* So the user will know what is available and can select his own country or categories via the account creation tag */
fun getHelpHomePage(provider: MacIPTVProvider): List<HomePageList> { fun getHelpHomePage(provider: MacIPTVProvider): List<HomePageList> {
val arraychannel = mutableListOf<Channel>()
val helpCat = val helpCat =
"\uD83C\uDDF9\u200B\u200B\u200B\u200B\u200B\uD83C\uDDE6\u200B\u200B\u200B\u200B\u200B\uD83C\uDDEC\u200B\u200B\u200B\u200B\u200B Account" "\uD83C\uDDF9\u200B\u200B\u200B\u200B\u200B\uD83C\uDDE6\u200B\u200B\u200B\u200B\u200B\uD83C\uDDEC\u200B\u200B\u200B\u200B\u200B Account"
arraychannel.add(
Channel( return arrayListOf(
"\uD83D\uDD0E TAG", arrayListOf(
"${provider.mainUrl}I_Need_Help", Channel(
"https://userguiding.com/wp-content/uploads/2021/06/best-help-center-software.jpg", "\uD83D\uDD0E TAG",
"", "${provider.mainUrl}I_Need_Help", // trick to load the help page
"000976000", "https://userguiding.com/wp-content/uploads/2021/06/best-help-center-software.jpg",
"0097600", "",
"" "000976000",
) "0097600",
) ""
return mutableListOf( )
arraychannel.toHomePageList( ).toHomePageList(
helpCat, helpCat,
provider, provider,
"0097600" "0097600"
@ -619,8 +709,8 @@ class MacIPTVProvider(override var lang: String) : MainAPI() {
var responseGetGenretoJSON = ArrayList<Js_category>() // all genres from the provider var responseGetGenretoJSON = ArrayList<Js_category>() // all genres from the provider
fun findKeyWord(str: String): Regex { fun findKeyWord(str: String): Regex {
val upperSTR = str.uppercase() val upperSTR = str.uppercase()
val sequence = when (true) { val sequence = when (upperSTR) {
upperSTR == "EN" -> { "EN" -> {
"US|UK" "US|UK"
} }
else -> upperSTR else -> upperSTR
@ -662,16 +752,16 @@ class MacIPTVProvider(override var lang: String) : MainAPI() {
) )
} }
fun MutableList<Channel>.toSearchResponseHomePage( fun MutableList<Channel>.toSearchResponseHomePage( // require mutableList to be able to remove elements from the list while iterating
provider: MacIPTVProvider, GenreId: String provider: MacIPTVProvider, GenreId: String
): List<SearchResponse> { ): List<SearchResponse> {
val groupChannel = ArrayList<String>() val groupChannel = ArrayList<String>()
var b_new: String var b_new: String
var newgroupChannel: Boolean var newgroupChannel: Boolean
val rgxcodeCountry = provider.rgxcodeCountry val rgxcodeCountry = findKeyWord(provider.lang)
val home = ArrayList<SearchResponse>() val home = ArrayList<SearchResponse>()
val itr = this.iterator() val itr = this.iterator()
while (itr.hasNext()) { while (itr.hasNext()) { //permit to remove elements from the list while iterating
val media = itr.next() val media = itr.next()
val b = cleanTitle(media.title).replace(rgxcodeCountry, "").trim() val b = cleanTitle(media.title).replace(rgxcodeCountry, "").trim()
b_new = b.take(6) b_new = b.take(6)
@ -685,7 +775,7 @@ class MacIPTVProvider(override var lang: String) : MainAPI() {
if (newgroupChannel && media.tv_genre_id == GenreId) { if (newgroupChannel && media.tv_genre_id == GenreId) {
groupChannel.add(b_new) groupChannel.add(b_new)
val groupName = cleanTitle(media.title).replace(rgxcodeCountry, "").trim() val groupName = cleanTitle(media.title).replace(rgxcodeCountry, "").trim()
itr.remove() itr.remove() //remove elements
home.add( home.add(
LiveSearchResponse( LiveSearchResponse(
groupName, groupName,

View file

@ -30,7 +30,7 @@ class MacIptvAPI(index: Int) : InAppAuthAPIManager(index) {
} }
override suspend fun login(data: InAppAuthAPI.LoginData): Boolean { override suspend fun login(data: InAppAuthAPI.LoginData): Boolean {
if (data.server.isNullOrBlank() || !data.password?.contains("""(([0-9A-Za-z]{2}[:-]){5}[0-9A-Za-z]{2})""".toRegex())!!) return false // we require a server if (data.server.isNullOrBlank() || !data.password?.contains("""(([0-9A-Za-z]{2}[:-]){5}[0-9A-Za-z]{2})""".toRegex())!!) return false // we require a server and a mac address
switchToNewAccount() switchToNewAccount()
setKey(accountId, IPTVBOX_USER_KEY, data) setKey(accountId, IPTVBOX_USER_KEY, data)
registerAccount() registerAccount()