mirror of
https://github.com/recloudstream/cloudstream-extensions-multilingual.git
synced 2024-08-15 03:15:14 +00:00
add MacIPTVProvider (tested lang : English, Arabic, French)
This commit is contained in:
parent
33d15586e9
commit
b29fed2c69
21 changed files with 1162 additions and 0 deletions
29
MaciptvProvider/build.gradle.kts
Normal file
29
MaciptvProvider/build.gradle.kts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
dependencies {
|
||||||
|
implementation("androidx.legacy:legacy-support-v4:1.0.0")
|
||||||
|
implementation("com.google.android.material:material:1.4.0")
|
||||||
|
implementation("androidx.lifecycle:lifecycle-extensions:2.2.0")
|
||||||
|
}
|
||||||
|
|
||||||
|
// use an integer for version numbers
|
||||||
|
version = 1
|
||||||
|
|
||||||
|
|
||||||
|
cloudstream {
|
||||||
|
// All of these properties are optional, you can safely remove them
|
||||||
|
|
||||||
|
description = "Add your IPTV account or alternatively, the automatic account will allow you to watch live (Sport, movies, ...)."
|
||||||
|
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("Live")
|
||||||
|
|
||||||
|
iconUrl = "https://www.google.com/s2/favicons?domain=franceiptv.fr/&sz=%size%"
|
||||||
|
requiresResources = true
|
||||||
|
}
|
2
MaciptvProvider/src/main/AndroidManifest.xml
Normal file
2
MaciptvProvider/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest package="com.lagradost"/>
|
746
MaciptvProvider/src/main/kotlin/com/lagradost/MacIPTVProvider.kt
Normal file
746
MaciptvProvider/src/main/kotlin/com/lagradost/MacIPTVProvider.kt
Normal file
|
@ -0,0 +1,746 @@
|
||||||
|
package com.lagradost
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
|
import com.lagradost.cloudstream3.*
|
||||||
|
import okhttp3.Interceptor
|
||||||
|
import com.lagradost.cloudstream3.utils.*
|
||||||
|
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
||||||
|
import com.lagradost.nicehttp.NiceResponse
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import me.xdrop.fuzzywuzzy.FuzzySearch
|
||||||
|
|
||||||
|
|
||||||
|
class MacIPTVProvider(override var lang: String) : MainAPI() {
|
||||||
|
private val defaulmac_adresse =
|
||||||
|
"mac=00:1a:79:ae:2a:30"
|
||||||
|
private val defaultmainUrl =
|
||||||
|
"http://ultra-box.club/c/"
|
||||||
|
var defaultname = "BoxIPTV-MatrixOTT |${lang.uppercase()}|"
|
||||||
|
override var name = "Box Iptv |${lang.uppercase()}|"
|
||||||
|
override val hasQuickSearch = false
|
||||||
|
override val hasMainPage = true
|
||||||
|
override val supportedTypes =
|
||||||
|
setOf(TvType.Live) // live
|
||||||
|
|
||||||
|
private var init = false
|
||||||
|
private var key: String? = ""
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
var companionName: String? = null
|
||||||
|
var loginMac: String? = null
|
||||||
|
var overrideUrl: String? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getAuthHeader(): Map<String, String> {
|
||||||
|
|
||||||
|
mainUrl = overrideUrl.toString()
|
||||||
|
val main = mainUrl.uppercase().trim()
|
||||||
|
val localCredentials = loginMac
|
||||||
|
val mac = localCredentials?.uppercase()?.trim()
|
||||||
|
|
||||||
|
if (main == "NONE" || main.isBlank() || mac == "NONE" || mac.isNullOrBlank()) {
|
||||||
|
mainUrl = defaultmainUrl
|
||||||
|
name = defaultname
|
||||||
|
|
||||||
|
if (!init) {
|
||||||
|
val url_key = "$mainUrl/portal.php?type=stb&action=handshake&JsHttpRequest=1-xml"
|
||||||
|
val reponseGetkey = app.get(
|
||||||
|
url_key, headers = mapOf(
|
||||||
|
"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",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val keyJson = reponseGetkey.parsed<Getkey>()
|
||||||
|
key = keyJson.js?.token
|
||||||
|
}
|
||||||
|
init = true
|
||||||
|
return mapOf(
|
||||||
|
"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",
|
||||||
|
"Authorization" to "Bearer $key",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
name = (companionName ?: name) + " |${lang.uppercase()}|"
|
||||||
|
if (!init) {
|
||||||
|
val url_key = "$mainUrl/portal.php?type=stb&action=handshake&JsHttpRequest=1-xml"
|
||||||
|
val reponseGetkey = app.get(
|
||||||
|
url_key, headers = mapOf(
|
||||||
|
"Cookie" to "mac=$localCredentials",
|
||||||
|
"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",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val keyJson = reponseGetkey.parsed<Getkey>()
|
||||||
|
key = keyJson.js?.token
|
||||||
|
}
|
||||||
|
init = true
|
||||||
|
return mapOf(
|
||||||
|
"Cookie" to "mac=$localCredentials",
|
||||||
|
"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",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun List<Channel>.sortByname(query: String?): List<Channel> {
|
||||||
|
return if (query == null) {
|
||||||
|
// Return list to base state if no query
|
||||||
|
this.sortedBy { it.title }
|
||||||
|
} else {
|
||||||
|
|
||||||
|
this.sortedBy {
|
||||||
|
val name = cleanTitle(it.title)
|
||||||
|
-FuzzySearch.ratio(name.lowercase(), query.lowercase())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
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
|
||||||
|
**/
|
||||||
|
private val resultsSearchNbr = 50
|
||||||
|
override suspend fun search(query: String): List<SearchResponse> {
|
||||||
|
val searchResutls = ArrayList<SearchResponse>()
|
||||||
|
arraymediaPlaylist.sortByname(query).take(resultsSearchNbr).forEach { media ->
|
||||||
|
searchResutls.add(
|
||||||
|
LiveSearchResponse(
|
||||||
|
media.title,
|
||||||
|
media.url,
|
||||||
|
name,
|
||||||
|
TvType.Live,
|
||||||
|
media.url_image,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return searchResutls
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Root_epg(
|
||||||
|
|
||||||
|
@JsonProperty("js") var js: ArrayList<Js_epg> = arrayListOf()
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Js_epg(
|
||||||
|
|
||||||
|
/* @JsonProperty("id") var id: String? = null,
|
||||||
|
@JsonProperty("ch_id") var chId: String? = null,
|
||||||
|
@JsonProperty("correct") var correct: String? = null,
|
||||||
|
@JsonProperty("time") var time: String? = null,
|
||||||
|
@JsonProperty("time_to") var timeTo: String? = null,
|
||||||
|
@JsonProperty("duration") var duration: Int? = null,*/
|
||||||
|
@JsonProperty("name") var name: String? = null,
|
||||||
|
@JsonProperty("descr") var descr: String? = null,
|
||||||
|
/* @JsonProperty("real_id") var realId: String? = null,
|
||||||
|
@JsonProperty("category") var category: String? = null,
|
||||||
|
@JsonProperty("director") var director: String? = null,
|
||||||
|
@JsonProperty("actor") var actor: String? = null,
|
||||||
|
@JsonProperty("start_timestamp") var startTimestamp: Int? = null,
|
||||||
|
@JsonProperty("stop_timestamp") var stopTimestamp: Int? = null,*/
|
||||||
|
@JsonProperty("t_time") var tTime: String? = null,
|
||||||
|
@JsonProperty("t_time_to") var tTimeTo: String? = null,
|
||||||
|
/* @JsonProperty("mark_memo") var markMemo: Int? = null,
|
||||||
|
@JsonProperty("mark_archive") var markArchive: Int? = null*/
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun getEpg(response: String): String {
|
||||||
|
val reponseJSON_0 = tryParseJson<Root_epg>(response)
|
||||||
|
var description = ""
|
||||||
|
reponseJSON_0?.js?.forEach { epg_i ->
|
||||||
|
val name = epg_i.name
|
||||||
|
val descr = epg_i.descr
|
||||||
|
val t_time = epg_i.tTime
|
||||||
|
val t_time_to = epg_i.tTimeTo
|
||||||
|
val new_descr = "||($t_time -> $t_time_to) $name : $descr"
|
||||||
|
if (!description.contains(new_descr)) {
|
||||||
|
description = "$description\n $new_descr"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return description
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
override suspend fun load(url: String): LoadResponse {
|
||||||
|
|
||||||
|
var link = ""
|
||||||
|
var title = ""
|
||||||
|
var posterUrl = ""
|
||||||
|
var description = ""
|
||||||
|
val header = getAuthHeader()
|
||||||
|
val allresultshome: MutableList<SearchResponse> = mutableListOf()
|
||||||
|
for (media in arraymediaPlaylist) {
|
||||||
|
val keyId = "/-${media.id}-"
|
||||||
|
if (url.contains(keyId) || url == media.url) {
|
||||||
|
val epg_url =
|
||||||
|
"$mainUrl/portal.php?type=itv&action=get_short_epg&ch_id=${media.ch_id}&size=10&JsHttpRequest=1-xml" // descriptif
|
||||||
|
val response = app.get(epg_url, headers = header)
|
||||||
|
description = getEpg(response.text)
|
||||||
|
link = media.url
|
||||||
|
title = media.title
|
||||||
|
val a = cleanTitle(title)
|
||||||
|
posterUrl = media.url_image.toString()
|
||||||
|
var b_new: String
|
||||||
|
arraymediaPlaylist.forEach { channel ->
|
||||||
|
val b = cleanTitle(channel.title)
|
||||||
|
b_new = b.take(6)
|
||||||
|
if (channel.id != media.id && a.take(6)
|
||||||
|
.contains(b_new) && media.tv_genre_id == channel.tv_genre_id
|
||||||
|
) {
|
||||||
|
val streamurl = channel.url
|
||||||
|
val channelname = channel.title
|
||||||
|
val posterurl = channel.url_image.toString()
|
||||||
|
val uppername = channelname.uppercase()
|
||||||
|
val quality = getQualityFromString(
|
||||||
|
when (!channelname.isNullOrBlank()) {
|
||||||
|
uppername.contains(findKeyWord("UHD")) -> {
|
||||||
|
"UHD"
|
||||||
|
}
|
||||||
|
uppername.contains(findKeyWord("HD")) -> {
|
||||||
|
"HD"
|
||||||
|
}
|
||||||
|
uppername.contains(findKeyWord("SD")) -> {
|
||||||
|
"SD"
|
||||||
|
}
|
||||||
|
uppername.contains(findKeyWord("FHD")) -> {
|
||||||
|
"HD"
|
||||||
|
}
|
||||||
|
uppername.contains(findKeyWord("4K")) -> {
|
||||||
|
"FourK"
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
allresultshome.add(
|
||||||
|
LiveSearchResponse(
|
||||||
|
name = cleanTitleButKeepNumber(channelname),
|
||||||
|
url = streamurl,
|
||||||
|
name,
|
||||||
|
TvType.Live,
|
||||||
|
posterUrl = posterurl,
|
||||||
|
quality = quality,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allresultshome.size >= 1) {
|
||||||
|
val recommendation = allresultshome
|
||||||
|
return LiveStreamLoadResponse(
|
||||||
|
name = title,
|
||||||
|
url = link,
|
||||||
|
apiName = this.name,
|
||||||
|
dataUrl = link,
|
||||||
|
posterUrl = posterUrl,
|
||||||
|
plot = description,
|
||||||
|
recommendations = recommendation
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return LiveStreamLoadResponse(
|
||||||
|
name = title,
|
||||||
|
url = link,
|
||||||
|
apiName = this.name,
|
||||||
|
dataUrl = link,
|
||||||
|
posterUrl = posterUrl,
|
||||||
|
plot = description,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** get the channel id */
|
||||||
|
val regexCode_ch = Regex("""\/(\d*)\?""")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use new token.
|
||||||
|
* */
|
||||||
|
override fun getVideoInterceptor(extractorLink: ExtractorLink): Interceptor {
|
||||||
|
// Needs to be object instead of lambda to make it compile correctly
|
||||||
|
return object : Interceptor {
|
||||||
|
override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
|
||||||
|
val request = chain.request()
|
||||||
|
if (request.url.toString().contains("token")
|
||||||
|
|
||||||
|
) {
|
||||||
|
val chID = regexCode_ch.find(request.url.toString())!!.groupValues[1] + "_"
|
||||||
|
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"
|
||||||
|
|
||||||
|
var link: String
|
||||||
|
var lien: String
|
||||||
|
runBlocking {
|
||||||
|
val header = getAuthHeader()
|
||||||
|
val getTokenLink = app.get(TokenLink, headers = header).text
|
||||||
|
val regexGetLink = Regex("""(http.*)\"\},""")
|
||||||
|
link = regexGetLink.find(getTokenLink)?.groupValues?.get(1).toString()
|
||||||
|
.replace(
|
||||||
|
"""\""",
|
||||||
|
""
|
||||||
|
)
|
||||||
|
lien = link
|
||||||
|
if (link.contains("extension")) {
|
||||||
|
val headerlocation = app.get(
|
||||||
|
link,
|
||||||
|
allowRedirects = false
|
||||||
|
).headers
|
||||||
|
val redirectlink = headerlocation.get("location")
|
||||||
|
.toString()
|
||||||
|
if (redirectlink != "null") {
|
||||||
|
lien = redirectlink
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val newRequest = chain.request()
|
||||||
|
.newBuilder().url(lien).build()
|
||||||
|
return chain.proceed(newRequest)
|
||||||
|
} else {
|
||||||
|
return chain.proceed(chain.request())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 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 header = getAuthHeader()
|
||||||
|
val TokenLink =
|
||||||
|
"$mainUrl/portal.php?type=itv&action=create_link&cmd=ffmpeg%20$data&series=&forced_storage=0&disable_ad=0&download=0&force_ch_link_check=0&JsHttpRequest=1-xml"
|
||||||
|
val getTokenLink = app.get(TokenLink, headers = header).text
|
||||||
|
val regexGetLink = Regex("""(http.*)\"\},""")
|
||||||
|
val link =
|
||||||
|
regexGetLink.find(getTokenLink)?.groupValues?.get(1).toString().replace("""\""", "")
|
||||||
|
|
||||||
|
|
||||||
|
val head = mapOf(
|
||||||
|
"Accept" to "*/*",
|
||||||
|
"Accept-Language" to "en_US",
|
||||||
|
"User-Agent" to "VLC/3.0.18 LibVLC/3.0.18",
|
||||||
|
"Range" to "bytes=0-"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
var lien = link
|
||||||
|
if (link.contains("extension")) {
|
||||||
|
val headerlocation = app.get(
|
||||||
|
link,
|
||||||
|
allowRedirects = false
|
||||||
|
).headers
|
||||||
|
val redirectlink = headerlocation.get("location")
|
||||||
|
.toString()
|
||||||
|
|
||||||
|
if (redirectlink != "null") {
|
||||||
|
lien = redirectlink
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val isM3u8 = false// lien.contains("extension=ts")
|
||||||
|
callback.invoke(
|
||||||
|
ExtractorLink(
|
||||||
|
name,
|
||||||
|
name,
|
||||||
|
lien,
|
||||||
|
mainUrl,
|
||||||
|
Qualities.Unknown.value,
|
||||||
|
isM3u8 = isM3u8,
|
||||||
|
headers = head
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private val arraymediaPlaylist = ArrayList<Channel>()
|
||||||
|
|
||||||
|
data class Channel(
|
||||||
|
var title: String,
|
||||||
|
var url: String,
|
||||||
|
val url_image: String?,
|
||||||
|
val lang: String?,
|
||||||
|
var id: String?,
|
||||||
|
var tv_genre_id: String?,
|
||||||
|
var ch_id: String?,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Cmds(
|
||||||
|
|
||||||
|
// @JsonProperty("id") var id: String? = null,
|
||||||
|
@JsonProperty("ch_id") var chId: String? = null,
|
||||||
|
/* @JsonProperty("priority") var priority: String? = null,
|
||||||
|
@JsonProperty("url") var url: String? = null,
|
||||||
|
@JsonProperty("status") var status: String? = null,
|
||||||
|
@JsonProperty("use_http_tmp_link") var useHttpTmpLink: String? = null,
|
||||||
|
@JsonProperty("wowza_tmp_link") var wowzaTmpLink: String? = null,
|
||||||
|
@JsonProperty("user_agent_filter") var userAgentFilter: String? = null,
|
||||||
|
@JsonProperty("use_load_balancing") var useLoadBalancing: String? = null,
|
||||||
|
@JsonProperty("changed") var changed: String? = null,
|
||||||
|
@JsonProperty("enable_monitoring") var enableMonitoring: String? = null,
|
||||||
|
@JsonProperty("enable_balancer_monitoring") var enableBalancerMonitoring: String? = null,
|
||||||
|
@JsonProperty("nginx_secure_link") var nginxSecureLink: String? = null,
|
||||||
|
@JsonProperty("flussonic_tmp_link") var flussonicTmpLink: String? = null*/
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Data(
|
||||||
|
|
||||||
|
@JsonProperty("id") var id: String? = null,
|
||||||
|
@JsonProperty("name") var name: String? = null,
|
||||||
|
@JsonProperty("number") var number: String? = null,
|
||||||
|
/* @JsonProperty("censored") var censored: Int? = null,
|
||||||
|
@JsonProperty("cmd") var cmd: String? = null,
|
||||||
|
@JsonProperty("cost") var cost: String? = null,
|
||||||
|
@JsonProperty("count") var count: String? = null,
|
||||||
|
@JsonProperty("status") var status: Int? = null,
|
||||||
|
@JsonProperty("hd") var hd: String? = null,*/
|
||||||
|
@JsonProperty("tv_genre_id") var tvGenreId: String? = null,
|
||||||
|
/* @JsonProperty("base_ch") var baseCh: String? = null,
|
||||||
|
@JsonProperty("xmltv_id") var xmltvId: String? = null,
|
||||||
|
@JsonProperty("service_id") var serviceId: String? = null,
|
||||||
|
@JsonProperty("bonus_ch") var bonusCh: String? = null,
|
||||||
|
@JsonProperty("volume_correction") var volumeCorrection: String? = null,
|
||||||
|
@JsonProperty("mc_cmd") var mcCmd: String? = null,
|
||||||
|
@JsonProperty("enable_tv_archive") var enableTvArchive: Int? = null,
|
||||||
|
@JsonProperty("wowza_tmp_link") var wowzaTmpLink: String? = null,
|
||||||
|
@JsonProperty("wowza_dvr") var wowzaDvr: String? = null,
|
||||||
|
@JsonProperty("use_http_tmp_link") var useHttpTmpLink: String? = null,
|
||||||
|
@JsonProperty("monitoring_status") var monitoringStatus: String? = null,
|
||||||
|
@JsonProperty("enable_monitoring") var enableMonitoring: String? = null,
|
||||||
|
@JsonProperty("enable_wowza_load_balancing") var enableWowzaLoadBalancing: String? = null,
|
||||||
|
@JsonProperty("cmd_1") var cmd1: String? = null,
|
||||||
|
@JsonProperty("cmd_2") var cmd2: String? = null,
|
||||||
|
@JsonProperty("cmd_3") var cmd3: String? = null,*/
|
||||||
|
@JsonProperty("logo") var logo: String? = null,
|
||||||
|
/* @JsonProperty("correct_time") var correctTime: String? = null,
|
||||||
|
@JsonProperty("nimble_dvr") var nimbleDvr: String? = null,
|
||||||
|
@JsonProperty("allow_pvr") var allowPvr: Int? = null,
|
||||||
|
@JsonProperty("allow_local_pvr") var allowLocalPvr: Int? = null,
|
||||||
|
@JsonProperty("allow_remote_pvr") var allowRemotePvr: Int? = null,
|
||||||
|
@JsonProperty("modified") var modified: String? = null,
|
||||||
|
@JsonProperty("allow_local_timeshift") var allowLocalTimeshift: String? = null,
|
||||||
|
@JsonProperty("nginx_secure_link") var nginxSecureLink: String? = null,
|
||||||
|
@JsonProperty("tv_archive_duration") var tvArchiveDuration: Int? = null,
|
||||||
|
@JsonProperty("locked") var locked: Int? = null,
|
||||||
|
@JsonProperty("lock") var lock: Int? = null,
|
||||||
|
@JsonProperty("fav") var fav: Int? = null,
|
||||||
|
@JsonProperty("archive") var archive: Int? = null,
|
||||||
|
@JsonProperty("genres_str") var genresStr: String? = null,
|
||||||
|
@JsonProperty("cur_playing") var curPlaying: String? = null,
|
||||||
|
@JsonProperty("epg") var epg: ArrayList<String> = arrayListOf(),
|
||||||
|
@JsonProperty("open") var open: Int? = null,*/
|
||||||
|
@JsonProperty("cmds") var cmds: ArrayList<Cmds> = arrayListOf(),
|
||||||
|
/* @JsonProperty("use_load_balancing") var useLoadBalancing: Int? = null,
|
||||||
|
@JsonProperty("pvr") var pvr: Int? = null*/
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
data class JsKey(
|
||||||
|
|
||||||
|
@JsonProperty("token") var token: String? = null
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Getkey(
|
||||||
|
|
||||||
|
@JsonProperty("js") var js: JsKey? = JsKey()
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
data class JsInfo(
|
||||||
|
|
||||||
|
@JsonProperty("mac") var mac: String? = null,
|
||||||
|
@JsonProperty("phone") var phone: String? = null
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
data class GetExpiration(
|
||||||
|
|
||||||
|
@JsonProperty("js") var js: JsInfo? = JsInfo()
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Js(
|
||||||
|
|
||||||
|
@JsonProperty("total_items") var totalItems: Int? = null,
|
||||||
|
@JsonProperty("max_page_items") var maxPageItems: Int? = null,
|
||||||
|
/* @JsonProperty("selected_item") var selectedItem: Int? = null,
|
||||||
|
@JsonProperty("cur_page") var curPage: Int? = null,*/
|
||||||
|
@JsonProperty("data") var data: ArrayList<Data> = arrayListOf()
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
data class JsonGetGenre(
|
||||||
|
|
||||||
|
@JsonProperty("js") var js: ArrayList<Js_category> = arrayListOf()
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Js_category(
|
||||||
|
|
||||||
|
@JsonProperty("id") var id: String? = null,
|
||||||
|
@JsonProperty("title") var title: String? = null,
|
||||||
|
/* @JsonProperty("alias") var alias: String? = null,
|
||||||
|
@JsonProperty("active_sub") var activeSub: Boolean? = null,
|
||||||
|
@JsonProperty("censored") var censored: Int? = null*/
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Root(
|
||||||
|
|
||||||
|
@JsonProperty("js") var js: Js? = Js()
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
private var codeCountry = lang
|
||||||
|
private fun findKeyWord(str: String): Regex {
|
||||||
|
val upperSTR = str.uppercase()
|
||||||
|
val sequence = when (true) {
|
||||||
|
(upperSTR == "EN") -> {
|
||||||
|
"US|UK"
|
||||||
|
}
|
||||||
|
else -> upperSTR
|
||||||
|
}
|
||||||
|
return """(?:^|\W+|\s)+($sequence)(?:\s|\W+|${'$'}|\|)+""".toRegex()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.isContainsTargetCountry(): Boolean {
|
||||||
|
val getLang = lang.uppercase()
|
||||||
|
var resp = false
|
||||||
|
when (true) {
|
||||||
|
(getLang == "FR") -> {
|
||||||
|
arrayListOf("FRENCH", "FRANCE").forEach {
|
||||||
|
if (this.uppercase().contains(findKeyWord(it))) {
|
||||||
|
resp = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(getLang == "EN") -> {
|
||||||
|
arrayListOf("ENGLISH", "USA").forEach {
|
||||||
|
if (this.uppercase().contains(findKeyWord(it))) {
|
||||||
|
resp = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(getLang == "AR") -> {
|
||||||
|
arrayListOf("ARABIC", "ARAB", "ARABIA").forEach {
|
||||||
|
if (this.uppercase().contains(findKeyWord(it))) {
|
||||||
|
resp = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> resp = false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cleanTitle(title: String): String {
|
||||||
|
return title.uppercase().replace("""(\s\d{1,}${'$'}|\s\d{1,}\s)""".toRegex(), " ")
|
||||||
|
.replace("""FHD""", "")
|
||||||
|
.replace(findKeyWord("VIP"), "")
|
||||||
|
.replace("""UHD""", "").replace(rgxcodeCountry, "").replace("""HEVC""", "")
|
||||||
|
.replace("""HDR""", "").replace("""SD""", "").replace("""4K""", "")
|
||||||
|
.replace("""HD""", "").replace(rgxcodeCountry, "").trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun getFlag(sequence: String): String {
|
||||||
|
val FR = findKeyWord("FR|FRANCE|FRENCH")
|
||||||
|
val US = findKeyWord("US|USA")
|
||||||
|
val AR = findKeyWord("AR|ARAB|ARABIC|ARABIA")
|
||||||
|
val UK = findKeyWord("UK")
|
||||||
|
val flag: String
|
||||||
|
flag = when (true) {
|
||||||
|
sequence.uppercase()
|
||||||
|
.contains(FR) -> "\uD83C\uDDE8\uD83C\uDDF5"
|
||||||
|
sequence.uppercase()
|
||||||
|
.contains(US) -> "\uD83C\uDDFA\uD83C\uDDF8"
|
||||||
|
sequence.uppercase()
|
||||||
|
.contains(UK) -> "\uD83C\uDDEC\uD83C\uDDE7"
|
||||||
|
sequence.uppercase()
|
||||||
|
.contains(AR) -> " نظرة"
|
||||||
|
else -> ""
|
||||||
|
}
|
||||||
|
return flag
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val rgxcodeCountry = findKeyWord(codeCountry)
|
||||||
|
val arrayHomepage = arrayListOf<HomePageList>()
|
||||||
|
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
|
||||||
|
|
||||||
|
|
||||||
|
if (!init) {
|
||||||
|
val header = getAuthHeader()
|
||||||
|
val url_info =
|
||||||
|
"$mainUrl/portal.php?type=account_info&action=get_main_info&JsHttpRequest=1-xml"
|
||||||
|
val urlGetGenre =
|
||||||
|
"$mainUrl/portal.php?type=itv&action=get_genres&JsHttpRequest=1-xml"
|
||||||
|
val urlGetallchannels =
|
||||||
|
"$mainUrl/portal.php?type=itv&action=get_all_channels&JsHttpRequest=1-xml"
|
||||||
|
|
||||||
|
var reponseGetInfo: NiceResponse? = null
|
||||||
|
var responseGetgenre: NiceResponse? = null
|
||||||
|
var responseAllchannels: NiceResponse? = null
|
||||||
|
listOf(
|
||||||
|
url_info,
|
||||||
|
urlGetGenre,
|
||||||
|
urlGetallchannels
|
||||||
|
).apmap { url ->
|
||||||
|
val response = app.get(url, headers = header)
|
||||||
|
when (true) {
|
||||||
|
url.contains("action=get_main_info") -> {
|
||||||
|
reponseGetInfo = response
|
||||||
|
}
|
||||||
|
url.contains("action=get_genre") -> {
|
||||||
|
responseGetgenre = response
|
||||||
|
}
|
||||||
|
url.contains("action=get_all_channels") -> {
|
||||||
|
responseAllchannels = response
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
///////////// GET EXPIRATION
|
||||||
|
val infoExpirationJson = reponseGetInfo!!.parsed<GetExpiration>()
|
||||||
|
val expiration = infoExpirationJson.js?.phone
|
||||||
|
////////////////////////// GET ALL GENRES
|
||||||
|
val responseGetGenretoJSON = responseGetgenre!!.parsed<JsonGetGenre>()
|
||||||
|
////////////////////////// GET ALL CHANNELS
|
||||||
|
val responseAllchannelstoJSON = responseAllchannels!!.parsed<Root>()
|
||||||
|
val AllchannelstoJSON = responseAllchannelstoJSON.js!!.data.sortByTitleNumber()
|
||||||
|
var firstCat = true
|
||||||
|
responseGetGenretoJSON.js.forEach { js ->
|
||||||
|
val idGenre = js.id
|
||||||
|
val categoryTitle = js.title.toString()
|
||||||
|
|
||||||
|
if (idGenre!!.contains("""\d""".toRegex()) && (categoryTitle.uppercase()
|
||||||
|
.contains(rgxcodeCountry) ||
|
||||||
|
categoryTitle.isContainsTargetCountry()
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
AllchannelstoJSON.forEach { data ->
|
||||||
|
val genre = data.tvGenreId
|
||||||
|
|
||||||
|
if (genre != null) {
|
||||||
|
if (genre == idGenre) {
|
||||||
|
val name = data.name.toString()
|
||||||
|
val tv_genre_id = data.tvGenreId
|
||||||
|
val idCH = data.id
|
||||||
|
val link = "http://localhost/ch/$idCH" + "_"
|
||||||
|
val logo = data.logo?.replace("""\""", "")
|
||||||
|
val ch_id = data.cmds[0].chId
|
||||||
|
arraymediaPlaylist.add(
|
||||||
|
Channel(
|
||||||
|
name,
|
||||||
|
link,
|
||||||
|
logo,
|
||||||
|
"",
|
||||||
|
idCH,
|
||||||
|
tv_genre_id,
|
||||||
|
ch_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/***************************************** */
|
||||||
|
|
||||||
|
val groupMedia = ArrayList<String>()
|
||||||
|
var b_new: String
|
||||||
|
var newgroupMedia: Boolean
|
||||||
|
val home = arraymediaPlaylist.mapNotNull { media ->
|
||||||
|
val b = cleanTitle(media.title)//
|
||||||
|
b_new = b.take(6)
|
||||||
|
newgroupMedia = true
|
||||||
|
for (nameMedia in groupMedia) {
|
||||||
|
if (nameMedia.contains(b_new) && (media.tv_genre_id == idGenre)) {
|
||||||
|
newgroupMedia = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
groupMedia.contains(b_new)
|
||||||
|
if (page == 1 && (media.tv_genre_id == idGenre) && newgroupMedia
|
||||||
|
) {
|
||||||
|
groupMedia.add(b_new)
|
||||||
|
val groupName = cleanTitle(media.title)
|
||||||
|
|
||||||
|
LiveSearchResponse(
|
||||||
|
groupName,
|
||||||
|
"$mainUrl/-${media.id}-",
|
||||||
|
name,
|
||||||
|
TvType.Live,
|
||||||
|
media.url_image,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val flag: String
|
||||||
|
if ((categoryTitle.uppercase()
|
||||||
|
.contains(rgxcodeCountry) || categoryTitle.isContainsTargetCountry())
|
||||||
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
flag = getFlag(categoryTitle)
|
||||||
|
val nameGenre = if (firstCat) {
|
||||||
|
firstCat = false
|
||||||
|
"$flag ${cleanTitle(categoryTitle)} \uD83D\uDCFA $name \uD83D\uDCFA (⏳ $expiration)"
|
||||||
|
} else {
|
||||||
|
"$flag ${cleanTitle(categoryTitle)}"
|
||||||
|
}
|
||||||
|
arrayHomepage.add(HomePageList(nameGenre, home, isHorizontalImages = true))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return HomePageResponse(
|
||||||
|
arrayHomepage
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cleanTitleButKeepNumber(title: String): String {
|
||||||
|
return title.uppercase().replace("""FHD""", "")
|
||||||
|
.replace(findKeyWord("VIP"), "")
|
||||||
|
.replace("""UHD""", "").replace("""HEVC""", "")
|
||||||
|
.replace("""HDR""", "").replace("""SD""", "").replace("""4K""", "")
|
||||||
|
.replace("""HD""", "").replace(rgxcodeCountry, "").trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ArrayList<Data>.sortByTitleNumber(): ArrayList<Data> {
|
||||||
|
val regxNbr = Regex("""(\s\d{1,}${'$'}|\s\d{1,}\s)""")
|
||||||
|
return ArrayList(this.sortedBy {
|
||||||
|
val str = it.name.toString()
|
||||||
|
regxNbr.find(str)?.groupValues?.get(0)?.trim()?.toInt() ?: 1000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
|
||||||
|
package com.lagradost
|
||||||
|
|
||||||
|
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||||
|
import com.lagradost.cloudstream3.plugins.Plugin
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import com.lagradost.cloudstream3.AcraApplication.Companion.context
|
||||||
|
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||||
|
import com.lagradost.cloudstream3.AcraApplication.Companion.getActivity
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.SettingsAccount
|
||||||
|
|
||||||
|
@CloudstreamPlugin
|
||||||
|
class MacIPTVProviderPlugin : Plugin() {
|
||||||
|
val iptvboxApi = MacIptvAPI(0)
|
||||||
|
|
||||||
|
override fun load(context: Context) {
|
||||||
|
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||||
|
iptvboxApi.init()
|
||||||
|
registerMainAPI(MacIPTVProvider("fr"))
|
||||||
|
registerMainAPI(MacIPTVProvider("en"))
|
||||||
|
registerMainAPI(MacIPTVProvider("ar"))
|
||||||
|
ioSafe {
|
||||||
|
iptvboxApi.initialize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
this.openSettings = {
|
||||||
|
val activity = it as? AppCompatActivity
|
||||||
|
if (activity != null) {
|
||||||
|
val frag = MacIptvSettingsFragment(this, iptvboxApi)
|
||||||
|
frag.show(activity.supportFragmentManager, iptvboxApi.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
64
MaciptvProvider/src/main/kotlin/com/lagradost/MacIptvAPI.kt
Normal file
64
MaciptvProvider/src/main/kotlin/com/lagradost/MacIptvAPI.kt
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package com.lagradost
|
||||||
|
|
||||||
|
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
||||||
|
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
||||||
|
import com.lagradost.cloudstream3.R
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.AccountManager
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.AuthAPI
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.InAppAuthAPI
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.InAppAuthAPIManager
|
||||||
|
|
||||||
|
class MacIptvAPI(index: Int) : InAppAuthAPIManager(index) {
|
||||||
|
override val name = "Iptvbox"
|
||||||
|
override val idPrefix = "iptvbox"
|
||||||
|
override val icon = R.drawable.ic_baseline_extension_24
|
||||||
|
override val requiresUsername = true
|
||||||
|
override val requiresPassword = true
|
||||||
|
override val requiresServer = true
|
||||||
|
override val createAccountUrl = ""
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val IPTVBOX_USER_KEY: String = "iptvbox_user"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getLatestLoginData(): InAppAuthAPI.LoginData? {
|
||||||
|
return getKey(accountId, IPTVBOX_USER_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun loginInfo(): AuthAPI.LoginInfo? {
|
||||||
|
val data = getLatestLoginData() ?: return null
|
||||||
|
return AuthAPI.LoginInfo(name = data.username ?: data.server, accountIndex = accountIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun login(data: InAppAuthAPI.LoginData): Boolean {
|
||||||
|
if (data.server.isNullOrBlank()) return false // we require a server
|
||||||
|
switchToNewAccount()
|
||||||
|
setKey(accountId, IPTVBOX_USER_KEY, data)
|
||||||
|
registerAccount()
|
||||||
|
initialize()
|
||||||
|
AccountManager.inAppAuths
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun logOut() {
|
||||||
|
removeAccountKeys()
|
||||||
|
initializeData()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initializeData() {
|
||||||
|
val data = getLatestLoginData() ?: run {
|
||||||
|
MacIPTVProvider.overrideUrl = null
|
||||||
|
MacIPTVProvider.loginMac = null
|
||||||
|
MacIPTVProvider.companionName = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
MacIPTVProvider.overrideUrl = data.server?.removeSuffix("/")
|
||||||
|
MacIPTVProvider.loginMac = data.password ?: ""
|
||||||
|
MacIPTVProvider.companionName = data.username
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun initialize() {
|
||||||
|
initializeData()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
package com.lagradost
|
||||||
|
|
||||||
|
import android.content.res.ColorStateList
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.core.content.res.ResourcesCompat
|
||||||
|
import com.lagradost.cloudstream3.R
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser
|
||||||
|
import com.lagradost.cloudstream3.plugins.Plugin
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.SettingsAccount.Companion.showLoginInfo
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.SettingsAccount.Companion.addAccount
|
||||||
|
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
||||||
|
|
||||||
|
|
||||||
|
class MacIptvSettingsFragment(private val plugin: Plugin, val maciptvAPI: MacIptvAPI) :
|
||||||
|
BottomSheetDialogFragment() {
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View? {
|
||||||
|
// Inflate the layout for this fragment
|
||||||
|
val id = plugin.resources!!.getIdentifier("nginx_settings", "layout", BuildConfig.LIBRARY_PACKAGE_NAME)
|
||||||
|
val layout = plugin.resources!!.getLayout(id)
|
||||||
|
return inflater.inflate(layout, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T : View> View.findView(name: String): T {
|
||||||
|
val id = plugin.resources!!.getIdentifier(name, "id", BuildConfig.LIBRARY_PACKAGE_NAME)
|
||||||
|
return this.findViewById(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDrawable(name: String): Drawable? {
|
||||||
|
val id = plugin.resources!!.getIdentifier(name, "drawable", BuildConfig.LIBRARY_PACKAGE_NAME)
|
||||||
|
return ResourcesCompat.getDrawable(plugin.resources!!, id, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getString(name: String): String? {
|
||||||
|
val id = plugin.resources!!.getIdentifier(name, "string", BuildConfig.LIBRARY_PACKAGE_NAME)
|
||||||
|
return plugin.resources!!.getString(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
val infoView = view.findView<LinearLayout>("nginx_info")
|
||||||
|
val infoTextView = view.findView<TextView>("info_main_text")
|
||||||
|
val infoSubTextView = view.findView<TextView>("info_sub_text")
|
||||||
|
val infoImageView = view.findView<ImageView>("nginx_info_imageview")
|
||||||
|
|
||||||
|
infoTextView.text = getString("nginx_info_title") ?: "Nginx"
|
||||||
|
infoSubTextView.text = getString("nginx_info_summary") ?: ""
|
||||||
|
infoImageView.setImageDrawable(getDrawable("nginx_question"))
|
||||||
|
infoImageView.imageTintList =
|
||||||
|
ColorStateList.valueOf(view.context.colorFromAttribute(R.attr.white))
|
||||||
|
|
||||||
|
val loginView = view.findView<LinearLayout>("nginx_login")
|
||||||
|
val loginTextView = view.findView<TextView>("main_text")
|
||||||
|
val loginImageView = view.findView<ImageView>("nginx_login_imageview")
|
||||||
|
loginImageView.setImageDrawable(getDrawable("nginx"))
|
||||||
|
loginImageView.imageTintList =
|
||||||
|
ColorStateList.valueOf(view.context.colorFromAttribute(R.attr.white))
|
||||||
|
|
||||||
|
// object : View.OnClickListener is required to make it compile because otherwise it used invoke-customs
|
||||||
|
infoView.setOnClickListener(object : View.OnClickListener {
|
||||||
|
override fun onClick(v: View?) {
|
||||||
|
openBrowser(maciptvAPI.createAccountUrl)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
loginTextView.text = view.context.resources.getString(R.string.login_format).format(
|
||||||
|
maciptvAPI.name,
|
||||||
|
view.context.resources.getString(R.string.account))
|
||||||
|
|
||||||
|
loginView.setOnClickListener(object : View.OnClickListener {
|
||||||
|
override fun onClick(v: View?) {
|
||||||
|
val info = maciptvAPI.loginInfo()
|
||||||
|
if (info != null) {
|
||||||
|
showLoginInfo(activity, maciptvAPI, info)
|
||||||
|
} else {
|
||||||
|
addAccount(activity, maciptvAPI)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
12
MaciptvProvider/src/main/res/drawable/nginx.xml
Normal file
12
MaciptvProvider/src/main/res/drawable/nginx.xml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:name="vector"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="283"
|
||||||
|
android:viewportHeight="283">
|
||||||
|
<path
|
||||||
|
android:name="path"
|
||||||
|
android:pathData="M 253.41 62.61 L 154.22 5.34 C 150.42 3.146 146.108 1.991 141.72 1.991 C 137.332 1.991 133.02 3.146 129.22 5.34 L 30 62.61 C 26.202 64.807 23.049 67.966 20.858 71.768 C 18.668 75.57 17.516 79.882 17.52 84.27 L 17.52 198.8 C 17.516 203.188 18.668 207.5 20.858 211.302 C 23.049 215.104 26.202 218.263 30 220.46 L 129.19 277.72 C 132.99 279.914 137.302 281.069 141.69 281.069 C 146.078 281.069 150.39 279.914 154.19 277.72 L 253.38 220.46 C 257.183 218.266 260.343 215.109 262.539 211.307 C 264.735 207.505 265.891 203.191 265.89 198.8 L 265.89 84.27 C 265.894 79.882 264.742 75.57 262.552 71.768 C 260.361 67.966 257.208 64.807 253.41 62.61 Z M 203.28 185.33 Q 203.28 200.61 187.03 200.61 C 184.56 200.637 182.098 200.331 179.71 199.7 C 177.529 199.086 175.467 198.109 173.61 196.81 C 171.687 195.463 169.917 193.91 168.33 192.18 Q 165.9 189.52 163.45 186.76 L 106.86 119.16 L 106.86 187.16 Q 106.86 193.81 102.86 197.22 C 100.004 199.558 96.388 200.768 92.7 200.62 Q 86.3 200.62 82.44 197.18 Q 78.58 193.74 78.58 187.18 L 78.58 97.63 C 78.438 94.563 78.992 91.503 80.2 88.68 C 81.685 86.126 83.925 84.093 86.61 82.86 C 89.603 81.356 92.911 80.585 96.26 80.61 C 98.633 80.541 101.001 80.879 103.26 81.61 C 105.096 82.243 106.813 83.179 108.34 84.38 C 109.979 85.728 111.477 87.239 112.81 88.89 C 114.33 90.74 115.91 92.66 117.53 94.67 L 175.53 163.06 L 175.53 94.06 Q 175.53 87.34 179.24 83.97 Q 182.95 80.6 189.24 80.61 C 193.57 80.61 197 81.73 199.5 83.97 C 202 86.21 203.26 89.58 203.26 94.06 Z"
|
||||||
|
android:fillColor="#000"
|
||||||
|
android:strokeWidth="1" />
|
||||||
|
</vector>
|
17
MaciptvProvider/src/main/res/drawable/nginx_question.xml
Normal file
17
MaciptvProvider/src/main/res/drawable/nginx_question.xml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:name="vector"
|
||||||
|
android:width="35dp"
|
||||||
|
android:height="26dp"
|
||||||
|
android:viewportWidth="379"
|
||||||
|
android:viewportHeight="279">
|
||||||
|
<path
|
||||||
|
android:name="path"
|
||||||
|
android:pathData="M 235.89 60.62 L 136.7 3.35 C 132.9 1.156 128.588 0.001 124.2 0.001 C 119.812 0.001 115.5 1.156 111.7 3.35 L 12.48 60.62 C 8.682 62.817 5.529 65.976 3.338 69.778 C 1.148 73.58 -0.004 77.892 0 82.28 L 0 196.81 C -0.004 201.198 1.148 205.51 3.338 209.312 C 5.529 213.114 8.682 216.273 12.48 218.47 L 111.67 275.73 C 115.47 277.924 119.782 279.079 124.17 279.079 C 128.558 279.079 132.87 277.924 136.67 275.73 L 235.86 218.47 C 239.663 216.276 242.823 213.119 245.019 209.317 C 247.215 205.515 248.371 201.201 248.37 196.81 L 248.37 82.28 C 248.374 77.892 247.222 73.58 245.032 69.778 C 242.841 65.976 239.688 62.817 235.89 60.62 Z M 185.76 183.34 Q 185.76 198.62 169.51 198.62 C 167.04 198.647 164.578 198.341 162.19 197.71 C 160.009 197.096 157.947 196.119 156.09 194.82 C 154.167 193.473 152.397 191.92 150.81 190.19 Q 148.38 187.53 145.93 184.77 L 89.34 117.17 L 89.34 185.17 Q 89.34 191.82 85.34 195.23 C 82.484 197.568 78.868 198.778 75.18 198.63 Q 68.78 198.63 64.92 195.19 Q 61.06 191.75 61.06 185.19 L 61.06 95.64 C 60.918 92.573 61.472 89.513 62.68 86.69 C 64.165 84.136 66.405 82.103 69.09 80.87 C 72.083 79.366 75.391 78.595 78.74 78.62 C 81.113 78.551 83.481 78.889 85.74 79.62 C 87.576 80.253 89.293 81.189 90.82 82.39 C 92.459 83.738 93.957 85.249 95.29 86.9 C 96.81 88.75 98.39 90.67 100.01 92.68 L 158.01 161.07 L 158.01 92.07 Q 158.01 85.35 161.72 81.98 Q 165.43 78.61 171.72 78.62 C 176.05 78.62 179.48 79.74 181.98 81.98 C 184.48 84.22 185.74 87.59 185.74 92.07 Z"
|
||||||
|
android:fillColor="#000"
|
||||||
|
android:strokeWidth="1" />
|
||||||
|
<path
|
||||||
|
android:name="path_1"
|
||||||
|
android:pathData="M 312.84 143.37 C 320.84 128.98 336.13 120.49 345.04 107.75 C 354.48 94.4 349.18 69.45 322.48 69.45 C 304.98 69.45 296.39 82.7 292.77 93.67 L 265.94 82.39 C 273.29 60.39 293.27 41.39 322.37 41.39 C 346.69 41.39 363.37 52.47 371.85 66.34 C 379.1 78.25 383.34 100.51 372.16 117.07 C 359.74 135.39 347.83 140.98 341.41 152.79 C 338.83 157.55 337.79 160.65 337.79 175.98 L 307.87 175.98 C 307.77 167.9 306.53 154.75 312.84 143.37 Z M 343.17 217.37 C 343.175 222.063 341.584 226.62 338.661 230.291 C 335.737 233.962 331.651 236.533 327.077 237.579 C 322.502 238.625 317.705 238.086 313.477 236.05 C 309.249 234.015 305.835 230.601 303.8 226.373 C 301.764 222.145 301.225 217.348 302.271 212.773 C 303.317 208.199 305.888 204.113 309.559 201.189 C 313.23 198.266 317.787 196.675 322.48 196.68 C 327.963 196.698 333.221 198.888 337.097 202.767 C 340.972 206.646 343.157 211.907 343.17 217.39 Z"
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:strokeWidth="1" />
|
||||||
|
</vector>
|
95
MaciptvProvider/src/main/res/layout/nginx_settings.xml
Normal file
95
MaciptvProvider/src/main/res/layout/nginx_settings.xml
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/settings_root"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/nginx_login"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/nginx_login_imageview"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_gravity="start|center_vertical"
|
||||||
|
android:layout_marginEnd="20dp"
|
||||||
|
android:scaleType="centerInside" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<!-- <LinearLayout-->
|
||||||
|
<!-- android:layout_width="match_parent"-->
|
||||||
|
<!-- android:layout_height="match_parent"-->
|
||||||
|
<!-- android:orientation="horizontal">-->
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/main_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
<!-- </LinearLayout>-->
|
||||||
|
|
||||||
|
<!-- <TextView-->
|
||||||
|
<!-- android:id="@+id/sub_text"-->
|
||||||
|
<!-- android:layout_width="wrap_content"-->
|
||||||
|
<!-- android:layout_height="wrap_content"-->
|
||||||
|
<!-- android:text="@string/nginx_info_summary"-->
|
||||||
|
<!-- android:textSize="12sp" />-->
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/nginx_info"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/nginx_info_imageview"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_gravity="start|center_vertical"
|
||||||
|
android:layout_marginEnd="20dp"
|
||||||
|
android:scaleType="centerInside" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/info_main_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/info_sub_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
</LinearLayout>
|
5
MaciptvProvider/src/main/res/values-bp/strings.xml
Normal file
5
MaciptvProvider/src/main/res/values-bp/strings.xml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="nginx_info_title">O que é Nginx ?</string>
|
||||||
|
<string name="nginx_info_summary">Nginx é um software que pode ser usado para exibir arquivos de um servidor que você possui. Clique para ver um guia de configuração do Nginx</string>
|
||||||
|
</resources>
|
5
MaciptvProvider/src/main/res/values-cs/strings.xml
Normal file
5
MaciptvProvider/src/main/res/values-cs/strings.xml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="nginx_info_title">Co je to Nginx?</string>
|
||||||
|
<string name="nginx_info_summary">Nginx je software, který může být použit ke zobrazení souborů na serveru, který vlastníte. Klikněte pro zobrazení návodu k nastavení Nginx</string>
|
||||||
|
</resources>
|
5
MaciptvProvider/src/main/res/values-es/strings.xml
Normal file
5
MaciptvProvider/src/main/res/values-es/strings.xml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="nginx_info_title">¿Qué es Nginx?</string>
|
||||||
|
<string name="nginx_info_summary">Nginx es un software que se puede usar para mostrar archivos de un servidor de su propiedad. Pulse para ver una guía de configuración de Nginx</string>
|
||||||
|
</resources>
|
9
MaciptvProvider/src/main/res/values-fr/strings.xml
Normal file
9
MaciptvProvider/src/main/res/values-fr/strings.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="nginx_info_title">C\'est quoi Maciptv ?</string>
|
||||||
|
<string name="example_username">NomIPTV</string>
|
||||||
|
<string name="nginx_info_summary">Maciptv est un emulateur qui va vous permettre de profiter de vos comptes IPTV. Donner l\'url portail du fournisseur http:\/\/exemple.com\/c\/ et votre mac 00:1A:79:XX:XX:XX comme password123. Pour sélectionner/revenir au compte par défaut, créer un compte avec \'none\' à la place de \'127.0.0.1\' et pas besoin de renseigner les autres infos</string>
|
||||||
|
<string name="nginx_url_pref">Addresse du serveur IPTV</string>
|
||||||
|
<string name="example_password">00:1A:79:XX:XX</string>
|
||||||
|
<string name="example_ip">http:\/\/exemple\-portal.com:8080\/c\/</string>
|
||||||
|
</resources>
|
5
MaciptvProvider/src/main/res/values-in/strings.xml
Normal file
5
MaciptvProvider/src/main/res/values-in/strings.xml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="nginx_info_title">Apa itu Nginx ?</string>
|
||||||
|
<string name="nginx_info_summary">Nginx adalah sebuah software yang dapat digunakan untuk menampilkan file dari server yang anda miliki. Klik untuk melihat panduan pengaturan Nginx</string>
|
||||||
|
</resources>
|
5
MaciptvProvider/src/main/res/values-it/strings.xml
Normal file
5
MaciptvProvider/src/main/res/values-it/strings.xml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="nginx_info_title">Cos\'è Nginx?</string>
|
||||||
|
<string name="nginx_info_summary">Nginx è un software che può essere utilizzato per visualizzare i file da un server di proprietà. Fare clic per vedere la guida all\'installazione di Nginx</string>
|
||||||
|
</resources>
|
5
MaciptvProvider/src/main/res/values-nl/strings.xml
Normal file
5
MaciptvProvider/src/main/res/values-nl/strings.xml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="nginx_info_title">Wat is Nginx ?</string>
|
||||||
|
<string name="nginx_info_summary">Nginx is een software die kan worden gebruikt om bestanden weer te geven van een server die u bezit. Klik om een Nginx installatiegids te zien</string>
|
||||||
|
</resources>
|
5
MaciptvProvider/src/main/res/values-pl/strings.xml
Normal file
5
MaciptvProvider/src/main/res/values-pl/strings.xml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="nginx_info_title">Co to Nginx?</string>
|
||||||
|
<string name="nginx_info_summary">Nginx to oprogramowanie, które może być używane do wyświetlania plików z serwera, którego jesteś właścicielem. Kliknij, aby zobaczyć przewodnik konfiguracji Nginx</string>
|
||||||
|
</resources>
|
5
MaciptvProvider/src/main/res/values-ro/strings.xml
Normal file
5
MaciptvProvider/src/main/res/values-ro/strings.xml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="nginx_info_title">Ce este Nginx?</string>
|
||||||
|
<string name="nginx_info_summary">Nginx este un software care poate fi utilizat pentru a vizualiza fișiere de pe un server proprietar. Faceți clic pentru a vedea ghidul de instalare Nginx</string>
|
||||||
|
</resources>
|
5
MaciptvProvider/src/main/res/values-tr/strings.xml
Normal file
5
MaciptvProvider/src/main/res/values-tr/strings.xml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="nginx_info_title">Nginx nedir?</string>
|
||||||
|
<string name="nginx_info_summary">Nginx, sahip olduğunuz bir sunucudan dosyaları görüntülemek için kullanılabilecek bir yazılımdır. Nginx kurulum kılavuzunu görmek için tıklayın</string>
|
||||||
|
</resources>
|
5
MaciptvProvider/src/main/res/values-vi/strings.xml
Normal file
5
MaciptvProvider/src/main/res/values-vi/strings.xml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="nginx_info_title">Nginx là gì?</string>
|
||||||
|
<string name="nginx_info_summary">Nginx được dùng để hiển thị các tệp từ máy chủ mà bạn sở hữu. Chọn để xem hướng dẫn thiết lập Nginx</string>
|
||||||
|
</resources>
|
8
MaciptvProvider/src/main/res/values/strings.xml
Normal file
8
MaciptvProvider/src/main/res/values/strings.xml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="nginx_info_title">What is Maciptv ?</string>
|
||||||
|
<string name="example_password">00:1A:79:XX:XX</string>
|
||||||
|
<string name="example_ip">http:\/\/exemple\-portal.com:8080\/c\/</string>
|
||||||
|
<string name="example_username">MyCoolIPTVname</string>
|
||||||
|
<string name="nginx_info_summary">Maciptv is an extension that will allow you to enjoy your IPTV accounts. Give the provider\'s portal url http:\/\/exemple.com\/c\/ and your mac 00:1A:79:XX:XX as password123. To select/revert to the default account, create an account with \'none\' instead of \'127.0.0.1\' and no need to fill in the other info</string>
|
||||||
|
</resources>
|
Loading…
Add table
Add a link
Reference in a new issue