add MacIPTVProvider (tested lang : English, Arabic, French)
This commit is contained in:
parent
33d15586e9
commit
b29fed2c69
|
@ -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
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.lagradost"/>
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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…
Reference in New Issue