Files
xTeVe/ts/menu_ts.ts
T
nathan 3a3ba861dc
continuous-integration/drone/push Build encountered an error
Phase 5a: persistent websocket with request ids and reconnect
Client (ts/network_ts.ts): one WebSocket per page. Commands queue and go
out one at a time with a client-chosen id; the response is matched on the
echoed id (or to the in-flight request for older servers). 12 s timeout
per request, exponential-backoff reconnect (0.5 s to 10 s), the in-flight
request is retried after a reconnect, and log polls are de-duplicated so
they cannot pile up behind a stalled connection. The old global flag that
silently dropped any request made while another was in flight is gone.

Server (src/webserver.go): the /data/ handler now serves any number of
commands on one connection (it used to break out of its loop after the
first reply without closing the socket, leaving it open and deaf; the old
client papered over that by opening a new socket per request). Connection
closed on exit, request id echoed in the response.

Test: TestWSServesMultipleCommandsPerConnection.
2026-09-26 13:19:38 +10:00

2478 lines
69 KiB
TypeScript

class MainMenu {
DocumentID:string = "main-menu"
HTMLTag:string = "LI"
ImagePath:string = "img/"
createIMG(src):any {
var element = document.createElement("IMG")
element.setAttribute("src", this.ImagePath + src)
return element
}
createValue(value):any {
var element = document.createElement("P")
element.textContent = value
return element
}
}
class MainMenuItem extends MainMenu {
menuKey:string
value:string
imgSrc:string
headline:string
id:string
tableHeader:string[]
constructor(menuKey:string, value:string, image:string, headline:string) {
super()
this.menuKey = menuKey
this.value = value
this.imgSrc = image
this.headline = headline
}
createItem():void {
var item = document.createElement("LI")
item.setAttribute("onclick", "javascript: openThisMenu(this)")
item.setAttribute("id", this.id)
item.setAttribute("data-menu", this.menuKey)
var img = this.createIMG(this.imgSrc)
var value = this.createValue(this.value)
item.appendChild(img)
item.appendChild(value)
var doc = document.getElementById(this.DocumentID)
doc.appendChild(item)
switch(this.menuKey) {
case "playlist":
this.tableHeader = ["Playlist", "Tuner", "Last Update", "Availability %", "Type", "Streams", "group-title %", "tvg-id %", "Unique ID %"]
break
case "xmltv":
this.tableHeader = ["Guide", "Last Update", "Availability %", "Channels", "Programs"]
break
case "filter":
this.tableHeader = ["Filter Name", "Filter Type", "Filter"]
break
case "users":
this.tableHeader = ["Username", "Password", "WEB", "PMS", "M3U", "XML", "API"]
break
case "mapping":
this.tableHeader = ["BULK", "Ch. No.", "Logo", "Channel Name", "Playlist", "Group Title", "XMLTV File", "XMLTV ID", "Edit"]
break
}
//console.log(this.menuKey, this.tableHeader);
}
}
class Content {
DocumentID:string = "content"
TableID:string = "content_table"
DivID:string
headerClass:string = "content_table_header"
interactionID:string = "content-interaction"
createHeadline(value):any {
var element = document.createElement("H3")
element.textContent = value
return element
}
createHR():any {
var element = document.createElement("HR")
return element
}
createInteraction():any {
var element = document.createElement("DIV")
element.setAttribute("id", this.interactionID)
return element
}
createDIV():any {
var element = document.createElement("DIV")
element.id = this.DivID
return element
}
createTABLE():any {
var element = document.createElement("TABLE")
element.id = this.TableID
return element
}
createTableRow():any {
var element = document.createElement("TR")
element.className = this.headerClass
return element
}
createTableContent(menuKey:string):string[] {
var data = new Object()
var rows = new Array()
switch(menuKey) {
case "playlist":
var fileTypes = new Array("m3u", "hdhr")
fileTypes.forEach(fileType => {
data = SERVER["settings"]["files"][fileType]
var keys = getObjKeys(data)
keys.forEach(key => {
var tr = document.createElement("TR")
tr.id = key
tr.setAttribute('onclick', 'javascript: openPopUp("' + fileType + '", this)')
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
cell.value = data[key]["name"]
tr.appendChild(cell.createCell())
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
if (SERVER["settings"]["buffer"] != "-") {
cell.value = data[key]["tuner"]
} else {
cell.value = "-"
}
tr.appendChild(cell.createCell())
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
cell.value = data[key]["last.update"]
tr.appendChild(cell.createCell())
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
cell.value = data[key]["provider.availability"]
tr.appendChild(cell.createCell())
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
cell.value = data[key]["type"].toUpperCase();
tr.appendChild(cell.createCell())
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
cell.value = data[key]["compatibility"]["streams"]
tr.appendChild(cell.createCell())
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
cell.value = data[key]["compatibility"]["group.title"]
tr.appendChild(cell.createCell())
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
cell.value = data[key]["compatibility"]["tvg.id"]
tr.appendChild(cell.createCell())
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
cell.value = data[key]["compatibility"]["stream.id"]
tr.appendChild(cell.createCell())
rows.push(tr)
});
});
break
case "filter":
delete SERVER["settings"]["filter"][-1]
data = SERVER["settings"]["filter"]
var keys = getObjKeys(data)
keys.forEach(key => {
var tr = document.createElement("TR")
tr.id = key
tr.setAttribute('onclick', 'javascript: openPopUp("' + data[key]["type"] + '", this)')
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
cell.value = data[key]["name"]
tr.appendChild(cell.createCell())
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
switch (data[key]["type"]) {
case "custom-filter":
cell.value = "Custom"
break;
case "group-title":
cell.value = "Group"
break;
default:
break;
}
tr.appendChild(cell.createCell())
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
cell.value = data[key]["filter"]
tr.appendChild(cell.createCell())
rows.push(tr)
});
break
case "xmltv":
var fileTypes = new Array("xmltv")
fileTypes.forEach(fileType => {
data = SERVER["settings"]["files"][fileType]
var keys = getObjKeys(data)
keys.forEach(key => {
var tr = document.createElement("TR")
tr.id = key
tr.setAttribute('onclick', 'javascript: openPopUp("' + fileType + '", this)')
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
cell.value = data[key]["name"]
tr.appendChild(cell.createCell())
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
cell.value = data[key]["last.update"]
tr.appendChild(cell.createCell())
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
cell.value = data[key]["provider.availability"]
tr.appendChild(cell.createCell())
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
cell.value = data[key]["compatibility"]["xmltv.channels"]
tr.appendChild(cell.createCell())
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
cell.value = data[key]["compatibility"]["xmltv.programs"]
tr.appendChild(cell.createCell())
rows.push(tr)
});
});
break
case "users":
var fileTypes = new Array("users")
fileTypes.forEach(fileType => {
data = SERVER[fileType]
var keys = getObjKeys(data)
keys.forEach(key => {
var tr = document.createElement("TR")
tr.id = key
tr.setAttribute('onclick', 'javascript: openPopUp("' + fileType + '", this)')
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
cell.value = data[key]["data"]["username"]
tr.appendChild(cell.createCell())
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
cell.value = "******"
tr.appendChild(cell.createCell())
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
if (data[key]["data"]["authentication.web"] == true) {
cell.value = "✓"
} else {
cell.value = "-"
}
tr.appendChild(cell.createCell())
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
if (data[key]["data"]["authentication.pms"] == true) {
cell.value = "✓"
} else {
cell.value = "-"
}
tr.appendChild(cell.createCell())
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
if (data[key]["data"]["authentication.m3u"] == true) {
cell.value = "✓"
} else {
cell.value = "-"
}
tr.appendChild(cell.createCell())
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
if (data[key]["data"]["authentication.xml"] == true) {
cell.value = "✓"
} else {
cell.value = "-"
}
tr.appendChild(cell.createCell())
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
if (data[key]["data"]["authentication.api"] == true) {
cell.value = "✓"
} else {
cell.value = "-"
}
tr.appendChild(cell.createCell())
rows.push(tr)
});
});
break
case "mapping":
BULK_EDIT = false
createSearchObj()
checkUndo("epgMapping")
console.log("MAPPING")
data = SERVER["xepg"]["epgMapping"]
var keys = getObjKeys(data)
keys.forEach(key => {
var tr = document.createElement("TR")
tr.id = key
//tr.setAttribute('oncontextmenu', 'javascript: rightClick(this)')
switch (data[key]["x-active"]) {
case true:
tr.className = "activeEPG"
break;
case false:
tr.className = "notActiveEPG"
break;
}
// Bulk
var cell:Cell = new Cell()
cell.child = true
cell.childType = "BULK"
cell.value = false
tr.appendChild(cell.createCell())
// Kanalnummer
var cell:Cell = new Cell()
cell.child = true
cell.childType = "INPUTCHANNEL"
cell.value = data[key]["x-channelID"]
//td.setAttribute('onclick', 'javascript: changeChannelNumber("' + key + '", this)')
tr.appendChild(cell.createCell())
// Logo
var cell:Cell = new Cell()
cell.child = true
cell.childType = "IMG"
cell.imageURL = data[key]["tvg-logo"]
tr.appendChild(cell.createCell())
// Kanalname
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
cell.className = data[key]["x-category"]
cell.value = data[key]["x-name"]
tr.appendChild(cell.createCell())
// Playlist
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
//cell.value = data[key]["_file.m3u.name"]
cell.value = getValueFromProviderFile(data[key]["_file.m3u.id"], "m3u", "name")
tr.appendChild(cell.createCell())
// Gruppe (group-title)
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
cell.value = data[key]["x-group-title"]
tr.appendChild(cell.createCell())
// XMLTV Datei
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
if (data[key]["x-xmltv-file"] != "-") {
cell.value = getValueFromProviderFile(data[key]["x-xmltv-file"], "xmltv", "name")
} else {
cell.value = data[key]["x-xmltv-file"]
}
tr.appendChild(cell.createCell())
// XMLTV Kanal
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
//var value = str.substring(1, 4);
var value = data[key]["x-mapping"]
if (value.length > 20) {
value = data[key]["x-mapping"].substring(0, 20) + "..."
}
cell.value = value
tr.appendChild(cell.createCell())
var cell:Cell = new Cell()
cell.child = true
cell.childType = "EDIT"
cell.value = "Edit"
var editTd = cell.createCell()
var editButton = (editTd.firstChild as HTMLInputElement)
editButton.setAttribute('onclick', 'javascript: openPopUp("mapping", this)')
editButton.setAttribute("id", key)
editButton.setAttribute("aria-label", "Edit " + data[key]["x-name"])
tr.appendChild(editTd)
rows.push(tr)
});
break
case "settings":
alert()
break
default:
console.log("Table content (menuKey):", menuKey);
break
}
return rows
}
return
}
class Cell {
child:Boolean
childType:string
value:any
className:string
tdClassName:string
imageURL:string
onclick:boolean
onclickFunktion:string
createCell():any {
var td = document.createElement("TD")
if (this.child == true) {
var element:any
switch(this.childType){
case "P":
element = document.createElement(this.childType);
element.textContent = this.value
element.className = this.className
break
case "INPUT":
element = document.createElement(this.childType);
(element as HTMLInputElement).value = this.value;
(element as HTMLInputElement).type = "text";
break
case "INPUTCHANNEL":
element = document.createElement("INPUT");
(element as HTMLInputElement).setAttribute("onchange", "javscript: changeChannelNumber(this)");
(element as HTMLInputElement).value = this.value;
(element as HTMLInputElement).type = "text";
break
case "BULK":
element = document.createElement("INPUT");
(element as HTMLInputElement).checked = this.value;
(element as HTMLInputElement).type = "checkbox";
(element as HTMLInputElement).className = "bulk hideBulk";
(element as HTMLInputElement).addEventListener("click", function(event) {
scheduleChannelRangeSelection((element as HTMLInputElement), (event as MouseEvent))
})
break
case "BULK_HEAD":
element = document.createElement("INPUT");
(element as HTMLInputElement).checked = this.value;
(element as HTMLInputElement).type = "checkbox";
(element as HTMLInputElement).className = "bulk hideBulk";
(element as HTMLInputElement).setAttribute("onclick", "javascript: selectAllChannels()")
break
case "IMG":
element = document.createElement(this.childType);
element.setAttribute("src", this.imageURL)
if (this.imageURL != "") {
element.setAttribute("onerror", "javascript: this.onerror=null;this.src=''" )
//onerror="this.onerror=null;this.src='missing.gif';"
}
break
case "EDIT":
element = document.createElement("INPUT");
(element as HTMLInputElement).type = "button";
(element as HTMLInputElement).value = this.value;
(element as HTMLInputElement).className = "mapping-edit-button";
break
}
td.appendChild(element)
} else {
td.textContent = this.value
}
if (this.onclick == true) {
td.setAttribute("onclick", this.onclickFunktion)
td.className = "pointer"
}
if (this.tdClassName != undefined) {
td.className = this.tdClassName
}
return td
}
return
}
class ShowContent extends Content {
menuID:number
constructor(menuID:number) {
super()
this.menuID = menuID
}
createInput(type:string, name:string, value:string,):any {
var input = document.createElement("INPUT")
input.setAttribute("type", type)
input.setAttribute("name", name)
input.setAttribute("value", value)
return input
}
show():void {
COLUMN_TO_SORT = -1
// Alten Inhalt löschen
var doc = document.getElementById(this.DocumentID)
doc.innerHTML = ""
showPreview(false)
// Überschrift
var headline:string[] = menuItems[this.menuID].headline
var menuKey = menuItems[this.menuID].menuKey
var h = this.createHeadline(headline)
doc.appendChild(h)
var hr = this.createHR()
doc.appendChild(hr)
// Interaktion
var div =this.createInteraction()
doc.appendChild(div)
var interaction = document.getElementById(this.interactionID)
switch (menuKey) {
case "playlist":
var input = this.createInput("button", menuKey, "New")
input.setAttribute("id", "-")
input.setAttribute("onclick", 'javascript: openPopUp("playlist")')
interaction.appendChild(input)
break;
case "filter":
var input = this.createInput("button", menuKey, "New")
input.setAttribute("id", -1)
input.setAttribute("onclick", 'javascript: openPopUp("filter", this)')
interaction.appendChild(input)
break;
case "xmltv":
var input = this.createInput("button", menuKey, "New")
input.setAttribute("id", "xmltv")
input.setAttribute("onclick", 'javascript: openPopUp("xmltv")')
interaction.appendChild(input)
break;
case "users":
var input = this.createInput("button", menuKey, "New")
input.setAttribute("id", "users")
input.setAttribute("onclick", 'javascript: openPopUp("users")')
interaction.appendChild(input)
break;
case "mapping":
showElement("loading", true)
var input = this.createInput("button", menuKey, "Save")
input.setAttribute("onclick", 'javascript: savePopupData("mapping", "", "")')
interaction.appendChild(input)
var input = this.createInput("button", menuKey, "Bulk Edit")
input.setAttribute("onclick", 'javascript: bulkEdit()')
interaction.appendChild(input)
var input = this.createInput("search", "search", "")
input.setAttribute("id", "searchMapping")
input.setAttribute("placeholder", "Search")
input.className = "search"
input.setAttribute("oninput", 'javascript: searchInMapping()')
interaction.appendChild(input)
break;
case "settings":
var input = this.createInput("button", menuKey, "Save")
input.setAttribute("onclick", 'javascript: saveSettings();')
interaction.appendChild(input)
var input = this.createInput("button", menuKey, "Backup")
input.setAttribute("onclick", 'javascript: backup();')
interaction.appendChild(input)
var input = this.createInput("button", menuKey, "Restore")
input.setAttribute("onclick", 'javascript: restore();')
interaction.appendChild(input)
var wrapper = document.createElement("DIV")
wrapper.setAttribute("id", "box-wrapper")
doc.appendChild(wrapper)
this.DivID = "content_settings"
var settings = this.createDIV()
wrapper.appendChild(settings)
showSettings()
return
break
case "log":
var input = this.createInput("button", menuKey, "Reset Logs")
input.setAttribute("onclick", 'javascript: resetLogs();')
interaction.appendChild(input)
var wrapper = document.createElement("DIV")
wrapper.setAttribute("id", "box-wrapper")
doc.appendChild(wrapper)
this.DivID = "content_log"
var logs = this.createDIV()
wrapper.appendChild(logs)
showLogs(true)
return
break
case "logout":
location.reload()
document.cookie = "Token= ; expires = Thu, 01 Jan 1970 00:00:00 GMT"
break
default:
console.log("Show content (menuKey):", menuKey);
break;
}
// Tabelle erstellen (falls benötigt)
var tableHeader:string[] = menuItems[this.menuID].tableHeader
if (tableHeader.length > 0) {
var wrapper = document.createElement("DIV")
doc.appendChild(wrapper)
wrapper.setAttribute("id", "box-wrapper")
var table = this.createTABLE()
wrapper.appendChild(table)
var header = this.createTableRow()
table.appendChild(header)
// Kopfzeile der Tablle
tableHeader.forEach(element => {
var cell:Cell = new Cell()
cell.child = true
cell.childType = "P"
cell.value = element
if (element == "BULK") {
cell.childType = "BULK_HEAD";
cell.value = false
}
if (menuKey == "mapping") {
if (element == "Ch. No.") {
cell.onclick = true
cell.onclickFunktion = "javascript: sortTable(1);"
cell.tdClassName = "sortThis"
}
if (element == "Channel Name") {
cell.onclick = true
cell.onclickFunktion = "javascript: sortTable(3);"
}
if (element == "Playlist") {
cell.onclick = true
cell.onclickFunktion = "javascript: sortTable(4);"
}
if (element == "Group Title") {
cell.onclick = true
cell.onclickFunktion = "javascript: sortTable(5);"
}
}
header.appendChild(cell.createCell())
});
table.appendChild(header)
// Inhalt der Tabelle
var rows:any = this.createTableContent(menuKey)
rows.forEach(tr => {
table.appendChild(tr)
});
}
switch (menuKey) {
case "mapping":
sortTable(1)
break;
case "filter":
showPreview(true)
sortTable(0)
break
default:
COLUMN_TO_SORT = -1
sortTable(0)
break;
}
showElement("loading", false)
}
}
var SHELL_LAYOUT_READY:boolean = false
function setLayoutMenuState(open:boolean) {
if (document.body == null) {
return
}
if (open == true) {
document.body.classList.add("menu-open")
} else {
document.body.classList.remove("menu-open")
}
}
function toggleLayoutMenu() {
if (document.body == null) {
return
}
var isOpen:boolean = document.body.classList.contains("menu-open")
setLayoutMenuState(!isOpen)
}
function closeLayoutMenuIfMobile() {
if (window.innerWidth <= 900) {
setLayoutMenuState(false)
}
}
function setActiveMenu(menuID:string) {
ACTIVE_MENU_ID = menuID.toString()
var menu = document.getElementById("main-menu")
if (menu == null) {
return
}
var items = menu.getElementsByTagName("LI")
for (var i = 0; i < items.length; i++) {
items[i].classList.remove("menu-active")
}
var activeItem = document.getElementById(ACTIVE_MENU_ID)
var activeMenuKey:string = ""
if (activeItem != null) {
activeItem.classList.add("menu-active")
var menuKeyValue = activeItem.getAttribute("data-menu")
if (menuKeyValue != null) {
activeMenuKey = menuKeyValue
}
}
if (document.body != null) {
if (activeMenuKey.length > 0) {
document.body.setAttribute("data-active-menu", activeMenuKey)
} else {
document.body.removeAttribute("data-active-menu")
}
if (activeMenuKey == "log") {
document.body.classList.add("menu-log-focus")
} else {
document.body.classList.remove("menu-log-focus")
}
}
}
function renderStatusCards() {
var wrapper = document.getElementById("status-cards")
if (wrapper == null || SERVER.hasOwnProperty("clientInfo") == false) {
return
}
var info = SERVER["clientInfo"]
var errors:number = parseInt(info["errors"], 10)
var warnings:number = parseInt(info["warnings"], 10)
var cards:any[] = [
{label: "Streams", value: info["streams"], tone: "ok"},
{label: "EPG Source", value: info["epgSource"], tone: "neutral"},
{label: "XEPG Channels", value: info["xepg"], tone: "ok"},
{label: "Errors", value: info["errors"], tone: errors > 0 ? "error" : "ok"},
{label: "Warnings", value: info["warnings"], tone: warnings > 0 ? "warn" : "ok"},
{label: "DVR", value: info["DVR"], tone: "neutral"},
]
wrapper.innerHTML = ""
cards.forEach(card => {
var box = document.createElement("DIV")
box.className = "status-card status-card-" + card.tone
var label = document.createElement("P")
label.className = "status-card-label"
label.innerText = card.label
var value = document.createElement("P")
value.className = "status-card-value"
if (card.value == undefined || card.value == "") {
value.innerText = "-"
} else {
value.innerText = card.value
}
box.appendChild(label)
box.appendChild(value)
wrapper.appendChild(box)
});
}
function initShellLayout() {
if (SHELL_LAYOUT_READY == true) {
return
}
var toggle = document.getElementById("menu-toggle")
if (toggle != null) {
toggle.onclick = function() {
toggleLayoutMenu()
}
}
var overlay = document.getElementById("layout-overlay")
if (overlay != null) {
overlay.onclick = function() {
setLayoutMenuState(false)
}
}
document.addEventListener("keydown", function(event) {
if (event.key == "Escape") {
setLayoutMenuState(false)
showElement("popup", false)
return
}
if (event.key == "/") {
var target = event.target as HTMLElement
var onInput = target.tagName == "INPUT" || target.tagName == "TEXTAREA" || target.tagName == "SELECT"
if (onInput == true) {
return
}
var search = document.getElementById("searchMapping")
if (search != null) {
event.preventDefault();
(search as HTMLInputElement).focus()
}
}
});
setConnectionState("idle")
SHELL_LAYOUT_READY = true
}
function shouldPollLogs():boolean {
if (document.hidden == true) {
return false
}
if (document.getElementById("content_log") == null) {
return false
}
if (ACTIVE_MENU_ID.length < 1) {
return false
}
var activeItem = document.getElementById(ACTIVE_MENU_ID)
if (activeItem == null) {
return false
}
return activeItem.getAttribute("data-menu") == "log"
}
function PageReady() {
initShellLayout()
var server:Server = new Server("getServerConfig")
server.request(new Object())
var bootstrapAttempts:number = 0
var maxBootstrapAttempts:number = 5
var bootstrapTimer:number = window.setInterval(function() {
if (SERVER.hasOwnProperty("clientInfo") == true) {
window.clearInterval(bootstrapTimer)
return
}
if (WS.hasQueued("getServerConfig")) {
return
}
bootstrapAttempts++
var retryServer:Server = new Server("getServerConfig")
retryServer.request(new Object())
if (bootstrapAttempts >= maxBootstrapAttempts) {
window.clearInterval(bootstrapTimer)
}
}, 3000)
window.addEventListener("resize", function(){
if (window.innerWidth > 900) {
setLayoutMenuState(false)
}
calculateWrapperHeight();
}, true);
setInterval(function(){
if (shouldPollLogs() == false) {
return
}
updateLog()
}, 10000);
return
}
function isClientInfoHttpURL(value:string):boolean {
return /^https?:\/\//i.test(value)
}
function setClientInfoValue(key:string, value:any) {
var element = document.getElementById(key)
if (element == null) {
return
}
var textValue = ""
if (value != undefined && value != null) {
textValue = String(value)
}
if ((key == "m3u-url" || key == "xepg-url") && isClientInfoHttpURL(textValue)) {
element.innerHTML = ""
var anchor = document.createElement("a")
anchor.href = textValue
anchor.target = "_blank"
anchor.rel = "noopener noreferrer"
anchor.textContent = textValue
element.appendChild(anchor)
return
}
element.textContent = textValue
}
function createLayout() {
// Client Info
var obj = SERVER["clientInfo"]
var keys = getObjKeys(obj);
for (var i = 0; i < keys.length; i++) {
setClientInfoValue(keys[i], obj[keys[i]])
}
renderStatusCards()
if (!document.getElementById("main-menu")) {
return
}
// Menü erstellen
document.getElementById("main-menu").innerHTML = ""
for (let i = 0; i < menuItems.length; i++) {
menuItems[i].id = i
switch (menuItems[i]["menuKey"]) {
case "users":
case "logout":
if (SERVER["settings"]["authentication.web"] == true) {
menuItems[i].createItem()
}
break
case "mapping":
case "xmltv":
if (SERVER["clientInfo"]["epgSource"] == "XEPG") {
menuItems[i].createItem()
}
break
default:
menuItems[i].createItem()
break
}
}
if (ACTIVE_MENU_ID.length > 0 && document.getElementById(ACTIVE_MENU_ID)) {
setActiveMenu(ACTIVE_MENU_ID)
}
var content = document.getElementById("content")
var menu = document.getElementById("main-menu")
if (ACTIVE_MENU_ID.length == 0 && content != null && menu != null) {
if (content.innerHTML.replace(/\\s/g, "").length == 0) {
var firstItem = menu.getElementsByTagName("LI")[0] as HTMLElement
if (firstItem != undefined) {
firstItem.click()
}
}
}
return
}
function openThisMenu(element) {
var id = element.id
var content:ShowContent = new ShowContent(id)
setActiveMenu(id)
content.show()
closeLayoutMenuIfMobile()
calculateWrapperHeight()
return
}
class PopupWindow {
DocumentID:string = "popup-custom"
InteractionID:string = "interaction"
doc = document.getElementById(this.DocumentID)
createTitle(title:string):any {
var td = document.createElement("TD")
td.className = "left"
td.innerHTML = title + ":"
return td
}
createContent(element):any {
var td = document.createElement("TD")
td.appendChild(element)
return td
}
createInteraction():any {
var div = document.createElement("div")
div.setAttribute("id", "popup-interaction")
div.className = "interaction"
this.doc.appendChild(div)
}
}
class PopupContent extends PopupWindow{
table = document.createElement("TABLE")
createHeadline(headline):void {
this.doc.innerHTML = ""
var element = document.createElement("H3")
element.innerHTML = headline.toUpperCase()
this.doc.appendChild(element)
// Tabelle erstellen
this.table = document.createElement("TABLE")
this.doc.appendChild(this.table)
}
appendRow(title:string, element:any):void {
var tr = document.createElement("TR")
// Bezeichnung
if (title.length != 0) {
tr.appendChild(this.createTitle(title))
}
// Content
tr.appendChild(this.createContent(element))
this.table.appendChild(tr)
}
createInput(type:string, name:string, value:string):any {
var input = document.createElement("INPUT")
if (value == undefined) {
value = ""
}
input.setAttribute("type", type)
input.setAttribute("name", name)
input.setAttribute("value", value)
return input
}
createCheckbox(name:string):any {
var input = document.createElement("INPUT")
input.setAttribute("type", "checkbox")
input.setAttribute("name", name)
return input
}
createSelect(text:string[], values:string[], set:string, dbKey:string):any {
var select = document.createElement("SELECT")
select.setAttribute("name", dbKey)
for (let i = 0; i < text.length; i++) {
var option = document.createElement("OPTION")
option.setAttribute("value", values[i])
option.innerText = text[i]
select.appendChild(option)
}
if(set != "") {
(select as HTMLSelectElement).value = set
}
if (set == undefined) {
(select as HTMLSelectElement).value = values[0]
}
return select
}
selectOption(select:any, value:string):any {
//select.selectedOptions = value
var s:HTMLSelectElement = (select as HTMLSelectElement)
s.options[s.selectedIndex].value = value
return select
}
// isHTML must only be true for static text written in the source; never for data.
description(value:string, isHTML:boolean = false):any {
var tr = document.createElement("TR")
var td = document.createElement("TD")
var span = document.createElement("PRE")
if (isHTML) {
span.innerHTML = value
} else {
span.textContent = value
}
tr.appendChild(td)
tr.appendChild(this.createContent(span))
this.table.appendChild(tr)
}
// Interaktion
addInteraction(element:any) {
var interaction = document.getElementById("popup-interaction")
interaction.appendChild(element)
}
}
function openPopUp(dataType, element) {
var data:object = new Object();
var id:any
switch (element) {
case undefined:
switch (dataType) {
case "group-title":
if (id == undefined) {
id = -1
}
data = getLocalData("filter", id)
data["type"] = "group-title"
break;
case "custom-filter":
if (id == undefined) {
id = -1
}
data = getLocalData("filter", id)
data["type"] = "custom-filter"
break;
default:
data["id.provider"] = "-"
data["type"] = dataType
id = "-"
break;
}
break
default:
id = element.id
data = getLocalData(dataType, id)
break;
}
var content:PopupContent = new PopupContent()
switch (dataType) {
case "playlist":
content.createHeadline("Playlist type")
// Type
var text:string[] = ["M3U", "HDHomeRun"]
var values:string[] = ["javascript: openPopUp('m3u')", "javascript: openPopUp('hdhr')"]
var select = content.createSelect(text, values, "", "type")
select.setAttribute("id", "type")
select.setAttribute("onchange", 'javascript: changeButtonAction(this, "next", "onclick")') // changeButtonAction
content.appendRow("Type", select)
// Interaktion
content.createInteraction()
// Abbrechen
var input = content.createInput("button", "cancel", "Cancel")
input.setAttribute("onclick", 'javascript: showElement("popup", false);')
content.addInteraction(input)
// Weiter
var input = content.createInput("button", "next", "Next")
input.setAttribute("onclick", 'javascript: openPopUp("m3u")')
input.setAttribute("id", 'next')
content.addInteraction(input)
break
case "m3u":
content.createHeadline(dataType)
// Name
var dbKey:string = "name"
var input = content.createInput("text", dbKey, data[dbKey])
input.setAttribute("placeholder", "Playlist name")
content.appendRow("Name", input)
// Beschreibung
var dbKey:string = "description"
var input = content.createInput("text", dbKey, data[dbKey])
input.setAttribute("placeholder", "Description")
content.appendRow("Description", input)
// URL
var dbKey:string = "file.source"
var input = content.createInput("text", dbKey, data[dbKey])
input.setAttribute("placeholder", "File path or URL of the M3U")
content.appendRow("M3U File", input)
// Tuner
if (SERVER["settings"]["buffer"] != "-") {
var text:string[] = new Array()
var values:string[] = new Array()
for (var i = 1; i <= 100; i++) {
text.push(i.toString())
values.push(i.toString())
}
var dbKey:string = "tuner"
var select = content.createSelect(text, values, data[dbKey], dbKey)
select.setAttribute("onfocus", "javascript: return;")
content.appendRow("Tuner / Streams", select)
} else {
var dbKey:string = "tuner"
if (data[dbKey] == undefined) {
data[dbKey] = 1
}
var input = content.createInput("text", dbKey, data[dbKey])
input.setAttribute("readonly", "true")
input.className = "notAvailable"
content.appendRow("Tuner / Streams", input)
}
content.description("Number of parallel connections that can be established to the provider. <br>Only available with activated buffer.<br>New settings will only be applied after quitting all streams.", true)
// Interaktion
content.createInteraction()
// Löschen
if (data["id.provider"]!= "-") {
var input = content.createInput("button", "delete", "Delete")
input.className = "delete"
input.setAttribute('onclick', 'javascript: savePopupData("m3u", "' + id + '", true, 0)')
content.addInteraction(input)
} else {
var input = content.createInput("button", "back", "Back")
input.setAttribute("onclick", 'javascript: openPopUp("playlist")')
content.addInteraction(input)
}
// Abbrechen
var input = content.createInput("button", "cancel", "Cancel")
input.setAttribute("onclick", 'javascript: showElement("popup", false);')
content.addInteraction(input)
// Aktualisieren
if (data["id.provider"]!= "-") {
var input = content.createInput("button", "update", "Update")
input.setAttribute('onclick', 'javascript: savePopupData("m3u", "' + id + '", false, 1)')
content.addInteraction(input)
}
// Speichern
var input = content.createInput("button", "save", "Save")
input.setAttribute('onclick', 'javascript: savePopupData("m3u", "' + id + '", false, 0)')
content.addInteraction(input)
break
case "hdhr":
content.createHeadline(dataType)
// Name
var dbKey:string = "name"
var input = content.createInput("text", dbKey, data[dbKey])
input.setAttribute("placeholder", "Playlist name")
content.appendRow("Name", input)
// Beschreibung
var dbKey:string = "description"
var input = content.createInput("text", dbKey, data[dbKey])
input.setAttribute("placeholder", "Description")
content.appendRow("Description", input)
// URL
var dbKey:string = "file.source"
var input = content.createInput("text", dbKey, data[dbKey])
input.setAttribute("placeholder", "IP address and port (192.168.1.10:5004)")
content.appendRow("HDHomeRun IP", input)
// Tuner
if (SERVER["settings"]["buffer"] != "-") {
var text:string[] = new Array()
var values:string[] = new Array()
for (var i = 1; i <= 100; i++) {
text.push(i.toString())
values.push(i.toString())
}
var dbKey:string = "tuner"
var select = content.createSelect(text, values, data[dbKey], dbKey)
select.setAttribute("onfocus", "javascript: return;")
content.appendRow("Tuner / Streams", select)
} else {
var dbKey:string = "tuner"
if (data[dbKey] == undefined) {
data[dbKey] = 1
}
var input = content.createInput("text", dbKey, data[dbKey])
input.setAttribute("readonly", "true")
input.className = "notAvailable"
content.appendRow("Tuner / Streams", input)
}
content.description("Number of parallel connections that can be established to the provider. <br>Only available with activated buffer.<br>New settings will only be applied after quitting all streams.", true)
// Interaktion
content.createInteraction()
// Löschen
if (data["id.provider"]!= "-") {
var input = content.createInput("button", "delete", "Delete")
input.setAttribute('onclick', 'javascript: savePopupData("hdhr", "' + id + '", true, 0)')
input.className = "delete"
content.addInteraction(input)
} else {
var input = content.createInput("button", "back", "Back")
input.setAttribute("onclick", 'javascript: openPopUp("playlist")')
content.addInteraction(input)
}
// Abbrechen
var input = content.createInput("button", "cancel", "Cancel")
input.setAttribute("onclick", 'javascript: showElement("popup", false);')
content.addInteraction(input)
// Aktualisieren
if (data["id.provider"]!= "-") {
var input = content.createInput("button", "update", "Update")
input.setAttribute('onclick', 'javascript: savePopupData("hdhr", "' + id + '", false, 1)')
content.addInteraction(input)
}
// Speichern
var input = content.createInput("button", "save", "Save")
input.setAttribute('onclick', 'javascript: savePopupData("hdhr", "' + id + '", false, 0)')
content.addInteraction(input)
break
case "filter":
content.createHeadline(dataType)
// Type
var dbKey:string = "type"
var text:string[] = ["M3U: " + "Group Title", "xTeVe: " + "Custom Filter"]
var values:string[] = ["javascript: openPopUp('group-title')", "javascript: openPopUp('custom-filter')"]
var select = content.createSelect(text, values, "javascript: openPopUp('group-title')", dbKey)
select.setAttribute("id", id)
select.setAttribute("onchange", 'javascript: changeButtonAction(this, "next", "onclick");') // changeButtonAction
content.appendRow("Type", select)
// Interaktion
content.createInteraction()
// Abbrechen
var input = content.createInput("button", "cancel", "Cancel")
input.setAttribute("onclick", 'javascript: showElement("popup", false);')
content.addInteraction(input)
// Weiter
var input = content.createInput("button", "next", "Next")
input.setAttribute("onclick", 'javascript: openPopUp("group-title")')
input.setAttribute("id", 'next')
content.addInteraction(input)
break
case "custom-filter":
case "group-title":
switch (dataType) {
case "custom-filter":
content.createHeadline("Custom")
break;
case "group-title":
content.createHeadline("Group")
break;
}
// Name
var dbKey:string = "name"
var input = content.createInput("text", dbKey, data[dbKey])
input.setAttribute("placeholder", "Filter name")
content.appendRow("Filter Name", input)
// Beschreibung
var dbKey:string = "description"
var input = content.createInput("text", dbKey, data[dbKey])
input.setAttribute("placeholder", "Description")
content.appendRow("Description", input)
// Typ
var dbKey:string = "type"
var input = content.createInput("hidden", dbKey, data[dbKey])
content.appendRow("", input)
var filterType = data[dbKey]
switch (filterType) {
case "custom-filter":
// Groß- Kleinschreibung beachten
var dbKey:string = "caseSensitive"
var input = content.createCheckbox(dbKey)
input.checked = data[dbKey]
content.appendRow("Case Sensitive", input)
// Filterregel (Benutzerdefiniert)
var dbKey:string = "filter"
var input = content.createInput("text", dbKey, data[dbKey])
input.setAttribute("placeholder", "Sport {HD} !{ES,IT}")
content.appendRow("Filter Rule", input)
break;
case "group-title":
//alert(dbKey + " " + filterType)
// Filter basierend auf den Gruppen in der M3U
var dbKey:string = "filter"
var groupsM3U = getLocalData("m3uGroups", "")
var text:string[] = groupsM3U["text"]
var values:string[] = groupsM3U["value"]
var select = content.createSelect(text, values, data[dbKey], dbKey)
select.setAttribute("onchange", "javascript: this.className = 'changed'")
content.appendRow("Group Title", select)
content.description("Select a M3U group. (Counter)<br>Changing the group title in the M3U invalidates the filter.", true)
// Groß- Kleinschreibung beachten
var dbKey:string = "caseSensitive"
var input = content.createCheckbox(dbKey)
input.checked = data[dbKey]
content.appendRow("Case Sensitive", input)
var dbKey:string = "include"
var input = content.createInput("text", dbKey, data[dbKey])
input.setAttribute("placeholder", "FHD,UHD")
content.appendRow("Include", input)
content.description("Channel name must include.<br>(Comma separated) Comma means or", true)
var dbKey:string = "exclude"
var input = content.createInput("text", dbKey, data[dbKey])
input.setAttribute("placeholder", "ES,IT")
content.appendRow("Exclude", input)
content.description("Channel name must not contain.<br>(Comma separated) Comma means or", true)
break
default:
break;
}
// Interaktion
content.createInteraction()
// Löschen
var input = content.createInput("button", "delete", "Delete")
input.setAttribute('onclick', 'javascript: savePopupData("filter", "' + id + '", true, 0)')
input.className = "delete"
content.addInteraction(input)
// Abbrechen
var input = content.createInput("button", "cancel", "Cancel")
input.setAttribute("onclick", 'javascript: showElement("popup", false);')
content.addInteraction(input)
// Speichern
var input = content.createInput("button", "save", "Save")
input.setAttribute('onclick', 'javascript: savePopupData("filter", "' + id + '", false, 0)')
content.addInteraction(input)
break
case "xmltv":
content.createHeadline(dataType)
// Name
var dbKey:string = "name"
var input = content.createInput("text", dbKey, data[dbKey])
input.setAttribute("placeholder", "Guide name")
content.appendRow("Name", input)
// Beschreibung
var dbKey:string = "description"
var input = content.createInput("text", dbKey, data[dbKey])
input.setAttribute("placeholder", "Description")
content.appendRow("Description", input)
// URL
var dbKey:string = "file.source"
var input = content.createInput("text", dbKey, data[dbKey])
input.setAttribute("placeholder", "File path or URL of the XMLTV")
content.appendRow("XMLTV File", input)
// Interaktion
content.createInteraction()
// Löschen
if (data["id.provider"]!= "-") {
var input = content.createInput("button", "delete", "Delete")
input.setAttribute('onclick', 'javascript: savePopupData("xmltv", "' + id + '", true, 0)')
input.className = "delete"
content.addInteraction(input)
}
// Abbrechen
var input = content.createInput("button", "cancel", "Cancel")
input.setAttribute("onclick", 'javascript: showElement("popup", false);')
content.addInteraction(input)
// Aktualisieren
if (data["id.provider"]!= "-") {
var input = content.createInput("button", "update", "Update")
input.setAttribute('onclick', 'javascript: savePopupData("xmltv", "' + id + '", false, 1)')
content.addInteraction(input)
}
// Speichern
var input = content.createInput("button", "save", "Save")
input.setAttribute('onclick', 'javascript: savePopupData("xmltv", "' + id + '", false, 0)')
content.addInteraction(input)
break
case "users":
content.createHeadline("Users")
// Benutzername
var dbKey:string = "username"
var input = content.createInput("text", dbKey, data[dbKey])
input.setAttribute("placeholder", "Username")
content.appendRow("Username", input)
// Neues Passwort
var dbKey:string = "password"
var input = content.createInput("password", dbKey, "")
input.setAttribute("placeholder", "Password")
content.appendRow("Password", input)
// Bestätigung
var dbKey:string = "confirm"
var input = content.createInput("password", dbKey, "")
input.setAttribute("placeholder", "Password confirm")
content.appendRow("Confirm", input)
// Berechtigung WEB
var dbKey:string = "authentication.web"
var input = content.createCheckbox(dbKey)
input.checked = data[dbKey]
if (data["defaultUser"] == true) {
input.setAttribute("onclick", "javascript: return false")
}
content.appendRow("Web Access", input)
// Berechtigung PMS
var dbKey:string = "authentication.pms"
var input = content.createCheckbox(dbKey)
input.checked = data[dbKey]
content.appendRow("PMS Access", input)
// Berechtigung M3U
var dbKey:string = "authentication.m3u"
var input = content.createCheckbox(dbKey)
input.checked = data[dbKey]
content.appendRow("M3U Access", input)
// Berechtigung XML
var dbKey:string = "authentication.xml"
var input = content.createCheckbox(dbKey)
input.checked = data[dbKey]
content.appendRow("XML Access", input)
// Berechtigung API
var dbKey:string = "authentication.api"
var input = content.createCheckbox(dbKey)
input.checked = data[dbKey]
content.appendRow("API Access", input)
// Interaktion
content.createInteraction()
// Löschen
if (data["defaultUser"]!= true && id != "-") {
var input = content.createInput("button", "delete", "Delete")
input.className = "delete"
input.setAttribute('onclick', 'javascript: savePopupData("' + dataType + '", "' + id + '", true, 0)')
content.addInteraction(input)
}
// Abbrechen
var input = content.createInput("button", "cancel", "Cancel")
input.setAttribute("onclick", 'javascript: showElement("popup", false);')
content.addInteraction(input)
// Speichern
var input = content.createInput("button", "save", "Save")
input.setAttribute("onclick", 'javascript: savePopupData("' + dataType + '", "' + id + '", "false");')
content.addInteraction(input)
break
case "mapping":
content.createHeadline("Mapping")
// Aktiv
var dbKey:string = "x-active"
var input = content.createCheckbox(dbKey)
input.checked = data[dbKey]
input.id = "active"
//input.setAttribute("onchange", "javascript: this.className = 'changed'")
input.setAttribute("onchange", "javascript: toggleChannelStatus('" + id + "', this)")
content.appendRow("Active", input)
// Kanalname
var dbKey:string = "x-name"
var input = content.createInput("text", dbKey, data[dbKey])
input.setAttribute("onchange", "javascript: this.className = 'changed'")
if (BULK_EDIT == true) {
input.style.border = "solid 1px red"
input.setAttribute("readonly", "true")
}
content.appendRow("Channel Name", input)
content.description(data["name"])
// Beschreibung
var dbKey:string = "x-description"
var input = content.createInput("text", dbKey, data[dbKey])
input.setAttribute("placeholder", "Used by the Dummy as an XML description")
input.setAttribute("onchange", "javascript: this.className = 'changed'")
content.appendRow("Channel Description", input)
// Aktualisierung des Kanalnamens
if (data.hasOwnProperty("_uuid.key")) {
if (data["_uuid.key"] != "") {
var dbKey:string = "x-update-channel-name"
var input = content.createCheckbox(dbKey)
input.setAttribute("onchange", "javascript: this.className = 'changed'")
input.checked = data[dbKey]
content.appendRow("Update Channel Name", input)
}
}
// Logo URL (Kanal)
var dbKey:string = "tvg-logo"
var input = content.createInput("text", dbKey, data[dbKey])
input.setAttribute("onchange", "javascript: this.className = 'changed'")
input.setAttribute("id", "channel-icon")
content.appendRow("Logo URL", input)
// Aktualisierung des Kanallogos
var dbKey:string = "x-update-channel-icon"
var input = content.createCheckbox(dbKey)
input.checked = data[dbKey]
input.setAttribute("id", "update-icon")
input.setAttribute("onchange", "javascript: this.className = 'changed'; changeChannelLogo('" + id + "');")
content.appendRow("Update Channel Logo", input)
// Erweitern der EPG Kategorie
var dbKey:string = "x-category"
var text:string[] = ["-", "Kids (Emby only)", "News", "Movie", "Series", "Sports"]
var values:string[] = ["", "Kids", "News", "Movie", "Series", "Sports"]
var select = content.createSelect(text, values, data[dbKey], dbKey)
select.setAttribute("onchange", "javascript: this.className = 'changed'")
content.appendRow("EPG Category", select)
// M3U Gruppentitel
var dbKey:string = "x-group-title"
var input = content.createInput("text", dbKey, data[dbKey])
input.setAttribute("onchange", "javascript: this.className = 'changed'")
content.appendRow("Group Title (xteve.m3u)", input)
if (data["group-title"] != undefined) {
content.description(data["group-title"])
}
// XMLTV Datei
var dbKey:string = "x-xmltv-file"
var xmlFile = data[dbKey]
var xmltv:XMLTVFile = new XMLTVFile()
var select = xmltv.getFiles(data[dbKey])
select.setAttribute("name", dbKey)
select.setAttribute("id", "popup-xmltv")
select.setAttribute("onchange", "javascript: this.className = 'changed'; setXmltvChannel('" + id + "',this);")
content.appendRow("XMLTV File", select)
var file = data[dbKey]
// XMLTV Mapping
var dbKey:string = "x-mapping"
var xmltv:XMLTVFile = new XMLTVFile()
var select = xmltv.getPrograms(file, data[dbKey])
select.setAttribute("name", dbKey)
select.setAttribute("id", "popup-mapping")
select.setAttribute("onchange", "javascript: this.className = 'changed'; checkXmltvChannel('" + id + "',this,'" + xmlFile + "');")
sortSelect(select)
content.appendRow("XMLTV Channel", select)
// Interaktion
content.createInteraction()
// Logo hochladen
var input = content.createInput("button", "cancel", "Upload Logo")
input.setAttribute("onclick", 'javascript: uploadLogo();')
content.addInteraction(input)
// Abbrechen
var input = content.createInput("button", "cancel", "Cancel")
input.setAttribute("onclick", 'javascript: showElement("popup", false);')
content.addInteraction(input)
// Fertig
var ids:string[] = new Array()
ids = getAllSelectedChannels()
if (ids.length == 0) {
ids.push(id)
}
var input = content.createInput("button", "save", "Done")
input.setAttribute("onclick", 'javascript: donePopupData("' + dataType + '", "' + ids + '", "false");')
content.addInteraction(input)
break
default:
break;
}
showPopUpElement('popup-custom');
}
class XMLTVFile {
File:string
getFiles(set:string):any {
var fileIDs:string[] = getObjKeys(SERVER["xepg"]["xmltvMap"])
var values = new Array("-");
var text = new Array("-");
for (let i = 0; i < fileIDs.length; i++) {
if (fileIDs[i] != "xTeVe Dummy") {
values.push(getValueFromProviderFile(fileIDs[i], "xmltv", "file.xteve"))
text.push(getValueFromProviderFile(fileIDs[i], "xmltv", "name"))
} else {
values.push(fileIDs[i])
text.push(fileIDs[i])
}
}
var select = document.createElement("SELECT")
for (let i = 0; i < text.length; i++) {
var option = document.createElement("OPTION")
option.setAttribute("value", values[i])
option.innerText = text[i]
select.appendChild(option)
}
if(set != "") {
(select as HTMLSelectElement).value = set
}
return select
}
getPrograms(file:string, set:string):any {
//var fileIDs:string[] = getObjKeys(SERVER["xepg"]["xmltvMap"])
var values = getObjKeys(SERVER["xepg"]["xmltvMap"][file]);
var text = new Array()
var displayName:string
for (let i = 0; i < values.length; i++) {
if (SERVER["xepg"]["xmltvMap"][file][values[i]].hasOwnProperty('display-name') == true) {
displayName = SERVER["xepg"]["xmltvMap"][file][values[i]]["display-name"];
} else {
displayName = "-"
}
text[i] = displayName + " (" + values[i] + ")";
}
text.unshift("-");
values.unshift("-");
var select = document.createElement("SELECT")
for (let i = 0; i < text.length; i++) {
var option = document.createElement("OPTION")
option.setAttribute("value", values[i])
option.innerText = text[i]
select.appendChild(option)
}
if(set != "") {
(select as HTMLSelectElement).value = set
}
if ((select as HTMLSelectElement).value != set) {
(select as HTMLSelectElement).value = "-"
}
return select
}
return
}
function getValueFromProviderFile(file:string, fileType, key) {
if (file == "xTeVe Dummy") {
return file
}
var fileID:string
var indicator = file.charAt(0)
switch (indicator) {
case "M":
fileType = "m3u"
fileID = file
break;
case "H":
fileType = "hdhr"
fileID = file
break;
case "X":
fileType = "xmltv"
fileID = file.substring(0, file.lastIndexOf('.'))
break;
}
if (SERVER["settings"]["files"][fileType].hasOwnProperty(fileID) == true) {
var data = SERVER["settings"]["files"][fileType][fileID];
return data[key]
}
return
}
function setXmltvChannel(id, element) {
var xmltv:XMLTVFile = new XMLTVFile()
var xmlFile = element.value
var tvgId:string = SERVER["xepg"]["epgMapping"][id]["tvg-id"]
var td = document.getElementById("popup-mapping").parentElement
td.innerHTML = ""
var select = xmltv.getPrograms(element.value, tvgId)
select.setAttribute("name", "x-mapping")
select.setAttribute("id", "popup-mapping")
select.setAttribute("onchange", "javascript: this.className = 'changed'; checkXmltvChannel('" + id + "',this,'" + xmlFile + "');")
select.className = "changed"
sortSelect(select)
td.appendChild(select);
checkXmltvChannel(id, select, xmlFile)
}
function checkXmltvChannel(id:string, element:any, xmlFile) {
var value = (element as HTMLSelectElement).value
var bool:boolean
var checkbox = document.getElementById('active')
var channel:any = SERVER["xepg"]["epgMapping"][id]
var updateLogo:boolean
if (value == "-") {
bool = false
} else {
bool = true
}
(checkbox as HTMLInputElement).checked = bool
checkbox.className = "changed"
console.log(xmlFile);
// Kanallogo aktualisieren
/*
updateLogo = (document.getElementById("update-icon") as HTMLInputElement).checked
console.log(updateLogo);
*/
if(xmlFile != "xTeVe Dummy" && bool == true) {
//(document.getElementById("update-icon") as HTMLInputElement).checked = true;
//(document.getElementById("update-icon") as HTMLInputElement).className = "changed";
console.log("ID", id)
changeChannelLogo(id)
return
}
if (xmlFile == "xTeVe Dummy") {
(document.getElementById("update-icon") as HTMLInputElement).checked = false;
(document.getElementById("update-icon") as HTMLInputElement).className = "changed";
}
return
}
function changeChannelLogo(id:string) {
var updateLogo:boolean
var channel:any = SERVER["xepg"]["epgMapping"][id]
var f = (document.getElementById("popup-xmltv") as HTMLSelectElement);
var xmltvFile = f.options[f.selectedIndex].value;
var m = (document.getElementById("popup-mapping") as HTMLSelectElement);
var xMapping = m.options[m.selectedIndex].value;
var xmltvLogo = SERVER["xepg"]["xmltvMap"][xmltvFile][xMapping]["icon"]
updateLogo = (document.getElementById("update-icon") as HTMLInputElement).checked
if (updateLogo == true && xmltvFile != "xTeVe Dummy") {
if (SERVER["xepg"]["xmltvMap"][xmltvFile].hasOwnProperty(xMapping)) {
var logo = xmltvLogo
} else {
logo = channel["tvg-logo"]
}
var logoInput = (document.getElementById("channel-icon") as HTMLInputElement);
logoInput.value = logo
if (BULK_EDIT == false) {
logoInput.className = "changed"
}
}
}
function savePopupData(dataType:string, id:string, remove:Boolean, option:number) {
if (dataType == "mapping") {
var data = new Object()
console.log("Save mapping data")
cmd = "saveEpgMapping"
data["epgMapping"] = SERVER["xepg"]["epgMapping"]
console.log("SEND TO SERVER");
var server:Server = new Server(cmd)
server.request(data)
delete UNDO["epgMapping"]
return
}
console.log("Save popup data")
var div = document.getElementById("popup-custom")
var inputs = div.getElementsByTagName("TABLE")[0].getElementsByTagName("INPUT");
var selects = div.getElementsByTagName("TABLE")[0].getElementsByTagName("SELECT");
var input = new Object();
var confirmMsg: string
for (let i = 0; i < selects.length; i++) {
var name:string
name = (selects[i] as HTMLSelectElement).name
var value = (selects[i] as HTMLSelectElement).value
switch (name) {
case "tuner":
input[name] = parseInt(value)
break;
default:
input[name] = value
break;
}
}
for (let i = 0; i < inputs.length; i++) {
switch ((inputs[i] as HTMLInputElement).type) {
case "checkbox":
name = (inputs[i] as HTMLInputElement).name
input[name] = (inputs[i] as HTMLInputElement).checked
break
case "text":
case "hidden":
case "password":
name = (inputs[i] as HTMLInputElement).name
switch (name) {
case "tuner":
input[name] = parseInt((inputs[i] as HTMLInputElement).value)
break;
default:
input[name] = (inputs[i] as HTMLInputElement).value
break;
}
break
}
}
var data = new Object()
var cmd:string
if (remove == true) {
input["delete"] = true
}
switch (dataType) {
case "users":
confirmMsg = "Delete this user?"
if (id == "-") {
cmd = "saveNewUser"
data["userData"] = input
} else {
cmd = "saveUserData"
var d = new Object()
d[id] = input
data["userData"] = d
}
break;
case "m3u":
confirmMsg = "Delete this playlist?"
switch (option) {
// Popup: Save
case 0:
cmd = "saveFilesM3U"
break
// Popup: Update
case 1:
cmd = "updateFileM3U"
break
}
data["files"] = new Object
data["files"][dataType] = new Object
data["files"][dataType][id] = input
break
case "hdhr":
confirmMsg = "Delete this HDHomeRun tuner?"
switch (option) {
// Popup: Save
case 0:
cmd = "saveFilesHDHR"
break
// Popup: Update
case 1:
cmd = "updateFileHDHR"
break
}
data["files"] = new Object
data["files"][dataType] = new Object
data["files"][dataType][id] = input
break
case "xmltv":
confirmMsg = "Delete this XMLTV file?"
switch (option) {
// Popup: Save
case 0:
cmd = "saveFilesXMLTV"
break
// Popup: Update
case 1:
cmd = "updateFileXMLTV"
break
}
data["files"] = new Object
data["files"][dataType] = new Object
data["files"][dataType][id] = input
break
case "filter":
confirmMsg = "Delete this filter?"
cmd = "saveFilter"
data["filter"] = new Object
data["filter"][id] = input
break
default:
console.log(dataType, id);
return
break;
}
if (remove == true) {
if (!confirm(confirmMsg)) {
showElement("popup", false)
return
}
}
console.log("SEND TO SERVER");
console.log(data);
var server:Server = new Server(cmd)
server.request(data)
}
function donePopupData(dataType:string, idsStr:string) {
var ids:string[] = idsStr.split(',');
var div = document.getElementById("popup-custom")
var inputs = div.getElementsByClassName("changed")
ids.forEach(id => {
var input = new Object();
input = SERVER["xepg"]["epgMapping"][id]
console.log(input);
for (let i = 0; i < inputs.length; i++) {
var name:string
var value:any
switch (inputs[i].tagName) {
case "INPUT":
switch ((inputs[i] as HTMLInputElement).type) {
case "checkbox":
name = (inputs[i] as HTMLInputElement).name
value = (inputs[i] as HTMLInputElement).checked
input[name] = value
break
case "text":
name = (inputs[i] as HTMLInputElement).name
value = (inputs[i] as HTMLInputElement).value
input[name] = value
break
}
break
case "SELECT":
name = (inputs[i] as HTMLSelectElement).name
value = (inputs[i] as HTMLSelectElement).value
input[name] = value
break
}
switch (name) {
case "tvg-logo":
//(document.getElementById(id).childNodes[2].firstChild as HTMLElement).setAttribute("src", value)
break
case "x-name":
(document.getElementById(id).childNodes[3].firstChild as HTMLElement).textContent = value
break
case "x-category":
(document.getElementById(id).childNodes[3].firstChild as HTMLElement).className = value
break
case "x-group-title":
(document.getElementById(id).childNodes[5].firstChild as HTMLElement).textContent = value
break
case "x-xmltv-file":
if (value != "xTeVe Dummy" && value != "-") {
value = getValueFromProviderFile(value, "xmltv", "name")
}
if (value == "-") {
input["x-active"] = false
}
(document.getElementById(id).childNodes[6].firstChild as HTMLElement).textContent = value
break
case "x-mapping":
if (value == "-") {
input["x-active"] = false
}
(document.getElementById(id).childNodes[7].firstChild as HTMLElement).textContent = value
break
default:
}
createSearchObj()
searchInMapping()
}
if (input["x-active"] == false) {
document.getElementById(id).className = "notActiveEPG"
} else {
document.getElementById(id).className = "activeEPG"
}
console.log(input["tvg-logo"]);
(document.getElementById(id).childNodes[2].firstChild as HTMLElement).setAttribute("src", input["tvg-logo"])
});
showElement("popup", false);
return
}
function showPreview(element:boolean) {
var div = document.getElementById("myStreamsBox")
switch (element) {
case false:
div.className = "notVisible"
return
break;
}
var streams:string[] = ["activeStreams", "inactiveStreams"]
streams.forEach(preview => {
var table = document.getElementById(preview)
table.innerHTML = ""
var obj:string[] = SERVER["data"]["StreamPreviewUI"][preview]
obj.forEach(channel => {
var tr = document.createElement("TR")
var tdKey = document.createElement("TD")
var tdVal = document.createElement("TD")
tdKey.className = "tdKey"
tdVal.className = "tdVal"
switch (preview) {
case "activeStreams":
tdKey.innerText = "Channel: (+)"
break;
case "inactiveStreams":
tdKey.innerText = "Channel: (-)"
break;
}
tdVal.innerText = channel
tr.appendChild(tdKey)
tr.appendChild(tdVal)
table.appendChild(tr)
});
});
showElement("loading", false)
div.className = "visible"
return
}